2013-04-16 17 views
15

Secondo il F # spec (vedi §6.5.7), semplice per cicli sono delimitate da intero (int alias int32 alias System.Int32) limiti start e stop, ad esempioPerché le espressioni di loop semplici sono limitate agli intervalli interi?

for i = start to stop do 
    // do sth. 

mi chiedo perché i limiti di iterazione per questo tipo di ciclo for sono tenuti ad essere int32. Perché non consentire uint32? int64? bigint?

Sono consapevole che le espressioni di iterazione sequenziale (for ... in ...) possono scorrere su sequenze arbitrarie; che tuttavia richiede l'allocazione di un iteratore e chiama MoveNext e Current e cosa no e può quindi essere considerevolmente meno efficiente di un ciclo normale potrebbe essere (incremento contatore, confronto, salto condizionale). Per evitare che, si sono bloccati con l'utilizzo di while e un incrementali manualmente contatori di ciclo ...

Stranamente, F # non permette non int32 limiti di loop, se l'espressione for è avvolto in una sequenza di espressione, per esempio

seq { for i = 0I to 10I do 
     printfn "%A" i } 

Quindi, credo che la domanda è: C'è un motivo particolare per consentendo solo int32 per i cicli? E perché questa restrizione non si applica ai cicli for avvolti nelle espressioni seq?

+0

In generale, il quadro NET utilizza 'int' come numero intero di impiego generale, compreso l'utilizzo in tutti i tipi di scenari di indicizzazione numerici. Esempio: http://msdn.microsoft.com/en-us/library/system.array.indexof(v=vs.71).aspx. Counter-esempio: http://msdn.microsoft.com/en-us/library/system.io.filestream.position.aspx, che utilizza un lungo. –

+4

F # incoraggia la programmazione funzionale e quindi si ferma a corto di pieno supporto imperativo (ad esempio, la mancanza di 'break' /' return'). All'interno di un'espressione di calcolo 'for' è desugared ad una chiamata al metodo, che non è intrinsecamente imperativa come un ciclo, e quindi non ha gli stessi limiti. Posso capire il mistero però. +1 – Daniel

+0

@Daniel +1 "All'interno di un'espressione di calcolo per è desugared a una chiamata di metodo". Io vedo; in realtà il codice generato per 'seq {for .. to .. do ..}' e 'seq {for .. in .. do}' è in gran parte identico, entrambi vengono trasformati in un enumeratore 'GeneratedSequenceBase <_>'. – Frank

risposta

7

Non sono sicuro del motivo per cui F # non consente intervalli int64. Sembra una funzione utile ... (ma posso capire che int è il tipo standard per questo in C# e forse F # cerca di seguire questo modello).

Per quanto riguarda le soluzioni alternative, vale la pena di aggiungere che si può anche scrivere inline funzione di ordine superiore:

let inline longFor low high f = 
    let rec loop n = 
    if n < high then f n; loop (n + 1L) 
    loop low 

... e poi si può esprimere for loop oltre int64 gamme in modo piuttosto succinta:

longFor 1L 100L (fun n -> 
    <whatever>) 

ho fatto un paio di esperimenti e sembra che il compilatore F # è in grado di ottimizzare questa abbastanza decentemente (la funzione lambda è inline e la coda ricorsiva loop func viene trasformato in un ciclo while). Non penso che questo sia garantito quindi potrebbe essere necessario controllare questo a mano nel codice ad alte prestazioni, ma sembra funzionare bene per gli esempi più semplici.

C'è solo uno svantaggio: non sarà possibile utilizzare variabili mutabili locali (let mutable) perché queste non possono essere acquisite da una funzione lambda. Quindi potrebbero esserci costi aggiuntivi con le celle indirette ref (ma non sono sicuro di quanto sia grande questo problema).

+1

+1 Ho anche sperimentato di recente come i compilatori F # gestiscano le funzioni di ordine superiore in linea e sono rimasto piuttosto sorpreso (e lieto) di vedere che era in linea anche lambda sul sito di chiamata, almeno a partire dall'F # 3.0. Ora utilizzo un approccio molto simile al tuo suggerimento, ma con un ciclo temporale imperativo invece di ricorsione in coda (quindi posso usare una semplice mutevole invece di una cella ref). Sembra funzionare bene finora. – Frank

0

Un'altra possibile soluzione:

[1L..100L] |> List.iter (divertente che -> printfn "% i" i)

2

Se si desidera mantenere il ciclo for, c'è un molto semplice aggirare utilizzando il ciclo for...in con un sequence range operator:

for i in 0I .. 10I do 
    printfn "%A" i 

Il l'operatore di intervallo accetterà qualsiasi numero intero di qualsiasi dimensione purché entrambi i tipi corrispondano. Ad esempio, il seguente non compilazione:

for i in 0 .. 10I do 
    printfn "%A" i 
+0

Grazie, ma ne sono consapevole; leggi il secondo paragrafo della mia domanda. – Frank

+0

Oops ... mancato questo. Veniva da [un'altra domanda] (http://stackoverflow.com/q/21292082/211627) ... – JDB