PatternTest nicht optimiert?

8

Beim Vorbereiten einer Antwort auf Ein unerwartetes Verhalten von PatternTest in Mathematica stieß ich auf eine unerwartete Mathematica eigenes Verhalten.

Bitte beachten Sie:

%Vor% %Vor% %Vor%

Da, wie Simon's Zitat der Dokumentation präzisiert:

  

In einer Form wie __?test jedes Element in der Sequenz, das mit __ übereinstimmt   muss True ergeben, wenn der Test angewendet wird.

Ich frage mich, warum Mathematica das erste Element der Liste vier Mal testet. Natürlich gibt es vier Möglichkeiten, das zugrunde liegende Muster {x__, y__} zu erstellen, und wenn dies ein Condition Test wäre, müssten alle Elemente, die die Sequenz x ausmachen, getestet werden, aber ich denke nicht, dass das der ist Fall hier.

Stimmt die Logik nicht, wenn das erste Element der Liste fehlschlägt PatternTest , dann kann das angegebene Muster nicht übereinstimmen?

Wenn es gilt, warum macht Mathematica diese einfache Optimierung nicht?

Wenn ich ein Beispiel aus yodas Antwort entwerfe, ist hier ein weiteres Beispiel für eine Überbewertung:

%Vor%

Ich gebe zu, diesen Aspekt des Mustervergleichs noch nie zuvor erforscht zu haben, und ich bin beunruhigt über die scheinbare Ineffizienz. Ist das wirklich so schlimm, wie es aussieht, oder findet eine Art automatisches Caching statt? Print scheint dagegen anzuzeigen.

  • Gibt es eine effizientere musterbasierte Möglichkeit, dies zu schreiben?

  • Ist diese Redundanzstufe für das richtige Mustervergleichsverhalten erforderlich und warum?

Ich machte eine falsche Behauptung in Eile, aber ich lasse es, weil gute Antworten darunter sprechen. Bitte ignorieren Sie es in zukünftigen Antworten.

  

Es ist leicht zu zeigen, dass eine ähnliche Optimierung in anderen vorgenommen wird   Fälle:

%Vor%      

(Nichts wird gedruckt.)

%Vor%      

Hier testet Mathematica korrekt nie irgendwelche Elemente für y .

    
Mr.Wizard 13.12.2011, 04:23
quelle

4 Antworten

7

