2013-04-16 6 views
15

Gemäß der # spec F (siehe §6.5.7), sind einfach für Loops durch ganzzahlige begrenzt (int aka int32 aka System.Int32) begrenzt start und stop, z.B.Warum sind einfache For-Schleifenausdrücke auf ganzzahlige Bereiche beschränkt?

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

Ich frage mich, warum die Iteration Grenzen für diese Art von für Schleife int32 sein müssen. Warum nicht uint32 zulassen? int64? bigint?

Ich bin mir bewusst, dass Sequenziterationsausdrücke (for ... in ...) über beliebige Sequenzen iterieren können; das erfordert jedoch das Zuordnen eines Iterators und das Aufrufen von MoveNext und Current und was nicht und kann daher erheblich weniger effizient sein als eine einfache Schleife sein könnte (Inkrementierungs-, Vergleichs, Zustandssprung). Um zu vermeiden, dass, Sie sind mit der Verwendung von while und ein manuell Inkrementieren Schleifenzähler ...

Merkwürdiger stecken, F # nicht erlaubt nicht int32 Schleifen Grenzen, wenn die for Expression in einer Sequenz Ausdruck gewickelt wird, z.B.

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

Also, ich denke die Frage ist: Gibt es einen bestimmten Grund für die nur int32 für Schleifen ermöglichen? Und warum gilt diese Einschränkung nicht für for Schleifen, die in seq Ausdrücke eingebettet sind?

+0

Im Allgemeinen verwendet das .NET Framework 'int' als Allzweck- ganze Zahl, einschließlich ihrer Verwendung in allen Arten von numerischen Teilszenarien. Beispiel: http://msdn.microsoft.com/en-us/library/system.array.indexof(v=vs.71).aspx. Counter-Beispiel: http://msdn.microsoft.com/en-us/library/system.io.filestream.position.aspx, die eine lange verwendet. –

+4

F # fördert die funktionale Programmierung und stoppt daher die volle imperative Unterstützung (z. B. das Fehlen von "break"/"return"). Innerhalb eines Berechnungsausdrucks wird "for" zu einem Methodenaufruf entlüftet, der nicht inhärent zwingend wie eine Schleife ist und daher nicht die gleichen Grenzen hat. Ich kann jedoch das Geheimnis verstehen. +1 – Daniel

+0

@Daniel +1 "Innerhalb eines Berechnungsausdrucks wird für einen Methodenaufruf entzuckert". Aha; Tatsächlich ist der für 'seq {für .. to .. do ..}' und 'seq {für .. in .. do}' generierte Code weitgehend identisch, beide werden in einen 'GeneratedSequenceBase <_> '-Enumerator transformiert. – Frank

Antwort

7

Ich bin mir nicht sicher, warum F # int64 Bereiche nicht erlaubt. Es klingt wie ein nützliches Feature ... (aber ich kann verstehen, dass int der Standardtyp dafür in C# ist und vielleicht versucht F #, diesem Muster zu folgen).

Was die Abhilfen, ist es wert, fügte hinzu, dass Sie auch inline Funktion höherer Ordnung schreiben:

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

... und dann können Sie for Schleifen über int64 Bereiche in einer ziemlich prägnante Art und Weise auszudrücken:

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

ich habe ein paar Experimente und es scheint, dass die F # Compiler in der Lage ist, dies zu optimieren ziemlich anständig (der Lambda-Funktion ist inlined und der Schwanz-rekursive loop func wird in eine while Schleife umgewandelt). Ich glaube nicht, dass dies garantiert ist, so dass Sie dies möglicherweise per Hand in High-Performance-Code überprüfen müssen, aber es scheint für einfachere Beispiele gut zu funktionieren.

Es gibt nur einen Nachteil - Sie können lokale veränderbare Variablen (let mutable) nicht verwenden, da diese nicht von einer Lambda-Funktion erfasst werden können. So kann es zusätzliche Kosten mit indirekten ref Zellen geben (aber ich bin mir nicht sicher, wie groß das Problem ist).

+1

+1 Ich habe auch kürzlich experimentiert mit, wie die F # -Compiler inlineed Funktionen höherer Ordnung behandelt und ich war ziemlich überrascht (und erfreut) zu sehen, dass es sogar inLambdas auf der Call-Site, zumindest ab F # 3.0. Ich benutze jetzt einen Ansatz, der Ihrem Vorschlag sehr ähnlich ist, aber mit einer imperativen While-Schleife anstelle der Tail-Rekursion (so kann ich eine einfache Mutable anstelle einer Ref-Zelle verwenden). Scheint so gut zu funktionieren. – Frank

0

Eine weitere mögliche Abhilfe:

[1L..100L] |> Liste.iter (fun i -> printfn "% i" i)

2

Wenn Sie die for-Schleife halten wollen, gibt es eine sehr einfache Behelfslösung mit der for...in Schleife mit einem sequence range operator:

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

Die Der Bereichsoperator akzeptiert jede Ganzzahl beliebiger Größe, solange beide Typen übereinstimmen. Zum Beispiel wird die folgenden nicht Kompilierung:

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

Danke, aber ich bin mir dessen bewusst; lies den zweiten Absatz meiner Frage. – Frank

+0

Ups ... verpasste das. Kommt von [einer anderen Frage] (http://stackoverflow.com/q/21292082/211627) ... – JDB