Pomineme-li WB (která není k zodpovězení vaší otázky ve skutečnosti potřeba) – zdá se, že problém má přímou odpověď založenou pouze na tom, jak jsou výrazy vyhodnocovány během zadání. Zde je příklad:
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
Aby to fungovalo, schválně jsem vytvořil definici notGoodQ
vždy vrátit True
. Nyní, proč byl g[x0_]
vyhodnoceno během přiřazení prostřednictvím TagSetDelayed
? Odpověď zní, že zatímco TagSetDelayed
(stejně jako SetDelayed
) v přiřazení h/:f[h[elem1,...,elemn]]:=...
neuplatňuje žádná pravidla, která f
může mít, vyhodnotí h[elem1,...,elem2]
, stejně jako f
. Zde je příklad:
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
Skutečnost, že TagSetDelayed
je HoldAll
neznamená, že své argumenty nevyhodnocuje – znamená to pouze, že argumenty k němu dorazí nevyhodnocené a zda budou nebo nebudou vyhodnoceny, závisí na sémantice TagSetDelayed
(což jsem stručně popsal výše). Totéž platí pro SetDelayed
, takže běžně používané tvrzení, že „nehodnotí své argumenty“, není doslova správné. Správnější tvrzení je, že argumenty přijímá nevyhodnocené a hodnotí je zvláštním způsobem - nehodnotí r.h.s, zatímco pro l.h.s. hodnotí hlavu a prvky, ale neaplikuje pravidla pro hlavu. Abyste tomu zabránili, můžete věci zabalit do HoldPattern
, takto:
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
Tohle projde. Zde je nějaké použití:
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
Všimněte si však, že je potřeba HoldPattern
uvnitř vaší levé strany při vytváření definice je často známkou toho, že výraz uvnitř vaší hlavy se může také vyhodnotit během volání funkce, což může narušit váš kód. Zde je příklad toho, co mám na mysli:
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Tento kód se pokouší zachytit případy jako h[f[something]]
, ale zjevně selže, protože f[something]
vyhodnotí, než dojde k vyhodnocení h
:
In[1535]:= h[f[5]]
Out[1535]= h[25]
Pro mě je potřeba HoldPattern
na l.h.s. je známkou toho, že musím přehodnotit svůj návrh.
UPRAVIT
Pokud jde o ladění během načítání ve WB, jedna věc, kterou můžete udělat (IIRC, nelze nyní zkontrolovat), je použít staré dobré tiskové příkazy, jejichž výstup se objeví v konzole WB. Osobně jen zřídka cítím potřebu debuggeru pro tento účel (ladící balíček při načítání)
ÚPRAVA 2
V reakci na úpravu v otázce:
Pokud jde o pořadí definic:ano, můžete to udělat a řeší tento konkrétní problém. Ale obecně to není robustní a nepovažoval bych to za dobrou obecnou metodu. Je těžké dát jednoznačnou radu pro daný případ, protože je trochu mimo jeho kontext, ale zdá se mi, že použití UpValues
tady je to neopodstatněné. Pokud se tak děje kvůli zpracování chyb, existují jinými způsoby
to udělat bez použití UpValues
.
Obecně UpValues
se nejčastěji používají k přetížení některé funkce bezpečným způsobem, aniž by se k přetěžované funkci přidalo nějaké pravidlo. Jednou radou je vyhnout se přiřazování UpValues
s hlavami, které mají také DownValues
a může vyhodnotit - tím začnete hrát hru s hodnotitelem a nakonec prohrajete. Nejbezpečnější je připojit UpValues
na inertní symboly (hlavičky, kontejnery), které často představují „typ“ objektů, na které chcete danou funkci přetížit.
Pokud jde o můj komentář k přítomnosti HoldPattern
což naznačuje špatný design. Určitě existují legitimní použití pro HoldPattern
, jako je tento (poněkud umělý):
In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Zde je to oprávněné, protože v mnoha případech Plus
zůstává nevyhodnocena a je užitečná ve své neohodnocené formě - protože lze odvodit, že představuje součet. Potřebujeme HoldPattern
zde kvůli způsobu Plus
je definován na jediném argumentu, a protože vzor je během definice náhodou jediným argumentem (i když obecně popisuje více argumentů). Takže používáme HoldPattern
zde, aby se zabránilo zpracování vzoru jako normálního argumentu, ale to se většinou liší od zamýšlených případů použití pro Plus
. Kdykoli tomu tak je (jsme si jisti, že definice bude fungovat správně pro zamýšlené případy použití), HoldPattern
je v pořádku. Všimněte si, že tento příklad je také křehký:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
Důvod, proč je to stále většinou v pořádku, je ten, že normálně nepoužíváme Plus
na jediném argumentu.
Existuje však druhá skupina případů, kdy je struktura obvykle dodávaných argumentů stejná jako struktura vzorů použitých pro definici. V tomto případě vyhodnocení vzoru během přiřazení indikuje, že ke stejnému vyhodnocení dojde se skutečnými argumenty během volání funkce. Vaše použití spadá do této kategorie. Moje poznámka k chybě v návrhu byla pro takové případy - můžete zabránit vyhodnocení vzoru, ale budete muset zabránit také vyhodnocení argumentů, aby to fungovalo. A párování vzorů proti ne zcela vyhodnocenému výrazu je křehké. Funkce by také nikdy neměla převzít nějaké další podmínky (nad rámec toho, co může typově kontrolovat) pro argumenty.