semplice lasciare vincolante contro la funzione costante

3

Capisco i motivi per cui preferirei let a = f(x) a let a() = f(x) , specialmente quando f takes è una funzione a lunga esecuzione.

Penso anche che sia corretto dire che, considerando le origini del calcolo lambda della programmazione funzionale let a() è veramente funzionale, mentre let a non lo è. Per favore correggimi se sbaglio su questo.

Tuttavia non ho idea di quale espressione sarà effettivamente migliore:

let a = 4

o

let a() = 4

C'è qualche ragione per cui preferirei uno rispetto all'altro?

    
posta Grzegorz Sławecki 29.08.2014 - 17:39
fonte

3 risposte

3

Non c'è nulla di "non funzionale" su let a = 4 rispetto a let a () = 4 . In effetti, in molte formulazioni astratte delle teorie del tipo, quest'ultimo è veramente zucchero sintattico per let a = fun () -> 4 .

La differenza principale è quando vuoi che l'espressione venga valutata, come hai detto tu. Se alla fine a verrà valutato comunque, non ha senso avvolgerlo in una funzione. OTOH se pensi che ci sia una buona possibilità che a non possa essere valutato, e la valutazione è molto costosa (o può anche fallire se non è soddisfatta una precondizione), allora potrebbe essere utile mantenere come una funzione. Tuttavia, in quest'ultimo caso devi stare attento a non valutare la funzione più di una volta.

Tuttavia, non sono sicuro che l'uso pervasivo della pigrizia sia tutto ciò che è comune in un linguaggio rigoroso come F #.

Modifica per indirizzare la tua domanda nei commenti:

In molte specifiche matematiche formali di un linguaggio, il costrutto let associa un'espressione a una singola variabile:

let VARIABLE = EXPRESSION in EXPR

Il motivo è perché questo è più semplice di un let che associa funzioni arbitrarie come let f x = 2 * x . Invece, quest'ultimo è trattato come zucchero sintattico per il più dettagliato let f = fun x -> 2 * x .

Tuttavia, nessuno dei due è più "funzionale" (se questa parola ha anche un significato ben definito). La programmazione funzionale non significa che dovresti tentare di utilizzare le funzioni ovunque possibile. Piuttosto, si limita a mettere le funzioni sullo stesso piano con altri valori in modo da poter manipolare sia le funzioni che i valori con la stessa facilità.

Il costrutto let in Haskell ha un aspetto superficiale come quello di F #, ma semanticamente è molto diverso perché è non-strict ("pigro") per impostazione predefinita. Ciò significa che let x = 4 in 2 * x in Haskell viene tradotto in modo più appropriato in let x = lazy(4) in 2 * Lazy.force x .

Il costrutto let dovrebbe non essere confuso con assignments , che sono una cosa completamente diversa in quanto richiede una mutazione. Per contrasto, let non muta nulla: introduce semplicemente una nuova variabile. F # sfuma il limite perché consente di mescolare le funzioni pure e impure, quindi le associazioni let possono avere effetti collaterali e quindi il modo in cui li si lega (come funzioni o valori) avrà importanza.

In un linguaggio puro, let binding non può causare effetti collaterali, ma l'ordine di valutazione è ancora valido per due motivi:

  • Efficienza: perché non abbiamo risorse infinite e pazienza infinita.
  • Divergenza: le funzioni possono ancora fallire o entrare in un ciclo infinito.

Quindi, anche nelle lingue pure , è ancora importante sapere se la valutazione è fatta in modo avido o pigramente.

    
risposta data 29.08.2014 - 18:01
fonte
4

Oltre alle considerazioni relative al calcolo lambda, ci sono anche considerazioni pratiche. Se crei un valore ( let a = 4 ) o un valore di funzione ( let a() = 4 ) ha un impatto sul momento dell'esecuzione del codice, che a volte è importante. Il seguente codice F # mostra l'effetto in un modo semplice:

let A = printfn "A invoked"
let B() = printfn "B invoked"

let Run() =
    printfn "Run() invoked"
    A
    B()

Run()

L'output è:

A invoked
Run() invoked
B invoked

Potrebbe essere una sorpresa una volta ogni tanto. Per semplici valori numerici costanti, meno così, che per alcune funzioni che producono effetti collaterali (printfn, ...).

    
risposta data 29.08.2014 - 18:58
fonte
2

Non preoccuparti di quale sia più "funzionale". Sei un programmatore, non un matematico. Usa let a() quando vuoi valutarlo nel punto di utilizzo, quando non ti interessa valutarlo più volte. Usa let a quando vuoi valutarlo nel punto di definizione e vuoi solo che venga valutato una volta.

La terza scelta è let a = lazy(f(x)) , che attende finché non è necessario per valutare, ma la valuta solo una volta. Questa è la mia scelta preferita per funzioni costose che potrebbero non essere necessarie. Questo è un po 'brutto in F # però, perché richiede un esplicito a.forza, a differenza della maggior parte dei linguaggi funzionali che forza la valutazione automaticamente al momento opportuno.

Ricorda che let a assomiglia a un compito mutante, ma non lo è. Programmi funzionali puri possono essere sostituiti ed estesi a una grande espressione annidata. Concettualmente, se espandi il tuo programma, ti ritroverai comunque con la stessa espressione con let a , eliminando solo alcune duplicazioni quando lo valuti.

    
risposta data 29.08.2014 - 21:39
fonte

Leggi altre domande sui tag