Оставяйки настрана WB (който всъщност не е необходим, за да се отговори на вашия въпрос) - проблемът изглежда има ясен отговор, базиран само на това как изразите се оценяват по време на задания. Ето един пример:
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
За да работи, нарочно направих дефиниция за notGoodQ
за да връща винаги True
. Сега, защо беше g[x0_]
оценено по време на заданието чрез TagSetDelayed
? Отговорът е, че докато TagSetDelayed
(както и SetDelayed
) в задание h/:f[h[elem1,...,elemn]]:=...
не прилага никакви правила, които f
може да има, ще оцени h[elem1,...,elem2]
, както и f
. Ето един пример:
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
Фактът, че TagSetDelayed
е HoldAll
не означава, че не оценява своите аргументи - това означава само, че аргументите пристигат до него неоценени и дали ще бъдат оценени или не зависи от семантиката на TagSetDelayed
(което накратко описах по-горе). Същото важи и за SetDelayed
, така че често използваното твърдение, че "не оценява аргументите си" не е буквално правилно. По-правилното твърдение е, че той получава аргументите неоценени и ги оценява по специален начин - не оценява r.h.s, докато за l.h.s. оценява главата и елементите, но не прилага правила за главата. За да избегнете това, можете да обвиете нещата в HoldPattern
, като това:
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
Това минава през. Ето някои употреби:
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
Имайте предвид обаче, че необходимостта от HoldPattern
вътре в лявата ви страна, когато правите дефиниция, често е знак, че изразът в главата ви може също да се оцени по време на извикването на функция, което може да разбие вашия код. Ето пример за това, което имам предвид:
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Този код се опитва да улови случаи като h[f[something]]
, но очевидно ще се провали, тъй като f[something]
ще направи оценка, преди оценката да стигне до h
:
In[1535]:= h[f[5]]
Out[1535]= h[25]
За мен необходимостта от HoldPattern
на л.ч.с. е знак, че трябва да преразгледам своя дизайн.
РЕДАКТИРАНЕ
По отношение на отстраняването на грешки по време на зареждане в WB, едно нещо, което можете да направите (IIRC, не мога да проверя в момента) е да използвате добрите стари оператори за печат, чийто изход ще се появи в конзолата на WB. Лично аз рядко изпитвам нужда от дебъгер за тази цел (пакет за отстраняване на грешки при зареждане)
РЕДАКТИРАНЕ 2
В отговор на редакцията във въпроса:
По отношение на реда на дефинициите:да, можете да направите това и това решава този конкретен проблем. Но като цяло това не е стабилно и не бих го счел за добър общ метод. Трудно е да се даде категоричен съвет за даден случай, тъй като е малко извън контекста му, но ми се струва, че използването на UpValues
тук е неоправдано. Ако това се прави за обработка на грешки, има други начини
за да го направите без да използвате UpValues
.
Обикновено UpValues
се използват най-често за претоварване на някаква функция по безопасен начин, без да се добавя никакво правило към претоварената функция. Един съвет е да избягвате свързването на UpValues
с глави, които също имат DownValues
и може да оценявате - като правите това, започвате да играете игра с оценител и в крайна сметка ще загубите. Най-безопасното е да прикачите UpValues
към инертни символи (глави, контейнери), които често представляват „тип“ обекти, върху които искате да претоварите дадена функция.
Относно моя коментар относно наличието на HoldPattern
което показва лош дизайн. Със сигурност има законно използване на HoldPattern
, като този (донякъде изкуствен):
In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Тук е оправдано, защото в много случаи Plus
остава неоценен и е полезен в неоценената си форма - тъй като може да се заключи, че представлява сума. Нуждаем се от HoldPattern
тук заради начина Plus
е дефиниран с един аргумент и тъй като моделът се оказва единичен аргумент (въпреки че обикновено описва множество аргументи) по време на дефиницията. И така, ние използваме HoldPattern
тук, за да предотвратите третирането на шаблона като нормален аргумент, но това е най-вече различно от предвидените случаи на употреба за Plus
. Когато случаят е такъв (сигурни сме, че дефиницията ще работи добре за предвидените случаи на употреба), HoldPattern
е наред. Обърнете внимание, че този пример също е крехък:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
Причината, поради която все още е почти добре, е, че обикновено не използваме Plus
на един аргумент.
Но има втора група случаи, при които структурата на обикновено предоставяните аргументи е същата като структурата на моделите, използвани за дефиницията. В този случай оценката на шаблона по време на присвояването показва, че същата оценка ще се случи с действителните аргументи по време на извикванията на функция. Вашето използване попада в тази категория. Коментарът ми за недостатък в дизайна беше за такива случаи - можете да предотвратите оценката на шаблона, но ще трябва да предотвратите и оценката на аргументите, за да работи това. А съпоставянето на образци срещу не напълно оценено изразяване е крехко. Също така, функцията никога не трябва да приема някои допълнителни условия (отвъд това, което може да провери на типа) за аргументите.