非标准评估和PackedArray

Yar*_*tov 5 wolfram-mathematica

我刚才如何使allTrue[{x,list},test]功能保护的占位符号x从评估当前环境相同的方式为Table[expr,{x,...}]保护x

我最终使用的配方间歇性失败,我发现问题是由列表自动转换为PackedArrays引起的.这是一个失败的例子

SetAttributes[allTrue, HoldAll];
allTrue[{var_, lis_}, expr_] := 
  LengthWhile[lis, 
    TrueQ[ReleaseHold[Hold[expr] /. HoldPattern[var] -> #]] &] == 
   Length[lis];
allTrue[{y, Developer`ToPackedArray[{1, 1, 1}]}, y > 0]
Run Code Online (Sandbox Code Playgroud)

无论是否自动转换为什么,我想allTrue[{x,{1,2,3}},x>0]返回,实现它的更好方法是什么?True{1,2,3}PackedArray

Leo*_*rin 7

这是我已经使用了很长一段时间的版本(最初为我的书的第二版写了它,但我最终使用了它很多).如果参数代表一些未评估的代码,那么如果我们想要一个代表一个特定子句的单个代码以未评估的形式传递给它(这可能是也可能不是可取的),那么测试函数必须具有 HoldAllHoldFirst属性.

ClearAll[fastOr];
Attributes[fastOr] = {HoldRest};
fastOr[test_, {args___}] := fastOr[test, args];
fastOr[test_, args___] :=
TrueQ[Scan[
        Function[arg, If[test[arg], Return[True]], HoldAll],
        Hold[args]]];
Run Code Online (Sandbox Code Playgroud)

编辑:我刚刚注意到Daniel Reeves在问题中链接的页面底部的解决方案与此非常相似.主要区别在于我关心短路和保持参数未被评估(见下文),而丹尼尔只关注短路部分.

它确实有短路行为.我们需要HoldRest属性,因为我们希望以未评估的形式保存参数.我们还需要pure函数中的HoldAll(或HoldFirst)属性来保留每个未经评估的参数,直到它被传递给它test.在它被用于体内之前是否进行评估test现在取决于它的属性test.举个例子:

Clear[fullSquareQ];
fullSquareQ[x_Integer] := IntegerQ[Sqrt[x]];

In[13]:= Or @@ Map[fullSquareQ, Range[50000]] // Timing

Out[13]= {0.594, True}

In[14]:= fastOr[fullSquareQ, Evaluate[Range[10000]]] // Timing

Out[14]= {0., True}
Run Code Online (Sandbox Code Playgroud)

这是一个例子,我们将一些代码片段作为参数传递,从而产生副作用(打印).最后一个参数的代码没有机会执行,因为结果已在前一个子句中确定:

In[15]:= fastOr[# &, Print["*"]; False, Print["**"]; False, 
  Print["***"]; True, Print["****"]; False]

During evaluation of In[15]:= *

During evaluation of In[15]:= **

During evaluation of In[15]:= ***

Out[15]= True
Run Code Online (Sandbox Code Playgroud)

请注意,由于fastOr接受一般未经评估的代码段作为子句Or,Evaluate如果您不关心它们将在开始时进行评估,则必须将值列表包装起来(如上Range例所示).

最后,我将说明所持代码的程序化构造fastOr,以展示如何使用它(如果你愿意的话,可以考虑使用保存的表达式的小型速成课程).使用保持的表达式时,以下函数非常有用:

joinHeld[a___Hold] := Hold @@ Replace[Hold[a], Hold[x___] :> Sequence[x], {1}];
Run Code Online (Sandbox Code Playgroud)

例:

In[26]:= joinHeld[Hold[Print[1]], Hold[Print[2], Print[3]], Hold[], Hold[Print[4]]]

Out[26]= Hold[Print[1], Print[2], Print[3], Print[4]]
Run Code Online (Sandbox Code Playgroud)

以下是我们如何使用它以编程方式构造上面使用Print-s的示例中使用的保持参数:

In[27]:= 
held = joinHeld @@ MapThread[Hold[Print[#]; #2] &, 
      {NestList[# <> "*" &, "*", 3], {False, False, True, False}}]

Out[27]= Hold[Print["*"]; False, Print["**"]; False, Print["***"]; True, Print["****"]; False]
Run Code Online (Sandbox Code Playgroud)

为了传递它fastOr,我们将使用另一个有用的习惯用法:append(或prepend)Hold[args]直到我们获得所有函数参数,然后使用Apply(注意,一般来说,如果我们不想要我们附加/前置的部分来评估,我们必须把它包装好Unevaluated,所以一般成语看起来像Append[Hold[parts___],Unevaluated[newpart]]):

In[28]:= fastOr @@ Prepend[held, # &]

During evaluation of In[28]:= *

During evaluation of In[28]:= **

During evaluation of In[28]:= ***

Out[28]= True
Run Code Online (Sandbox Code Playgroud)

关于你所提到的原始实现,你可以看一下我之前做的评论.问题是TakeWhile和LengthWhile在v.8.0.0中有打包数组的错误,它们在8.0.1的源代码中被修复 - 因此,从8.0.1开始,你可以使用我的或者Michael的版本.

HTH

编辑:

我刚才注意到,在你提到的帖子中,你想要一个不同的语法.虽然采用这种方法所采用的方法并不是很困难fastOr,但这里有一个不同的实现,可以说与这种特定语法的现有语言结构更为一致.我建议使用Table和异常,因为迭代器Table接受你想要的相同语法.这里是:

ClearAll[AnyTrue, AllTrue];
SetAttributes[{AnyTrue, AllTrue}, HoldAll];
Module[{exany, exall},
 AnyTrue[iter : {var_Symbol, lis_List}, expr_] :=
    TrueQ[Catch[Table[If[TrueQ[expr], Throw[True, exany]], iter], exany]];
 AllTrue[iter : {var_Symbol, lis_List}, expr_] :=
   Catch[Table[If[! TrueQ[expr], Throw[False, exall]], iter], exall] =!= False;
];
Run Code Online (Sandbox Code Playgroud)

几句解释:我在顶层使用Module,因为我们需要定义一次自定义异常标记,也可以在定义时执行.突破表的方法是通过例外.不是很优雅并且会引起小的性能损失,但我们购买迭代器变量的自动动态本地化Table,以及简单性.要以安全的方式执行此操作,我们必须使用唯一标记标记异常,因此我们不会错误地捕获其他异常.我发现使用Module来创建持久异常标记通常是一个非常有用的技巧.现在,一些例子:

In[40]:= i = 1

Out[40]= 1

In[41]:= AnyTrue[{i, {1, 2, 3, 4, 5}}, i > 3]

Out[41]= True

In[42]:= AnyTrue[{i, {1, 2, 3, 4, 5}}, i > 6]

Out[42]= False

In[43]:= AllTrue[{i, {1, 2, 3, 4, 5}}, i > 3]

Out[43]= False

In[44]:= AllTrue[{i, {1, 2, 3, 4, 5}}, i < 6]

Out[44]= True

In[45]:= AllTrue[{a, {1, 3, 5}}, AnyTrue[{b, {2, 4, 5}}, EvenQ[a + b]]]

Out[45]= True

In[46]:= AnyTrue[{a, {1, 3, 5}}, AllTrue[{b, {2, 4, 5}}, EvenQ[a + b]]]

Out[46]= False
Run Code Online (Sandbox Code Playgroud)

我从一个赋值开始,i以显示迭代器变量的可能全局值无关紧要 - 这是由它来处理的Table.最后,注意(如我评论其他地方),你的亲笔签名AllTrueAnyTrue有点过于严格,在这个意义上,下面不工作:

In[47]:= lst = Range[5];
AllTrue[{i, lst}, i > 3]

Out[48]= AllTrue[{i, lst}, i > 3] 
Run Code Online (Sandbox Code Playgroud)

(因为lst由于HoldAll属性,在模式匹配时不知道表示列表的事实).没有充分的理由保持这种行为,因此您可以删除_List检查:AnyTrue[iter : {var_Symbol, lis_}, expr_]类似地AllTrue,并且将涵盖此类用例.