Abgesehen vom WB (der zur Beantwortung Ihrer Frage nicht wirklich benötigt wird) scheint das Problem eine einfache Antwort zu haben, die nur darauf basiert, wie Ausdrücke während der Aufgaben ausgewertet werden. Hier ist ein Beispiel:
In[1505]:=
notGoodQ[x_]:=True;
Clear[g];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
In[1509]:= g/:cccQ[g[x0_]]:=True
During evaluation of In[1509]:= g::nogood: -- Message text not found -- (x0_)
Out[1509]= $Aborted
Damit es funktioniert, habe ich absichtlich eine Definition für notGoodQ
erstellt um immer True
zurückzugeben . Nun, warum war g[x0_]
bei der Zuweisung durch TagSetDelayed
ausgewertet ? Die Antwort lautet, während TagSetDelayed
(sowie SetDelayed
) in einer Zuweisung h/:f[h[elem1,...,elemn]]:=...
wendet keine Regeln an, die f
hat, wird es h[elem1,...,elem2]
auswerten , sowie f
. Hier ist ein Beispiel:
In[1513]:=
ClearAll[h,f];
h[___]:=Print["Evaluated"];
In[1515]:= h/:f[h[1,2]]:=3
During evaluation of In[1515]:= Evaluated
During evaluation of In[1515]:= TagSetDelayed::tagnf: Tag h not found in f[Null]. >>
Out[1515]= $Failed
Die Tatsache, dass TagSetDelayed
ist HoldAll
bedeutet nicht, dass es seine Argumente nicht auswertet - es bedeutet nur, dass die Argumente unausgewertet ankommen, und ob sie ausgewertet werden oder nicht, hängt von der Semantik von TagSetDelayed
ab (was ich oben kurz beschrieben habe). Dasselbe gilt für SetDelayed
, daher ist die häufig verwendete Aussage, dass es "seine Argumente nicht bewertet", nicht wörtlich richtig. Eine korrektere Aussage ist, dass es die Argumente unevaluiert empfängt und sie auf besondere Weise auswertet – nicht die rechte Seite auswertet, während für die linke Seite Kopf und Elemente ausgewertet werden, aber keine Regeln für den Kopf angewendet werden. Um dies zu vermeiden, können Sie Dinge in HoldPattern
einschließen , etwa so:
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
Das geht durch. Hier ist einige Verwendung:
In[1527]:= cccQ[g[1]]
Out[1527]= True
In[1528]:= cccQ[g[2]]
During evaluation of In[1528]:= g::nogood: -- Message text not found -- (2)
Out[1528]= $Aborted
Beachten Sie jedoch, dass HoldPattern
erforderlich ist in Ihrer linken Seite, wenn Sie eine Definition vornehmen, ist oft ein Zeichen dafür, dass der Ausdruck in Ihrem Kopf auch während des Funktionsaufrufs ausgewertet werden kann, was Ihren Code beschädigen kann. Hier ist ein Beispiel dafür, was ich meine:
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Dieser Code versucht Fälle wie h[f[something]]
abzufangen , aber es wird offensichtlich seit f[something]
fehlschlagen wird ausgewertet, bevor die Auswertung zu h
kommt :
In[1535]:= h[f[5]]
Out[1535]= h[25]
Für mich die Notwendigkeit von HoldPattern
auf der linken Seite ist ein Zeichen dafür, dass ich mein Design überdenken muss.
BEARBEITEN
In Bezug auf das Debuggen während des Ladens in WB können Sie (IIRC, kann es jetzt nicht überprüfen) die guten alten Druckanweisungen verwenden, deren Ausgabe in der WB-Konsole angezeigt wird. Ich persönlich habe selten das Bedürfnis nach einem Debugger für diesen Zweck (Debugging-Paket beim Laden)
BEARBEITEN 2
Als Antwort auf die Bearbeitung in der Frage:
In Bezug auf die Reihenfolge der Definitionen:Ja, das können Sie tun, und es löst dieses spezielle Problem. Aber im Allgemeinen ist dies nicht robust, und ich würde es nicht als eine gute allgemeine Methode betrachten. Es ist schwer, für einen konkreten Fall einen konkreten Rat zu geben, da es etwas aus dem Kontext gerissen ist, aber mir scheint, dass die Verwendung von UpValues
hier ist ungerechtfertigt. Wenn dies zur Fehlerbehandlung getan wird, gibt es andere Wege
um dies ohne Verwendung von UpValues
zu tun .
Im Allgemeinen UpValues
werden am häufigsten verwendet, um eine Funktion auf sichere Weise zu überladen, ohne der zu überladenden Funktion eine Regel hinzuzufügen. Ein Ratschlag ist, die Verknüpfung von UpValues
zu vermeiden mit Köpfen, die auch DownValues
haben und kann evaluieren - dadurch fangen Sie an, ein Spiel mit evaluator zu spielen, und werden schließlich verlieren. Am sichersten ist es, UpValues
anzuhängen Symbole (Köpfe, Behälter) zu inertisieren, die oft einen "Typ" von Objekten darstellen, auf die Sie eine bestimmte Funktion überladen möchten.
Zu meinem Kommentar zum Vorhandensein von HoldPattern
auf ein schlechtes Design hindeutet. Es gibt sicherlich sind legitime Verwendungen für HoldPattern
, wie dieses (etwas künstliche):
In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Hier ist es gerechtfertigt, weil in vielen Fällen Plus
bleibt unbewertet und ist in seiner unbewerteten Form nützlich - da man daraus schließen kann, dass es eine Summe darstellt. Wir brauchen HoldPattern
hier übrigens Plus
auf einem einzelnen Argument definiert ist und weil ein Muster während der Definition zufällig ein einzelnes Argument ist (obwohl es im Allgemeinen mehrere Argumente beschreibt). Also verwenden wir HoldPattern
hier, um zu verhindern, dass das Muster als normales Argument behandelt wird, aber dies unterscheidet sich größtenteils von den beabsichtigten Anwendungsfällen für Plus
. Wann immer dies der Fall ist (wir sind sicher, dass die Definition für die beabsichtigten Anwendungsfälle gut funktioniert), HoldPattern
ist gut. Beachten Sie übrigens, dass dieses Beispiel auch zerbrechlich ist:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
Der Grund, warum es meistens immer noch in Ordnung ist, ist, dass wir normalerweise Plus
nicht verwenden auf einem einzigen Argument.
Aber es gibt eine zweite Gruppe von Fällen, in denen die Struktur der normalerweise gelieferten Argumente die gleiche ist wie die Struktur der Muster, die für die Definition verwendet werden. In diesem Fall zeigt die Musterauswertung während der Zuweisung an, dass die gleiche Auswertung mit tatsächlichen Argumenten während der Funktionsaufrufe erfolgen wird. Ihre Nutzung fällt in diese Kategorie. Mein Kommentar für einen Designfehler war für solche Fälle - Sie können verhindern, dass das Muster ausgewertet wird, aber Sie müssen auch verhindern, dass die Argumente ausgewertet werden, damit dies funktioniert. Und der Musterabgleich mit einem nicht vollständig ausgewerteten Ausdruck ist fragil. Außerdem sollte die Funktion niemals einige zusätzliche Bedingungen (über das hinaus, was sie typisieren kann) für die Argumente annehmen.