Traduzioni da un linguaggio a un altro

btf3

Un discorso che devo prendere un po’ alla lunga, da lontano, roba di prima di voi giovani, ma poi arrivo al dunque, cioè all’oggi, promesso.

In Fortran π, pi, il rapporto tra le misure della circonferenza e il raggio non è definito. Ma tutti sanno che l’arcotangente di 1 rad vale π/4.
E allora diventa tutto semplice, gli antichi (non gli antichissimi ma non cambia molto) usavano il Fortran 77 (1978).
Vediamo se ricordo bene perché possono esserci diversi modi di scrivere, tenendo presente che i tipi sono impliciti e allora

      pi1 = 4.0 * atan(1.0)
      pi2 = 4.0 * atan(1)
      pi3 = 4 * atan(1.0)
      pi4 = 4.0 * datan(1.0)
      
      print *, pi1, pi2, pi3, pi4
      end

ap0

Partito molto male: 2 errori –no uno solo in 2 varietà– correggo.

La riga pi2 = 4.0 * atan(1) è irrecuperabile, a correggerla diventa uguale alla precedente.
La riga pi4 = 4.0 * datan(1.0) è più intrigante. Intanto la D iniziale sta per double precision, quello che in C si chiama double.

      pi1 = 4.0 * atan(1.0)
      pi3 = 4 * atan(1.0)
      pi4 = 4.0 * datan(1.d0)
      pi5 = 4.0d0 * datan(1.d0)
      pi6 = 4.0d0 * datan(dble(1.0))
      
      print "(f12.10)" , pi1, pi3, pi4, pi5, pi6
      end

ap1

OK, ottengo gli stessi valori. Notare la promozione a double nella moltiplicazione per il calcolo di pi4. Notare anche la funzione dble, cast a double direbbe il C-ista.

Ma non va come previsto: otteniamo sempre lo stesso numero di cifre corrette, sia usando i real*4 che i real*8 corrispondenti a float e double del C.
Devo dichiarare il tipo quando non rientra in quelli di default. Infatti questo viene automaticamente assegnato a integer (2 bytes nel Fortran IV, 4 nel 77) se la prima lettera della variabile è nell’intervallo [I-N], real*4 per tutte le altre. Si ricorda inoltre che al di fuori delle stringhe quotate si ha una conversione automatica al maiuscolo.
Ecco:

      double precision pi4
      real * 8 pi6

      pi1 = 4.0 * atan(1.0)
      pi3 = 4 * atan(1.0)
      pi4 = 4.0 * datan(1.d0)
      pi5 = 4.0d0 * datan(1.d0)
      pi6 = 4.0d0 * datan(dble(1.0))
      
      print "(f12.10)", pi1, pi3, pi4, pi5, pi6
      end

ap2

OK 😀 Ah! dimenticavo: gli spazi non sono significativi, real*8 e real * 8 sono sinonimi, lo sarebbe pure r e a l * 8.

Lo so che nessuno usa più il Fortran, neanch’io, ma devo raccontare questo altrimenti poi non si capisce.

Neanche e, quella di Euler, dei logaritmi naturali, è predefinita. Ma facciamo come per π:

      real * 8 e2

      e1 = exp(1.0)
      e2 = dexp(1.d0)      

      print "(f12.10)", e1, e2
      end

ae

OK 😀 notare che al posto di 1.0 posso scrivere 1. come posso scrivere .1 per 0.1; i vecchi lo facevano sempre.

Finora niente di sconvolgente ma ecco un passo ancora

      e = exp(1.0)
      pi = 4.0 * atan(1.0)
      ie2 = 2 * e
      ipi2 = 2 * pi
        
      print *, e, pi
      print *, ie2, ipi2
      end

app

OK, i cast a integer*4 tronca i real*4. Ma attenzione, questo avviene –ovviamente– dopo la moltiplicazione. Ovviamente è diverso da questo caso:

      e = exp(1.0)
      pi = 4.0 * atan(1.0)
      ie = e
      ipi = pi
      ie2 = 2 * ie
      ipi2 = 2 * ipi
        
      print *, e, pi
      print *, ie2, ipi2
      end

iapp

Se si vuole l’arrotondamento all’intero più vicino ci sono due modi:

      e = exp(1.0)
      ie1 = e + 0.5
      ie2 = int(e + 0.5)
      ie3 = nint(e)

      print *, ie1, ie2, ie3
      end

