MySQL Workbench
 sql >> Base de Dados >  >> Database Tools >> MySQL Workbench

Avaliação indesejada em atribuições no Mathematica:por que isso acontece e como depurá-lo durante o carregamento do pacote?


Deixando de lado o WB (que não é realmente necessário para responder à sua pergunta) - o problema parece ter uma resposta direta baseada apenas em como as expressões são avaliadas durante as atribuições. Aqui está um exemplo:
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

Para fazê-lo funcionar, eu deliberadamente fiz uma definição para notGoodQ para sempre retornar True . Agora, por que foi g[x0_] avaliado durante a atribuição através de TagSetDelayed ? A resposta é que, enquanto TagSetDelayed (assim como SetDelayed ) em uma atribuição h/:f[h[elem1,...,elemn]]:=... não aplica nenhuma regra que f pode ter, ele avaliará h[elem1,...,elem2] , bem como f . Aqui está um exemplo:
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  

O fato de TagSetDelayed é HoldAll não significa que ele não avalie seus argumentos - significa apenas que os argumentos chegam a ele sem avaliação, e se eles serão avaliados ou não depende da semântica de TagSetDelayed (que eu descrevi brevemente acima). O mesmo vale para SetDelayed , portanto, a declaração comumente usada de que "não avalia seus argumentos" não está literalmente correta. Uma afirmação mais correta é que ela recebe os argumentos não avaliados e os avalia de uma maneira especial - não avalia o r.h.s, enquanto para l.h.s. avalia cabeça e elementos, mas não aplica regras para a cabeça. Para evitar isso, você pode envolver as coisas em HoldPattern , assim:
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;

Isso passa. Aqui está algum uso:
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

Observe, no entanto, que a necessidade de HoldPattern dentro do seu lado esquerdo ao fazer uma definição geralmente é um sinal de que a expressão dentro de sua cabeça também pode ser avaliada durante a chamada de função, o que pode quebrar seu código. Aqui está um exemplo do que quero dizer:
In[1532]:= 
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;

Este código tenta capturar casos como h[f[something]] , mas obviamente falhará, pois f[something] avaliará antes que a avaliação chegue a h :
In[1535]:= h[f[5]]
Out[1535]= h[25]

Para mim, a necessidade de HoldPattern no l.h.s. é um sinal de que preciso reconsiderar meu design.

EDITAR

Com relação à depuração durante o carregamento no WB, uma coisa que você pode fazer (IIRC, não pode verificar agora) é usar as boas e velhas instruções de impressão, cuja saída aparecerá no console do WB. Pessoalmente, raramente sinto necessidade de depurador para esse fim (pacote de depuração ao carregar)

EDITAR 2

Em resposta à edição na pergunta:

Em relação à ordem das definições:sim, você pode fazer isso e resolve esse problema específico. Mas, geralmente, isso não é robusto, e eu não o consideraria um bom método geral. É difícil dar um conselho definitivo para um caso em questão, pois está um pouco fora de seu contexto, mas me parece que o uso de UpValues aqui é injustificável. Se isso for feito para tratamento de erros, há outras maneiras para fazer isso sem usar UpValues .

Geralmente, UpValues são usados ​​mais comumente para sobrecarregar alguma função de forma segura, sem adicionar nenhuma regra à função que está sendo sobrecarregada. Um conselho é evitar associar UpValues com cabeças que também têm DownValues e pode avaliar - fazendo isso você começa a jogar um jogo com avaliador, e eventualmente perderá. O mais seguro é anexar UpValues a símbolos inertes (cabeças, contêineres), que geralmente representam um "tipo" de objetos nos quais você deseja sobrecarregar uma determinada função.

Em relação ao meu comentário sobre a presença de HoldPattern indicando um projeto ruim. Certamente usos legítimos para HoldPattern , como este (um pouco artificial):
In[25]:= 
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]

Out[27]= {a,b,c} 

Aqui se justifica porque em muitos casos Plus permanece não avaliado, e é útil em sua forma não avaliada - pois pode-se deduzir que ele representa uma soma. Precisamos de HoldPattern aqui por causa do modo Plus é definido em um único argumento e porque um padrão é um único argumento (mesmo que descreva geralmente vários argumentos) durante a definição. Então, usamos HoldPattern aqui para evitar tratar o padrão como argumento normal, mas isso é principalmente diferente dos casos de uso pretendidos para Plus . Sempre que esse for o caso (temos certeza de que a definição funcionará corretamente para os casos de uso pretendidos), HoldPattern está bem. Observe b.t.w., que este exemplo também é frágil:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]

A razão pela qual ainda está tudo bem é que normalmente não usamos Plus em um único argumento.

Mas, há um segundo grupo de casos, onde a estrutura dos argumentos normalmente fornecidos é a mesma que a estrutura dos padrões usados ​​para a definição. Nesse caso, a avaliação do padrão durante a atribuição indica que a mesma avaliação acontecerá com os argumentos reais durante as chamadas da função. Seu uso se enquadra nesta categoria. Meu comentário para uma falha de design foi para esses casos - você pode impedir que o padrão seja avaliado, mas terá que impedir que os argumentos sejam avaliados também, para que isso funcione. E a correspondência de padrões com expressões não completamente avaliadas é frágil. Além disso, a função nunca deve assumir algumas condições extras (além do que ela pode verificar) para os argumentos.