2010-08-18 7 views
5

Dopo un lungo e doloroso debug, credo di aver trovato una proprietà unica di Fortran che vorrei verificare qui su StackOverflow.Fortran conserva il valore delle variabili interne tramite chiamate di funzioni e subroutine?

Quello che ho notato è che, per lo meno, il valore delle variabili logiche interne è preservato attraverso chiamate di funzioni o subroutine.

Ecco alcuni esempi di codice per illustrare il mio punto:

PROGRAM function_variable_preserve 
IMPLICIT NONE 

CHARACTER(len=8) :: func_negative_or_not ! Declares function name 
INTEGER :: input 
CHARACTER(len=8) :: output 

input = -9 

output = func_negative_or_not(input) 
WRITE(*,10) input, " is ", output 
10 FORMAT("FUNCTION: ", I2, 2A) 

CALL sub_negative_or_not(input, output) 
WRITE(*,20) input, " is ", output 
20 FORMAT("SUBROUTINE: ", I2, 2A) 

WRITE(*,*) 'Expected negative.' 


input = 7 
output = func_negative_or_not(output) 
WRITE(*,10) input, " is ", output 

CALL sub_negative_or_not(input, output) 
WRITE(*,20) input, " is ", output 

WRITE(*,*) 'Expected positive.' 

END PROGRAM function_variable_preserve 

CHARACTER(len=*) FUNCTION func_negative_or_not(input) 
IMPLICIT NONE 

INTEGER, INTENT(IN) :: input 
LOGICAL :: negative = .FALSE. 

IF (input < 0) THEN 
    negative = .TRUE. 
END IF 

IF (negative) THEN 
    func_negative_or_not = 'negative' 
ELSE 
    func_negative_or_not = 'positive' 
END IF 

END FUNCTION func_negative_or_not 

SUBROUTINE sub_negative_or_not(input, output) 
IMPLICIT NONE 

INTEGER, INTENT(IN) :: input 
CHARACTER(len=*), INTENT(OUT) :: output 
LOGICAL :: negative = .FALSE. 

IF (input < 0) THEN 
    negative = .TRUE. 
END IF 

IF (negative) THEN 
    output = 'negative' 
ELSE 
    output = 'positive' 
END IF 

END SUBROUTINE sub_negative_or_not 

Questa è l'uscita:

FUNCTION: -9 is negative 
SUBROUTINE: -9 is negative 
Expected negative. 
FUNCTION: 7 is negative 
SUBROUTINE: 7 is negative 
Expected positive. 

Come si può vedere, sembra che una volta che la funzione o subroutine viene chiamato una volta, la variabile logica negative, se impostata su .TRUE., rimane tale nonostante l'inizializzazione di negative in .FALSE. nella dichiarazione di dichiarazione del tipo.

Potrei, ovviamente, correggere questo problema aggiungendo semplicemente una riga negativa = .FALSE. dopo aver dichiarato la variabile nella mia funzione e subroutine.

Tuttavia, mi sembra molto strano che ciò sia necessario.

Per motivi di portabilità e riusabilità del codice, il linguaggio (o il compilatore forse) non deve richiedere la reinizializzazione di tutte le variabili interne ogni volta che viene richiamata la subroutine o la funzione?

risposta

9

Per rispondere alla tua domanda: Sì Fortran conserva il valore delle variabili interne tramite chiamate di subroutine e funzioni.

In determinate condizioni ...

Se si dichiara una variabile interna con l'attributo SAVE, il suo valore viene salvato da una chiamata alla successiva. Questo è, ovviamente, utile in alcuni casi.

Tuttavia, la tua domanda è una reazione comune al primo apprendimento di uno dei trucchi di Fortran: se si inizializza una variabile interna nella sua dichiarazione, esso acquisisce automaticamente l'attributo SAVE. Hai fatto esattamente questo nelle tue subroutine. Questo è conforme alla norma. Se non si desidera che ciò accada, non inizializzarlo nella dichiarazione.

