Ich komme aus dem SML-Hintergrund und fühle mich sehr wohl mit den Funktionen höherer Ordnung. Aber ich bin nicht wirklich auf die Idee des Listenverständnisses gekommen. Gibt es eine Situation, in der Listenverständnis besser geeignet ist als höherwertige Funktionen in List
und umgekehrt?
Ich habe irgendwo gehört, dass Listenverstehen langsamer ist als Funktionen höherer Ordnung, sollte ich vermeiden, es beim Schreiben von performance-kritischen Funktionen zu verwenden?
Sehen Sie sich zum Beispiel das Projizieren einer Liste an von Listen effizient in F # , wobei @ cferns Antwort zwei Versionen enthält, die Listenverständnis bzw. höherwertige Funktionen verwenden:
%Vor%und:
%Vor% Die Wahl zwischen Funktionen und Funktionen höherer Ordnung ist meist eine Frage des Stils. Ich denke, dass Verständnisse manchmal besser lesbar sind, aber das ist nur eine persönliche Vorliebe. Beachten Sie, dass die Funktion cartesian
elegant so geschrieben werden könnte:
Der interessante Fall ist, wenn rekursive Funktionen geschrieben werden. Wenn Sie Sequenzen (und Sequenz-Comprehensions) verwenden, entfernen sie einige unnötige Zuweisung von temporären Listen und wenn Sie yield!
in einer Tail-Call-Position verwenden, können Sie auch Stack-Overflow-Ausnahmen vermeiden:
Das ist ein ziemlich interessantes Muster, weil es nicht mit Listen auf dieselbe Weise geschrieben werden kann. Wenn Sie Listen verwenden, können Sie kein Element zurückgeben und dann einen rekursiven Aufruf ausführen, weil es n::(nums ...)
entspricht, was nicht tail-rekursiv ist.
Wenn Sie sich den generierten Code in ILSpy ansehen, können Sie sehen, dass Listen-Comprehensions zu State-Maschinen kompiliert werden (wie Methoden, die yield return
in C # verwenden) und dann an etwas wie List.ofSeq
übergeben werden. Funktionen höherer Ordnung sind andererseits handkodiert und verwenden häufig einen veränderbaren Zustand oder andere imperative Konstrukte, um so effizient wie möglich zu sein. Wie es oft der Fall ist, ist der Allzweckmechanismus teurer.
Um Ihre Frage zu beantworten, sollten Sie, wenn Leistung wichtig ist, normalerweise eine höherwertige Funktion für Ihr Problem verwenden, die bevorzugt werden sollte.
Hinzufügen zu Tomas Petriceks Antwort. Sie können die Listenversion rekursiv machen.
%Vor%Mit dem zusätzlichen Vorteil einer beträchtlichen Beschleunigung. Zumindest wenn ich mit dem Stoppuhr-Tool gemessen habe. (nums2 ist der Algorithmus mit Seq).
%Vor%Bei höheren Zahlen schrumpft dieser Vorteil, da List.rev ineffizient ist. Z.B. für 10000000 bekomme ich:
%Vor%Tags und Links f# list list-comprehension