Ich denke, jeder vergisst die Nebenwirkungen, die in Testfunktionen möglich sind. Hier ist, was ich denke, geschieht: wie Mr.Wizard und andere erwähnt haben, gibt es mehrere Möglichkeiten, die Muster nur kombinatorisch zusammenpassen. Für jede Kombination von {x} und {y} wird zuerst das Muster x getestet. Nebenbei bemerkt, es hat keinen Sinn, Funktionen mehrerer Argumente zu definieren ( ## ), da, wie @Simon erklärte, die Testfunktion für jedes Element in der Sequenz separat angewendet wird. Und dies erklärt auch, warum nur das erste Element (-1) gedruckt wird: Sobald das erste nicht übereinstimmende Element gefunden wird, stoppt der Mustervergleicher und fährt fort, um die nächste verfügbare Kombination zu testen.

Hier ist ein illustrativeres Beispiel:

%Vor%

Nun druckt es alle, da die Funktion nacheinander auf sie angewendet wird, wie es in diesem Fall sein muss.

Nun zum Kern der Sache. Hier richte ich eine Testfunktion mit einem Nebeneffekt ein, der seine Meinung ändert, nachdem er das allererste Element getestet hat:

%Vor%

Die erste Kombination (die {-1},{2,3,4,5} ist, wird abgelehnt, da die Funktion False zuerst gibt. Die zweite ( {-1,2},{3,4,5} ) wird jedoch akzeptiert. Und genau das beobachten wir:

%Vor%

Der Druck wurde gestoppt, sobald der Muster-Matcher eine Übereinstimmung gefunden hat.

Von nun an muss es offensichtlich sein, dass die in der Frage erwähnte Optimierung und einige andere Antworten im Allgemeinen nicht möglich sind, da pattern-matcher keine Kontrolle über den möglicherweise in den Testfunktionen vorhandenen änderbaren Zustand hat.

Wenn wir über den Mustervergleich nachdenken, betrachten wir ihn normalerweise als einen von der Bewertung getrennten Prozess, was weitgehend zutrifft, da der Muster-Matcher eine eingebaute Komponente des Systems ist, die, sobald Muster und Ausdrücke ausgewertet werden über und umgeht weitgehend die Hauptauswerteschleife. Es gibt jedoch bemerkenswerte Ausnahmen, die das Muster-Matching um den Preis der Verwicklung mit dem Evaluator leistungsfähiger machen. Dazu gehören die Verwendungen von Condition und PatternTest , da diese beiden die "Eintrittspunkte" des Hauptbewertungsprozesses in den ansonsten von ihm getrennten Mustervergleichsprozess darstellen. Sobald der Muster-Matcher einen von diesen trifft, ruft er den Haupt-Evaluator über den zu testenden Zustand auf, und alles ist dann möglich. Das bringt mich wieder zu der Beobachtung, dass der Pattern-Matcher am effizientesten ist, wenn Tests mit PatternTest und Condition fehlen und die Patterns vollständig syntaktisch sind - im Fall kann sie optimiert werden .

    
Leonid Shifrin 13.12.2011, 07:27
quelle
3

Nur eine kurze Schätzung hier, aber {__?Positive, y__?test} muss mit dem Anfang der Liste bis zum Ende übereinstimmen. Also, {-1, 2, 3, 4, 5} schlägt auf dem ersten Element fehl, daher kein Ausdruck. Versuchen Sie, Positive durch ((Print[##];Positive[#])&) zu ersetzen, und Sie werden sehen, dass es sich genauso verhält wie das erste Muster.

    
rcollyer 13.12.2011 05:37
quelle
3

Ich denke, dass Ihre Prämisse, dass Mathematica den Mustertest optimiert, falsch ist. Betrachten Sie das Muster {x__, y__} . Wie Sie richtig sagen, gibt es 4 Möglichkeiten, auf die {1,2,3,4,5} in dieses Muster passen kann. Wenn Sie nun einen Mustertest mit ?test hinzufügen, sollte MatchQ True zurückgeben, wenn any der 4 Wege mit dem Muster übereinstimmt. Also muss es unbedingt alle möglichen Optionen testen bis eine Übereinstimmung gefunden wurde. Die Optimierung erfolgt also nur, wenn es positive Ergebnisse gibt, und nicht, wenn das Muster fehlschlägt.

Hier ist eine Modifikation Ihres Beispiels mit Positive , die Ihnen zeigt, dass Mathematica nicht die Optimierung so macht, wie Sie behaupten:

%Vor%

Es druckt es 4 mal! Es endet korrekt mit einem Druck, wenn das erste Element tatsächlich positiv ist (dadurch muss der Rest nicht getestet werden). Der Test für das zweite Element wird angewendet only , wenn der erste True ist, weshalb der Test für y in Ihrem nicht angewendet wurde. Hier ist ein anderes Beispiel:

%Vor%

Ich stimme Ihrer Logik zu, dass, wenn die erste fehlschlägt, dann, da jedes Element übereinstimmen muss, alle nachfolgenden Möglichkeiten ebenfalls fehlschlagen. Meine Vermutung ist, dass Mathematica noch nicht intelligent ist, und es ist einfacher, für jedes von _ , __ oder ___ dasselbe Verhalten zu implementieren, als für jedes andere. Wenn Sie ein anderes Verhalten implementieren, je nachdem, ob Sie __ oder ___ verwenden, wird die wahre Natur des Tests maskiert und das Debugging erschwert.

    
abcd 13.12.2011 05:38
quelle
2

Ok, ich werde es versuchen.

%Vor%

zeigt, dass die Slot-Sequenz redundant ist, da nur ein Element, in diesem Fall das erste in der Sequenz, getestet wird. z.B.

%Vor%

Druckt jetzt drei Zweier. Wenn Sie das erste Muster in BlankSequence ändern, werden mehr Permutationen generiert und einige davon haben andere erste Elemente

%Vor%

In Ihrem letzten Beispiel

%Vor%

Das erste Element schlägt immer den Test fehl. Wenn Sie dies zu

ändern %Vor%

Sie werden etwas mehr sehen, was Sie erwartet haben.

    
Mike Honeychurch 13.12.2011 05:51
quelle