2011-01-09 9 views
3

Gibt es einen One-Liner-Weg zu implementieren nextLine?Seq Kopf und Schwanz

let s = ref seqOfLines 
let nextLine() = 
    let hd = Seq.head !s 
    s := Seq.skip 1 !s 
    hd 

seqOfLines angenommen wird

+3

Es ist eine wirklich schlechte Idee, die Erweiterung von Sequenzen wie 's: = Seq.skip 1! S' zu verwenden, da sie ein neues Sequenzobjekt über dem anderen erzeugt, so dass Ihr Algorithmus in O (n^2) endet). Korrigiere mich, wenn ich falsch liege. –

+3

Warum willst du überhaupt die 'nextLine' Funktion? Warum nicht einfach über die Sequenz iterieren, was zu einer reinen Funktion führt. Du würdest so etwas nicht in C# machen. Wenn Sie darauf bestehen, was Sie tun, sollten Sie 's.GetEnumerator()' verwenden und das in Ihrer 'nextLine' Funktion verwenden. –

+2

Sie können den Quellcode hier sehen: https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/seq.fs#L1438 Es erstellt eine neue Sequenz, tut eine Reihe von Speicherzuweisungen, usw. Da Sie wissen, dass Sie nur einen Schritt jedes Mal verschieben möchten, ist es am besten, MoveNext selbst aufzurufen. –

Antwort

4

Eine Möglichkeit, unendlich zu sein, dies zu tun ist, um die zugrunde liegenden IEnumerator<String> zu nutzen. Es ist kein Ein-Liner, aber es scheint ein bisschen sauberer als die Implementierung, die Sie haben. (Verlässt sich nicht auf veränderbar, verwendet ordnungsgemäß die .NET-Idiome.)

Im Wesentlichen erhalten Sie die IEnumerator<'a>-Schnittstelle aus der Sequenz, und dann nur beim Aufruf von MoveNext Schleife. Dies funktioniert in einer unendlichen Sequenz.

> let getNextFunc (seqOfLines : seq<'a>) =    
-  let linesIE : IEnumerator<'a> = seqOfLines.GetEnumerator() 
-  (fun() -> ignore (linesIE.MoveNext()); linesIE.Current);; 

val getNextFunc : seq<'a> -> (unit -> 'a) 

zu verwenden, nur getNextFunc eine Folge passieren, und es wird Ihre Funktion nextline zurück.

> let sequenceOfStrings = seq { for i = 0 to 10000 do yield i.ToString() };; 

val sequenceOfStrings : seq<string> 

> let nextLine = getNextFunc sequenceOfStrings;; 

val nextLine : (unit -> string) 

> nextLine();; 
val it : string = "0" 
> nextLine();; 
val it : string = "1" 
> nextLine();; 
val it : string = "2" 
> nextLine();; 
val it : string = "3" 
2

Hmmm, ich glaube, Sie versuchen, dies zu imperativ zu nähern, und als ein Ergebnis, Sie gehen einige flippige Code, um am Ende zu schreiben und die Vorteile der Funktionsprogrammierung zu verlieren.

Sie könnten profitieren, indem Sie Ihre Funktion neu schreiben, so dass es current state und value * next state zurückgibt. Dadurch bleibt Ihre Funktion rein funktional. Man könnte es auch einfacher, finden Sie Ihre unendlichen seq zu einem LazyList statt zu konvertieren (Sie müssen die F # Powerpack für diese verweisen), so dass Sie den zugrunde liegenden enumerator nicht direkt berühren müssen:

> open LazyList 
let seqOfLines = Seq.initInfinite (fun i -> i) |> LazyList.ofSeq 
let nextLine = function Cons(x, xs) -> x, xs | Nil -> failwith "Empty list";; 

val seqOfLines : LazyList<int> 
val nextLine : LazyList<'a> -> 'a * LazyList<'a> 

> nextLine seqOfLines;; 
val it : int * LazyList<int> = (0, seq [1; 2; 3; 4; ...]) 
> nextLine (snd it);; 
val it : int * LazyList<int> = (1, seq [2; 3; 4; 5; ...]) 
> nextLine (snd it);; 
val it : int * LazyList<int> = (2, seq [3; 4; 5; 6; ...]) 
> nextLine (snd it);; 
val it : int * LazyList<int> = (3, seq [4; 5; 6; 7; ...]) 
0

FSharpx.Collections einige hat nützliche/effiziente Funktionen wie Seq.tail, Seq.Head und Seq.UnCons, die nützlich sein könnten, wenn Sie einen Seq in Kopf und Schwanz zerlegen möchten.

Verwandte Themen