在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};
答案 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}}让它不要抱怨非标准使用。