我想定义一个符号pt
来保持一个点(并最终缓存一些与该点相关的数据):
pt::"usage" = "pt[{x,y}] represents a point at {x,y}";
我希望能够以尽可能多的方式使用这些pt
个对象,特别是,我希望能够写出
{a0,a1}+pt[{b0,b1}]
并让它返回pt[{a0+b0,a1+b1}]
而不是{a0+pt[{b0,b1}],a1+pt[{b0,b1}]}
。
我最初的想法是使用:
pt /: Plus[pt[p0_], p1 : {_, _}] = pt[p0 + p1];
但这不起作用(因为Plus
是Listable
?)。有没有办法在不取消保护Plus
?
更新
正如Leonid所指出的,如果没有全局或本地黑客Plus
,这是不可能的,因为Listable
属性在任何*值之前被考虑。这实际上在evaluation tutorial中非常精确地描述。
答案 0 :(得分:6)
Mathematica的评估员似乎不够灵活,不能轻易做到这一点。用于pt的UpValues确实在DownValues for Plus之前应用,但是由于可列表性而导致列表上的线程甚至在此之前发生。在这种特殊情况下,以下内容可能对您有用:
eval = Function[code,Block[{Plus = Plus, attr = DeleteCases[Attributes[Plus], Listable]},
SetAttributes[Plus, attr]; code], HoldAll]
要使用它,请将其包裹在您希望应用pt规则的代码段中,例如:
eval[{a0, a1} + pt[{b0, b1}]]
你可以使用$ Pre作为$Pre = eval
来避免每次输入eval,尽管通常我不推荐这样做。 Blocking Plus是一种暂时禁用其部分或全部属性的更软方式。优点w.r.t.清除和设置不带阻塞的属性是指即使抛出异常或计算中止,也无法最终处于永久禁用Listable属性的全局状态。
由于Listable属性直接影响评估而不是模式匹配(后者当然可能间接影响,如果某些模式必须匹配Plus线程列表的结果),在大多数情况下这应该是正常的。理论上,在某些情况下,它可能仍然会导致一些不必要的影响,特别是在涉及模式匹配的情况下。但在实践中,它可能已经足够好了。更简洁但更复杂的解决方案是根据您的需求创建定制评估器。
答案 1 :(得分:1)
以下内容有点浪费,但确实有效:我们的目的只是观察Listable
Plus
属性将pt
放入所有元素的情况。列表(即原始点) - 然后将其拉出。首先,定义一个添加pt对象的函数:
SetAttributes[ptPlus, {Orderless}]
ptPlus[pt[pa : {_, _}], pt[pb : {_, _}], r___] :=
ptPlus[pt[pa + pb], r];
ptPlus[p_pt] := p;
然后我们确保涉及Plus
的任何pt
都映射到ptPlus
(与pt关联的规则)。
Plus[h___, a_pt, t___] ^:= ptPlus[h, a, t];
以上规则意味着:{x0,y0}+pt[{x1,y1}]
将从{x0+pt[{x1,y1}],y0+pt[{x1,y1}]}
扩展为{ptPlus[x0,pt[{x1,y1}]],ptPlus[y0,pt[{x1,y1}]]}
。现在我们只是制定一个规则将其转换为pt[{x0,y0}]+pt[{x1,y1}]
(注意检查pt
s相等的延迟条件):
{ptPlus[x__], ptPlus[y__]} ^:= Module[{
ptCases = Cases[{{x}, {y}}, _pt, {2}]},
ptCases[[1]] + pt[Plus @@@ DeleteCases[{{x}, {y}}, _pt, {2}]]
/; Equal @@ ptCases]
更不透明但更谨慎的版本,更容易推广到更高的尺寸:
ptPlus /: p : {_ptPlus, _ptPlus} := Module[{ptCases, rest,
lp = ReleaseHold@Apply[List, Hold[p], {2}]},
ptCases = Cases[lp, _pt, {2}];
rest = Plus @@@ DeleteCases[lp, _pt, {2}];
ptCases[[1]] + pt[rest] /; And[Equal @@ ptCases, VectorQ@rest]]
当{a+pt[{0,0}],a+pt[{0,b}]} /. {a -> pt[{0,0}]}
评估为pt[{0,0}]
c==0
而{pt[{0,0}],pt[{0,c}]}
时,这整个方法当然会导致可怕的微妙错误。