Questa è la causa di molte sorprese e lamentele da parte di (alcuni) nuovi arrivati ​​nella lingua. Ma non importa quanto duramente si lamentino, non cambierà quindi basta (a) conoscerlo e (b) programmare in consapevolezza di ciò.

+0

Credo (per essere sicuro che dovrei andare a controllare lo standard, che non ho con me in questo momento) che a partire dalla F2003, tutte le variabili hanno l'attributo SAVE. – Rook

+0

Sorprendente davvero! – EMiller

3

Questo non è molto diverso dalle variabili con ambito funzionale static in C o C++.

La progettazione del linguaggio di programmazione era agli inizi, quando FORTRAN era sviluppato. Se fosse stato progettato da zero oggi, senza dubbio molte delle decisioni di design sarebbero state diverse.

Originariamente, FORTRAN non ha nemmeno sostenere la ricorsione, non c'era l'allocazione dinamica della memoria , programmi giocato tutti i tipi di giochi tipo-giochi di parole con COMMON blocchi e EQUIVALENCE dichiarazioni, procedure potrebbero avere molteplici punti di ingresso .... così il modello di memoria era fondamentalmente per il compilatore/linker per disporre tutto, anche le variabili locali e le costanti numeriche letterali, in posizioni di memoria fisse, piuttosto che su lo stack. Se lo volessi, potresti persino scrivere un codice che ha cambiato il valore di "2" in "42"!

A questo punto, c'è un sacco di codice legacy FORTRAN e gli autori di compilatori fanno di tutto per preservare la semantica compatibile con le versioni precedenti. Non posso citare il capitolo e il versetto dello standard che impone il comportamento che hai notato, né la sua logica, ma sembra ragionevole che la compatibilità con le versioni precedenti abbia prevalso sulle sensibilità del design del linguaggio moderno, in questo caso.

+0

Ci sono indubbiamente alcuni problemi legacy in fortran che, se fossero stati scritti oggi da stratch, sarebbero gestiti in modo diverso, ma per favore - non farlo sembrare come se fosse un linguaggio legacy. Al contrario, in molti termini ha caratteristiche che altre lingue "moderne" stanno acquisendo (ad esempio, in c.l.f. le discussioni sono spesso correlate a f03/il più recente standard C, che cos'è C99?) – Rook

+0

@Idigas: buoni punti! Non volevo insinuare che FORTRAN fosse un fossile ... è solo che l'aspetto legato all'eredità sembrava essere più pertinente alla domanda di CmdrGuard, quindi è quello su cui mi sono concentrato. –

+0

Ovviamente, ho frainteso; mi dispiace per quello. Errore mio. Il resto della tua risposta è abbastanza ben fatto. – Rook

3

Questo è stato discusso più volte qui, la maggior parte di recente a Fortran assignment on declaration and SAVE attribute gotcha

Non è necessario scoprire questo comportamento sperimentazione, è chiaramente indicato nei libri di testo migliori.

Diverse lingue sono diverse e hanno comportamenti diversi.

C'è un motivo storico per questo comportamento. Molti compilatori per Fortran 77 e precedenti hanno conservato i valori di TUTTE le variabili locali attraverso le chiamate di procedure. I programmatori non dovevano basarsi su questo comportamento, ma molti lo facevano. Secondo lo standard, se si desiderava una variabile locale (non COMMON) per conservare il proprio valore, era necessario utilizzare "SAVE". Ma molti programmatori lo ignorarono. In quell'era i programmi venivano portati meno frequentemente su piattaforme e compilatori diversi, quindi non si potevano mai notare ipotesi errate. È frequente riscontrare questo problema nei programmi precedenti: i compilatori Fortran correnti forniscono in genere un commutatore per far sì che tutte le variabili vengano salvate. Sarebbe sciocco per lo standard del linguaggio richiedere che tutte le variabili locali mantengano i loro valori. Ma un requisito intermedio che salvasse molti programmi trascurati con "SAVE" sarebbe richiedere che tutte le variabili inizializzate nelle loro dichiarazioni abbiano automaticamente l'attributo SAVE. Da qui quello che hai scoperto ...