在不评估保持表达式的情况下从列表中删除空列表的有效方法?

时间:2011-07-10 13:49:37

标签: wolfram-mathematica

previous thread中,建议从列表中删除空列表({})的有效方法:

Replace[expr, x_List :> DeleteCases[x, {}], {0, Infinity}]

使用Trott-Strzebonski in-place evaluation technique这个方法可以推广用于保存表达式:

f1[expr_] := 
 Replace[expr, 
  x_List :> With[{eval = DeleteCases[x, {}]}, eval /; True], {0, Infinity}]

此解决方案比基于ReplaceRepeated的解决方案更有效:

f2[expr_] := expr //. {left___, {}, right___} :> {left, right}

但它有一个缺点:如果它们被List包裹,它会对持有的表达式进行评估:

In[20]:= f1[Hold[{{}, 1 + 1}]]

Out[20]= Hold[{2}]

所以我的问题是:在不评估保持表达式的情况下从列表中删除所有空列表({})的最有效方法是什么?如果空List[]对象是另一个List本身的元素,则应删除


以下是一些时间安排:

In[76]:= expr = Tuples[Tuples[{{}, {}}, 3], 4];
First@Timing[#[expr]] & /@ {f1, f2, f3}
pl = Plot3D[Sin[x y], {x, 0, Pi}, {y, 0, Pi}]; 
First@Timing[#[pl]] & /@ {f1, f2, f3}

Out[77]= {0.581, 0.901, 5.027}

Out[78]= {0.12, 0.21, 0.18}

说明:

Clear[f1, f2, f3];
f3[expr_] := 
  FixedPoint[
   Function[e, Replace[e, {a___, {}, b___} :> {a, b}, {0, Infinity}]], expr];
f1[expr_] := 
  Replace[expr, 
   x_List :> With[{eval = DeleteCases[x, {}]}, eval /; True], {0, Infinity}];
f2[expr_] := expr //. {left___, {}, right___} :> {left, right};

3 个答案:

答案 0 :(得分:3)

怎么样:

Clear[f3];
f3[expr_] := 
 FixedPoint[
  Function[e, 
   Replace[e, {a___, {}, b___} :> {a, b}, {0, Infinity}]],
   expr]

似乎不辜负规格:

In[275]:= f3[{a, {}, {b, {}}, c[d, {}]}]

Out[275]= {a, {b}, c[d, {}]}

In[276]:= f3[Hold[{{}, 1 + 1, {}}]]

Out[276]= Hold[{1 + 1}]

答案 1 :(得分:2)

您可以将您提到的解决方案与最低性能匹配相结合,并使用this帖子中的技术维护未评估的代码,并修改自定义保留包装器将使用{{1}设置为私有}:

Module

上述函数假定输入表达式包含在ClearAll[removeEmptyListsHeld]; removeEmptyListsHeld[expr_Hold] := Module[{myHold}, SetAttributes[myHold, HoldAllComplete]; Replace[MapAll[myHold, expr, Heads -> True], x : myHold[List][___] :> With[{eval = DeleteCases[x, myHold[myHold[List][]]]}, eval /; True], {0, Infinity}]//. myHold[x_] :> x]; 中。例子:

Hold

答案 2 :(得分:1)

我对这个问题有点晚了。 ; - )

虽然相当复杂,但测试速度比f1快一个数量级:

fx[expr_] :=
 Module[{s},
  expr // 
   Quiet[{s} /. {x_} :> ({} /. {x___} -> (# /. {} -> x //. {x ..} -> x) &)]
 ]

它没有评估:

Hold[{{}, 1 + 1}] // fx
Hold[{1 + 1}]

计时

expr = Tuples[Tuples[{{}, {}}, 3], 4];
First @ Timing @ Do[# @ expr, {100}] & /@ {f1, fx}

pl = Plot3D[Sin[x y], {x, 0, Pi}, {y, 0, Pi}];
First @ Timing @ Do[# @ pl, {100}] & /@ {f1, fx}
{10.577, 0.982}  (* 10.8x faster *)

{1.778, 0.266}   (* 6.7x faster  *)

检查

f1@expr === fx@expr
f1@pl   === fx@pl
True

True

解释

此功能的基本版本如下所示:

{} /. {x___} -> (# //. {} | {x ..} -> x) &

我们的想法是首先使用//. {} | {x ..} -> x减少表达式,然后使用带有空表达式的injector pattern删除x的所有实例,就好像它们被{{1}替换一样但没有评估。

第一个变化是通过将替换分成Sequence[]来进行优化。第二个更改是以某种方式在模式中本地化/. {} -> x //. {x ..} -> x,以便在表达式本身出现x时不会失败。由于 Mathematica 处理嵌套作用域构造的方式,我不能简单地使用x,而是必须再次使用注入器模式将唯一符号转换为Module[{x}, . . . ]等,并{ {1}}让它不要抱怨非标准使用。