Нежелательная оценка в заданиях в Mathematica: почему это происходит и как отлаживать его во время загрузки пакета? - PullRequest
9 голосов
/ 14 сентября 2011

Я разрабатываю (большой) пакет, который больше не загружается должным образом.Это произошло после того, как я изменил одну строку кода.Когда я пытаюсь загрузить пакет (с необходимостью), пакет начинает загружаться, а затем одно из заданных задержек «оживает» (т. Е. Как-то оценивается), попадает в ловушку подпрограммы перехвата ошибок, загруженной несколькими строками раньше, и пакетзагрузка прерывается.
Процедура перехвата ошибок с помощью abort выполняет свою работу, за исключением того, что она не должна была вызываться в первую очередь на этапе загрузки пакета.Сообщение об ошибке показывает, что неправильный аргумент на самом деле является выражением шаблона, которое я использую для lhs определения с задержкой набора несколькими строками позже.

Примерно так:

……Some code lines

Changed line of code 

g[x_?NotGoodQ]:=(Message[g::nogood, x];Abort[])

……..some other code lines

g/: cccQ[g[x0_]]:=True

Когда яПри попытке загрузить пакет я получаю:

g::nogood: Argument x0_ is not good

Как видите, переданный аргумент является шаблоном и может быть получен только из строки кода выше.

Я попытался найти причинуза такое поведение, но я пока не добился успеха.Поэтому я решил использовать мощные средства отладки Workbench.

Я бы хотел шаг за шагом (или с точками останова) увидеть, что происходит при загрузке пакета.Я еще не слишком знаком с WB, но кажется, что, используя Debug as…, пакет сначала загружается, а затем отлаживается с помощью точек останова, ect.Моя проблема в том, что пакет даже не загружается полностью!И любая точка останова, установленная перед загрузкой пакета, кажется неэффективной.

Итак ... 2 вопроса:

  1. Может кто-нибудь объяснить, почему эти строки кода "оживают" во время загрузки пакета?(насколько я вижу, в пакете не осталось явных синтаксических ошибок или фрагментов кода)
  2. Может кто-нибудь объяснить, как (если) возможно проверить / отладить код пакета во время загрузки в WB?

Спасибо за любую помощь.

Редактировать

В свете ответа Леонида и использования его примера EvenQ: Мы можем избежать использования Holdpattern просто путем определения значений для g ДО ДО значений для g

notGoodQ[x_] := EvenQ[x];
Clear[g];
g /: cccQ[g[x0_]] := True
g[x_?notGoodQ] := (Message[g::nogood, x]; Abort[])

Now

?g

Global`g

cccQ[g[x0_]]^:=True



g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])



In[6]:= cccQ[g[1]]

Out[6]= True

while

In[7]:= cccQ[g[2]]

During evaluation of In[7]:= g::nogood: -- Message text not found -- (2)

Out[7]= $Aborted

Итак ... общее правило:

При написании функции g сначала определите значения для g, затем определите значения для g, в противном случае используйте Holdpattern

Можете ли вы подписаться на это правило?

Леонид говорит, что используя Holdpattern может означать улучшенный дизайн.Помимо решения, указанного выше, как можно улучшить дизайн небольшого кода, приведенного выше, или, что лучше, в целом при работе с повышенными значениями?

Спасибо за помощь

1 Ответ

13 голосов
/ 14 сентября 2011

Оставляя в стороне 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, поэтому обычно используемое утверждение, что оно «не оценивает свои аргументы», не является буквально правильным.Более правильное утверждение состоит в том, что он получает аргументы, которые не были оценены, и оценивает их особым образом - не оценивает rhs, тогда как для lhs оценивает голову и элементы, но не применяет правила для головы.Чтобы избежать этого, вы можете обернуть вещи в 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 на lhs является знаком того, что мне нужно пересмотреть мой дизайн.

РЕДАКТИРОВАТЬ

Что касается отладки во время загрузки в WB, то, что вы можете сделать (IIRC, не можете проверить прямо сейчас), это использовать старые добрые операторы print, вывод которых будет отображаться в консоли 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 для одного аргумента.Существует вторая группа случаев, где структура обычно предоставляемых аргументов такая же, как структура шаблонов, используемых для определения.В этом случае оценка шаблона во время назначения указывает, что такая же оценка будет происходить с фактическими аргументами во время вызовов функции.Ваше использование попадает в эту категорию.Мой комментарий по поводу ошибки проектирования был для таких случаев - вы можете помешать оценке шаблона, но вам придется также помешать оценке аргументов, чтобы эта работа работала.А сопоставление с шаблоном по отношению к не полностью оцененному выражению хрупко.Кроме того, функция никогда не должна принимать некоторые дополнительные условия (помимо того, что она может проверять тип) для аргументов.

...