Ich würde gerne wissen, was der Standard in Haskell ist.
Der erste besagt eindeutig, dass wir (meistens) zwei Argumente haben wollen.
Die zweite beinhaltet einen Funktionsaufruf ( id
) in der zweiten Klausel, daher sollte sie weniger effizient sein, da wir in der ersten Implementierung einfach das zweite Argument zurückgeben können.
Also tendiere ich dazu, zu denken, dass das erste besser ist und dasjenige sein sollte, das man auswählen sollte: leichter zu lesen und herauszufinden, was es macht [1], und einen Funktionsaufruf speichern.
Aber ich bin Neuling von Haskell, vielleicht optimiert der Compiler diesen zusätzlichen Anruf.
%Vor% Außerdem würde ich gerne wissen, ob ich beide False
durch einen Platzhalter ersetzen kann.
Also, was ist die gute Praxis in Haskell? Vielleicht eine andere Implementierung?
[1] Wir unterlassen es dort, dass es eine bekannte Funktionalität ist, stellen wir uns vor, es ist eine nicht-triviale Funktion.
Danke
Natürlich hängt es vom Compiler und den Optionen ab, die an den Compiler übergeben werden.
Wenn Sie in diesem Beispiel ohne Optimierungen kompilieren, erzeugt GHC den Code so, wie Sie ihn geschrieben haben, also enthält die zweite Version einen Aufruf von id
resp. zu not
. Das ist etwas weniger effizient als die erste Version, die dann nur den Aufruf von not
enthält:
(Die Aufrufe sind immer noch in der erzeugten Assembly, aber Kern ist lesbarer, also poste ich nur das).
Aber bei Optimierungen kompilieren beide Funktionen zum selben Kern (und von dort zum selben Maschinencode),
%Vor% GHC eta-erweitert die zweite Version und inline die Aufrufe von id
und not
, erhalten Sie eine reine Mustererkennung.
Ob die zweite Gleichung False
oder einen Platzhalter verwendet, spielt in beiden Versionen keine Rolle, ob mit oder ohne Optimierungen.
vielleicht optimiert der Compiler diesen zusätzlichen Aufruf.
Wenn Sie es zur Optimierung auffordern, wird GHC in einfachen Fällen diesen zusätzlichen Anruf unterdrücken.
stellen wir uns vor, es ist eine nicht-triviale Funktion.
Hier ist ein mögliches Problem. Wenn der Code nicht trivial genug ist, kann der Compiler möglicherweise nicht alle Aufrufe löschen, die durch Definieren der Funktion mit nicht allen Argumenten eingeführt werden. GHC ist jedoch ziemlich gut darin, Anrufe zu tätigen und zu inte- grieren, sodass GHC nicht auf einfache Funktionen verzichten muss, die er beim Kompilieren von Code kennt (es können natürlich niemals Inline-Aufrufe von Funktionen ausgeführt werden) kenne die Implementierung beim Kompilieren des fraglichen Moduls nicht.
Wenn es sich um einen kritischen Code handelt, prüfen Sie immer, welchen Code der Compiler erzeugt, für GHC sind die relevanten Flags -ddump-simpl
, um den Kern nach Optimierungen zu erhalten, und -ddump-asm
, um die erzeugte Assembly zu erhalten.
Aus Gründen der Lesbarkeit würde ich versuchen, Mustervergleiche zu vermeiden und die Funktion mit einer einzigen Gleichung zu definieren, die etwas Interessantes über die zu definierende Funktion ausdrückt. Das ist nicht immer möglich, aber für dieses Beispiel gibt es viele Optionen:
xor = (/=)
xor a b = a /= b
xor a b = not (a == b)
xor a b = (a && not b) || (not a && b)
xor a b = (a || b) && not (a && b)
xor a b = odd (fromEnum a + fromEnum b)
Also tendiere ich dazu, zu denken, dass das erste besser ist und dasjenige sein sollte, das man auswählen sollte: leichter zu lesen und herauszufinden, was es macht
Ich stimme der Lesbarkeit zu. Der zweite ist jedoch sehr idiomatisch und für erfahrene Programmierer eher lesbar: Die Durchführung dieser trivialen Eta-Reduktion ist ziemlich verdächtig und könnte tatsächlich von der Absicht ablenken. Für eine optimierte Version schreibe ich es lieber vollständig in expliziter Form:
%Vor% Wenn jedoch eine solche Alternative wesentlich weniger lesbar ist als die idiomatische, sollten Sie sie nicht ersetzen, sondern Hinweise hinzufügen, damit der Compiler sie immer noch auf die ideale Version optimieren kann. Wie Daniel Fischer gezeigt hat, ist GHC ziemlich clever und wird es oft ohne Hilfe schaffen; Wenn dies nicht der Fall ist, können einige INLINE
und / oder RULES
Pragmas hinzugefügt werden. Es ist nicht einfach herauszufinden, wie man das macht, um optimale Leistung zu erzielen, aber das gleiche gilt für das Schreiben von schnellem Haskell98-Code.
Tags und Links optimization haskell