Afgezien van de WB (die niet echt nodig is om uw vraag te beantwoorden) lijkt het probleem een eenvoudig antwoord te hebben dat alleen is gebaseerd op hoe uitdrukkingen worden geëvalueerd tijdens opdrachten. Hier is een voorbeeld:
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
Om het te laten werken, heb ik bewust een definitie gemaakt voor notGoodQ
om altijd True
terug te geven . Nu, waarom was g[x0_]
geëvalueerd tijdens de opdracht via TagSetDelayed
? Het antwoord is dat, terwijl TagSetDelayed
(evenals SetDelayed
) in een opdracht h/:f[h[elem1,...,elemn]]:=...
past geen regels toe die f
kan hebben, zal het h[elem1,...,elem2]
. evalueren , evenals f
. Hier is een voorbeeld:
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
Het feit dat TagSetDelayed
is HoldAll
betekent niet dat het zijn argumenten niet evalueert - het betekent alleen dat de argumenten ongeëvalueerd aankomen, en of ze al dan niet worden geëvalueerd, hangt af van de semantiek van TagSetDelayed
(die ik hierboven kort heb beschreven). Hetzelfde geldt voor SetDelayed
, dus de veelgebruikte uitspraak dat het "zijn argumenten niet evalueert" is niet letterlijk correct. Een correctere verklaring is dat het de argumenten ongeëvalueerd ontvangt en ze op een speciale manier evalueert - niet de r.h.s. evalueren, terwijl voor l.h.s. head en elementen worden geëvalueerd, maar geen regels voor de head toepassen. Om dat te voorkomen, kun je dingen in HoldPattern
. plaatsen , zoals dit:
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
Dit gaat door. Hier is wat gebruik:
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
Merk echter op dat de noodzaak voor HoldPattern
binnenkant van je linkerkant bij het maken van een definitie is vaak een teken dat de uitdrukking in je hoofd ook kan evalueren tijdens de functieaanroep, wat je code kan breken. Hier is een voorbeeld van wat ik bedoel:
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Deze code probeert gevallen te vangen zoals h[f[something]]
, maar het zal uiteraard mislukken sinds f[something]
zal evalueren voordat de evaluatie komt tot h
:
In[1535]:= h[f[5]]
Out[1535]= h[25]
Voor mij is de behoefte aan HoldPattern
op de linkse is een teken dat ik mijn ontwerp moet heroverwegen.
BEWERKEN
Wat betreft het debuggen tijdens het laden in WB, één ding dat je kunt doen (IIRC, kan nu niet controleren) is om goede oude afdrukinstructies te gebruiken, waarvan de uitvoer in de WB-console zal verschijnen. Persoonlijk heb ik zelden behoefte aan een debugger voor dit doel (debuggen van pakket tijdens het laden)
BEWERK 2
Als reactie op de bewerking in de vraag:
Wat betreft de volgorde van definities:ja, je kunt dit doen, en het lost dit specifieke probleem op. Maar over het algemeen is dit niet robuust, en ik zou het geen goede algemene methode vinden. Het is moeilijk om een duidelijk advies te geven voor een casus, omdat het een beetje uit zijn context is, maar het lijkt mij dat het gebruik van UpValues
hier is onterecht. Als dit wordt gedaan voor foutafhandeling, zijn er andere manieren
om het te doen zonder UpValues
. te gebruiken .
Over het algemeen geldt:UpValues
worden meestal gebruikt om een bepaalde functie op een veilige manier te overbelasten, zonder enige regel toe te voegen aan de overbelaste functie. Een advies is om het associëren van UpValues
te vermijden met heads die ook DownValues
. hebben en kan evalueren - door dit te doen, begin je een spel met evaluator te spelen en uiteindelijk verlies je. Het veiligst is om UpValues
. toe te voegen tot inerte symbolen (koppen, containers), die vaak een "type" objecten vertegenwoordigen waarop u een bepaalde functie wilt overbelasten.
Wat betreft mijn opmerking over de aanwezigheid van HoldPattern
wijst op een slecht ontwerp. Er zijn zeker zijn legitiem gebruik voor HoldPattern
, zoals deze (enigszins kunstmatige):
In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Hier is het gerechtvaardigd omdat in veel gevallen Plus
blijft ongeëvalueerd, en is nuttig in zijn ongeëvalueerde vorm - aangezien men kan afleiden dat het een som vertegenwoordigt. We hebben HoldPattern
nodig hier vanwege de manier waarop Plus
wordt gedefinieerd op een enkel argument, en omdat een patroon tijdens de definitie een enkel argument is (ook al beschrijft het over het algemeen meerdere argumenten). We gebruiken dus HoldPattern
hier om te voorkomen dat het patroon als een normaal argument wordt behandeld, maar dit verschilt meestal van de beoogde use-cases voor Plus
. Wanneer dit het geval is (we zijn er zeker van dat de definitie goed zal werken voor beoogde gebruiksgevallen), HoldPattern
is goed. Merk trouwens op dat dit exemplaar ook kwetsbaar is:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
De reden waarom het nog grotendeels OK is, is dat we normaal gesproken geen Plus
gebruiken op een enkel argument.
Maar er is een tweede groep gevallen, waarbij de structuur van gewoonlijk geleverde argumenten dezelfde is als de structuur van patronen die voor de definitie worden gebruikt. In dit geval geeft patroonevaluatie tijdens de toewijzing aan dat dezelfde evaluatie zal gebeuren met werkelijke argumenten tijdens de functieaanroepen. Uw verbruik valt in deze categorie. Mijn opmerking over een ontwerpfout was voor dergelijke gevallen - je kunt voorkomen dat het patroon evalueert, maar je moet voorkomen dat de argumenten ook evalueren, om dit te laten werken. En het matchen van patronen met niet volledig geëvalueerde expressie is fragiel. Ook mag de functie nooit enkele extra voorwaarden aannemen (buiten wat hij kan typen) voor de argumenten.