Ser man bort fra WB'en (som egentlig ikke er nødvendig for at besvare dit spørgsmål) - ser problemet ud til at have et ligetil svar, der kun er baseret på, hvordan udtryk vurderes under opgaver. Her er et eksempel:
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
For at få det til at fungere lavede jeg bevidst en definition for notGoodQ
for altid at returnere True
. Hvorfor var g[x0_]
evalueret under opgaven gennem TagSetDelayed
? Svaret er, at mens TagSetDelayed
(samt SetDelayed
) i en opgave h/:f[h[elem1,...,elemn]]:=...
anvender ingen regler, der f
kan have, vil den evaluere h[elem1,...,elem2]
, samt f
. Her er et eksempel:
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
Det faktum, at TagSetDelayed
er HoldAll
betyder ikke, at den ikke evaluerer sine argumenter - det betyder kun, at argumenterne ankommer til den uevalueret, og hvorvidt de vil blive evalueret eller ej afhænger af semantikken i TagSetDelayed
(som jeg kort beskrevet ovenfor). Det samme gælder for SetDelayed
, så den almindeligt anvendte erklæring om, at den "ikke vurderer sine argumenter" er ikke bogstaveligt talt korrekt. Et mere korrekt udsagn er, at den modtager argumenterne uevalueret og vurderer dem på en særlig måde - ikke vurderer r.h.s., mens den for l.h.s. vurderer hoved og elementer, men ikke anvender regler for hovedet. For at undgå det, kan du pakke tingene ind i HoldPattern
, sådan her:
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
Dette går igennem. Her er lidt brug:
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
Bemærk dog, at behovet for HoldPattern
inde i din venstre side, når du laver en definition, er ofte et tegn på, at udtrykket inde i dit hoved også kan evaluere under funktionskaldet, hvilket kan knække din kode. Her er et eksempel på, hvad jeg mener:
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Denne kode forsøger at fange tilfælde som h[f[something]]
, men det vil åbenbart mislykkes siden f[something]
vil evaluere, før evalueringen kommer til h
:
In[1535]:= h[f[5]]
Out[1535]= h[25]
For mig er behovet for HoldPattern
på l.h.s. er et tegn på, at jeg skal genoverveje mit design.
REDIGER
Med hensyn til debugging under indlæsning i WB, er en ting du kan gøre (IIRC, kan ikke tjekke lige nu) at bruge gode gamle print statements, hvis output vil fremkomme i WB'ens konsol. Personligt føler jeg sjældent et behov for debugger til dette formål (fejlretning af pakke ved indlæsning)
REDIGERING 2
Som svar på redigeringen i spørgsmålet:
Med hensyn til rækkefølgen af definitioner:ja, det kan du gøre, og det løser netop dette problem. Men generelt er dette ikke robust, og jeg vil ikke betragte det som en god generel metode. Det er svært at give et konkret råd til en sag, da den er lidt ude af sin kontekst, men det forekommer mig, at brugen af UpValues
her er uberettiget. Hvis dette er gjort for fejl - håndtering, er der andre måder
at gøre det uden at bruge UpValues
.
Generelt UpValues
bruges oftest til at overbelaste en funktion på en sikker måde uden at tilføje nogen regel til den funktion, der overbelastes. Et råd er at undgå at tilknytte UpValues
med hoveder, som også har DownValues
og kan evaluere -ved at gøre dette begynder du at spille et spil med evaluator, og vil til sidst tabe. Det sikreste er at vedhæfte UpValues
at inerte symboler (hoveder, beholdere), som ofte repræsenterer en "type" af objekter, som du ønsker at overbelaste en given funktion på.
Med hensyn til min kommentar om tilstedeværelsen af HoldPattern
indikerer et dårligt design. Der er bestemt er legitim brug af HoldPattern
, såsom denne (lidt kunstige) en:
In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Her er det berettiget, fordi i mange tilfælde Plus
forbliver uevalueret, og er nyttig i sin ikke-evaluerede form - da man kan udlede, at det repræsenterer en sum. Vi har brug for HoldPattern
her på grund af måden Plus
er defineret på et enkelt argument, og fordi et mønster tilfældigvis er et enkelt argument (selvom det generelt beskriver flere argumenter) under definitionen. Så vi bruger HoldPattern
her for at forhindre, at mønsteret behandles som normalt argument, men dette er for det meste forskelligt fra de tilsigtede anvendelsestilfælde for Plus
. Når dette er tilfældet (vi er sikre på, at definitionen vil fungere godt for tilsigtede anvendelsestilfælde), HoldPattern
er fint. Bemærk b.t.w., at dette eksempel også er skrøbeligt:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
Grunden til, at det stadig for det meste er OK, er, at vi normalt ikke bruger Plus
på et enkelt argument.
Men der er en anden gruppe af tilfælde, hvor strukturen af normalt leverede argumenter er den samme som strukturen af mønstre, der bruges til definitionen. I dette tilfælde indikerer mønsterevaluering under opgaven, at den samme evaluering vil ske med faktiske argumenter under funktionskaldene. Dit forbrug falder ind under denne kategori. Min kommentar til en designfejl var for sådanne tilfælde - du kan forhindre mønsteret i at evaluere, men du bliver nødt til også at forhindre argumenterne i at evaluere for at få dette til at fungere. Og mønstermatchning mod ikke fuldstændigt evalueret udtryk er skrøbeligt. Funktionen bør heller aldrig antage nogle ekstra betingelser (ud over hvad den kan typetjekke) for argumenterne.