round

Siccome la conversione da real a integer avviene automaticamente per troncamento i vecchi usavano sommare 1/2 al valore da convertire. La funzione int() in questo caso è implicita per ie1 e esplicita per ie2. Esiste la funzione di arrotondamento nint() ma è usata raramente (mai).

Finora ho barato; no, non barato ma omesso di dire tutta la verità. Alcune funzioni intrinseche del Fortran sono da sempre (OK, quasi) polimorfe.
Già vista datan() ma risulta più chiaro con la funzione max():

      m1 = max(3, 1, 4, 1, 5)
      m2 = max0(3, 1, 4, 1, 5)
      m3 = max1(3.0, 1.0, 4.0, 1.0, 5.0)
      rm2 = amax0(3, 1, 4, 1, 5)
      rm3 = amax1(3.0, 1.0, 4.0, 1.0, 5.0)
      print *, m1, m2, m3
      print *, rm2, rm3
      end

fsp

Sì, conta la lettera iniziale (A per i real, D per i double) e l’eventuale numero finale (0 per integer, 1 per real). Come se non fosse abbastanza ci sono state variazioni (tante) tra le versioni 77 e 90 (e successive), RTFM (cit.).

Ci sarebbe da raccontare molto su common ma salto.
Come pure salto tutto il capitolo sulle matrici, c’è Octave visto recentemente.

Invece una cosa sempre in agguato: il passaggio di variabili ai sottoprogrammi. In Fortran ci sono le function, funzioni come quelle di C e Python e quasi tutti i linguaggi. Ma ci sono anche le subroutines –anzi sono nate prima– sottoprogrammi chiamate con la keyword call che non ritornano un valore come le funzioni ma modificano i dati passati. Tutti, perché vengono passati per indirizzo (questo è vero solo per le versioni non nuovissime ma il codice è spesso piuttosto attempato). Un esempio minimo:

*     main
      n = 5
      call foo(n, nq)
      print *, n, nq
      call foo(n, nq)
      print *, n, nq
      call foo(n, nq)
      print *, n, nq
      end
      
      subroutine foo(k, l)
      k = k + 1
      l = k ** 2
      end

pass

Sembra facile, traduco in Python:

### versione iniziale, non funziona

def foo(k, l):
      k = k + 1
      l = k ** 2

#  main
n = 5
foo(n, nq)
print(n, nq)
foo(n, nq)
print(n, nq)
foo(n, nq)
print(n, nq)

py-pass

Ehmmm… no; non basta aggiustare la sintassi delle singole righe. Il valore di l | nq dev’essere ritornato espressamente. Inoltre è inutile passarlo tra gli argomenti:

### ancora non funziona

def foo(k):
      k = k + 1
      l = k ** 2
      return l

#  main
n = 5
nq = foo(n)
print(n, nq)
nq = foo(n)
print(n, nq)
nq = foo(n)
print(n, nq)

py-pass1

No! non ci sono ancora: ovvio, gli passo sempre lo stesso parametro n = 5; la modifica all’interno di foo() è locale e in main non viene vista, correggo:

def foo(k):
      k = k + 1
      l = k ** 2
      return l

#  main
n = 5
nq = foo(n)
n += 1
print(n, nq)
n += 1
nq = foo(n)
print(n, nq)
n += 1
nq = foo(n)
print(n, nq)

py-pass2

OK! ma il codice è orrendo! Non voglio vedere niente di simile 😦

Come visto anche per un caso molto banale la traduzione automatica, riga per riga non è possibile. Ci sarebbe da dare la colpa al fortrainer che se avesse fatto le cose per bene… scrivendo la subroutine foo() così:

      subroutine foo(k, l)
      j = k + 1
      l = j ** 2
      end

la variabile k sarebbe stata solo di input –o anche intent(in) per le versioni nuove del Fortran– non sarebbe ritornata modificata e il main avrebbe dovuto essere scritto meglio.

Adesso provate –Gedankenexperiment– ad applicare questo a codice reale 👿 Ma secondo me va fatto. Prima o poi. Non necessariamente da me.
:mrgreen:

Posta un commento o usa questo indirizzo per il trackback.

Trackback

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: