Ist es besser Hüter als Muster für Rekursionsfunktionen in Haskell zu verwenden?

8

Ich wundere mich nur über eine Rekursionsfunktion, die ich in Haskell auslege. Ist es generell besser, Wächter als Muster für Rekursionsfunktionen zu verwenden?

Ich bin mir nicht sicher, was das beste Layout ist, aber ich weiß, dass Muster besser sind, wenn man Funktionen wie dieses definiert:

%Vor%

ist viel besser als

%Vor%

Ich bin mir aber nur nicht sicher, ob es bei der Rekursion darum geht, ob das gleich oder anders ist.

Nur nicht ganz sicher in der Terminologie: Ich benutze so etwas:

%Vor%

oder wäre das besser?

%Vor%     
maclunian 20.05.2011, 20:18
quelle

5 Antworten

8

Meine allgemeine Faustregel wäre dies:

  • Verwenden Sie die Mustererkennung, wenn der Wächter ein einfacher == Check ist.

Bei der Rekursion suchen Sie normalerweise nach einem Basisfall. Wenn Ihr Basisfall ein einfacher == Check ist, dann verwenden Sie den Mustervergleich.

Also würde ich das normalerweise tun:

%Vor%

Stattdessen ( null überprüft einfach, ob eine Liste leer ist. Es ist im Grunde == [] ):

%Vor%

Mustervergleich soll dein Leben einfacher machen, imho, also solltest du am Ende tun, was für dich Sinn macht. Wenn Sie mit einer Gruppe arbeiten, tun Sie, was für die Gruppe sinnvoll ist.

[Update]

Für Ihren speziellen Fall würde ich so etwas tun:

%Vor%

Musterübereinstimmungen fallen wie Wächter von oben nach unten und enden bei der ersten Definition, die der Eingabe entspricht. Ich habe das Unterstreichungssymbol verwendet, um anzuzeigen, dass es mir bei der ersten Musterübereinstimmung egal war, was das Argument y war, und für die zweite Musterübereinstimmung war mir egal, was das Listenargument war Verwenden Sie die Liste in dieser Berechnung, dann sollten Sie den Unterstrich nicht verwenden). Da es immer noch ziemlich einfach ist, würde ich persönlich bei der Mustererkennung bleiben.

Aber ich denke, es ist eine Frage der persönlichen Präferenz; Ihr Code ist perfekt lesbar und korrekt wie er ist. Wenn ich mich nicht irre, werden beim Kompilieren des Codes sowohl Wächter als auch Musterübereinstimmungen am Ende in case-Anweisungen umgewandelt.

    
Dan Burton 20.05.2011, 20:36
quelle
4

Eine einfache Regel

  • Wenn Sie eine Datenstruktur rekursiv verwenden, verwenden Sie den Mustervergleich
  • Wenn Ihre rekursive Bedingung komplexer ist, verwenden Sie Wächter.

Diskussion

Grundsätzlich hängt es von dem Test ab, den Sie tun möchten, um die Rekursion zu schützen. Wenn es sich um einen Test für die Struktur eines Datentyps handelt, verwenden Sie den Mustervergleich, da dies effizienter ist als das redundante Testen auf Gleichheit.

In Ihrem Beispiel ist die Mustererkennung für Ganzzahlen offensichtlich sauberer und effizienter:

%Vor%

Das Gleiche gilt für rekursive Aufrufe bei jedem Datentyp, bei denen Sie Fälle über die Form der Daten unterscheiden.

Wenn Sie nun kompliziertere logische Bedingungen hätten, würden Wächter einen Sinn ergeben.

    
Don Stewart 20.05.2011 20:21
quelle
2

Es gibt nicht wirklich harte und schnelle Regeln dafür, weshalb die Antworten, die du bekommen hast, ein wenig verschwommen waren. Einige Entscheidungen sind einfach, wie Pattern-Matching auf [] , anstatt mit f xs | null xs = ... zu schützen oder, heath forbid, f xs | length xs == 0 = ... , was in mehrfacher Hinsicht schrecklich ist. Aber wenn es kein zwingendes praktisches Problem gibt, verwenden Sie einfach den Code, der den Code klarer macht.

Betrachten Sie zum Beispiel diese Funktionen (die nicht wirklich nützlich sind und nur als Illustrationen dienen):

%Vor%

In f1 betonen die separaten Muster, dass die Rekursion zwei Basisfälle hat. In f2 betonen die Wächter, dass 0 nur ein Sonderfall für einige Berechnungen ist (die meisten werden von calc gemacht, definiert in einer where -Klausel, die von beiden Teilen der Wache geteilt wird) und ändert nicht die Struktur der Berechnung.

    
C. A. McCann 20.05.2011 21:19
quelle
2

@Dan ist richtig: Es ist im Grunde eine Frage der persönlichen Vorlieben und hat keinen Einfluss auf den generierten Code. Dieses Modul:

%Vor%

hat folgenden Kern erzeugt:

%Vor%

Genau das gleiche, außer für den anderen Standardfall, der in beiden Fällen ein Mustervergleichsfehler ist. GHC gewöhnt sogar die Saiten für die übereinstimmenden Fälle an.

    
John L 20.05.2011 23:07
quelle
2

Die bisherigen Antworten erwähnen nicht den Vorteil der Mustererkennung, die für mich am wichtigsten ist: die Fähigkeit, Gesamtfunktionen sicher zu implementieren.

Wenn Sie einen Mustervergleich durchführen, können Sie sicher auf die interne Struktur des Objekts zugreifen, ohne befürchten zu müssen, dass dieses Objekt etwas anderes ist. Falls Sie einige der Muster vergessen haben, kann der Compiler Sie warnen (leider ist diese Warnung in GHC standardmäßig deaktiviert).

Zum Beispiel beim Schreiben:

%Vor%

Sie sind gezwungen, die Nicht-Gesamtfunktionen head und tail zu verwenden, wodurch die Lebensdauer Ihres Programms gefährdet ist. Wenn Sie unter Guard-Bedingungen einen Fehler machen, kann der Compiler Ihnen nicht helfen.

Wenn Sie andererseits bei der Mustererkennung einen Fehler machen, kann der Compiler Ihnen einen Fehler oder eine Warnung geben, je nachdem, wie schlecht Ihr Fehler war.

Einige Beispiele:

%Vor%     
Rotsor 21.05.2011 05:45
quelle