Mathematica:覆盖`Plus`的`Listable`属性

Jan*_*nus 4 wolfram-mathematica

我想定义一个符号pt来保持一个点(并最终缓存一些与该点相关的数据):

pt::"usage" = "pt[{x,y}] represents a point at {x,y}";
Run Code Online (Sandbox Code Playgroud)

我希望能够pt尽可能多地使用这些对象作为点,特别是,我希望能够写出来

{a0,a1}+pt[{b0,b1}]
Run Code Online (Sandbox Code Playgroud)

并让它返回pt[{a0+b0,a1+b1}]而不是{a0+pt[{b0,b1}],a1+pt[{b0,b1}]}.我最初的想法是使用:

pt /: Plus[pt[p0_], p1 : {_, _}] = pt[p0 + p1];
Run Code Online (Sandbox Code Playgroud)

但这不起作用(因为PlusListable?).有没有办法在没有取消保护的情况下做到这一点Plus

更新: 正如Leonid所指出的,如果没有全局或本地黑客攻击,这是不可能的Plus,因为Listable在任何*值之前都会考虑该属性.这在评估教程中实际上非常精确地描述.

Leo*_*rin 6

Mathematica的评估员似乎不够灵活,不能轻易做到这一点.用于pt的UpValues确实在DownValues for Plus之前应用,但是由于可列表性而导致列表上的线程甚至在此之前发生.在这种特殊情况下,以下内容可能对您有用:

eval = Function[code,Block[{Plus = Plus, attr = DeleteCases[Attributes[Plus], Listable]},
SetAttributes[Plus, attr]; code], HoldAll]
Run Code Online (Sandbox Code Playgroud)

要使用它,请将其包装在您希望应用pt规则的代码段中,例如:

eval[{a0, a1} + pt[{b0, b1}]]
Run Code Online (Sandbox Code Playgroud)

您可以使用$ Pre作为$Pre = eval避免每次输入eval,尽管通常我不建议这样做.Blocking Plus是一种暂时禁用其部分或全部属性的更软方式.清除和设置没有阻止的属性的优点是,即使抛出异常或计算中止,您也无法最终处于永久禁用Listable属性的全局状态.

由于Listable属性直接影响评估而不是模式匹配(后者当然可能间接影响,如果某些模式必须匹配Plus线程列表的结果),在大多数情况下这应该是正常的.理论上,在某些情况下,它可能仍然会导致一些不必要的影响,特别是在涉及模式匹配的情况下.但在实践中,它可能已经足够好了.更简洁但更复杂的解决方案是根据您的需求创建定制评估器.