【发布时间】:2011-07-03 12:36:16
【问题描述】:
从出现在不同级别的表达式中的所有Lists 中删除所有空List[] 对象的最有效方法是什么?只有当空的List[] 是另一个List 本身的元素时,才应将其删除。
【问题讨论】:
从出现在不同级别的表达式中的所有Lists 中删除所有空List[] 对象的最有效方法是什么?只有当空的List[] 是另一个List 本身的元素时,才应将其删除。
【问题讨论】:
Andrew 和 Alexey 指出,使用 expr //. x_List :> DeleteCases[x, {}, Infinity] 在我之前的回答中也会删除 {} 中的 blah[{f[{}]}],而它应该保持不变,因为它的头部是 f,而不是 @987654331 @。多亏了 Leonid,解决方案是不使用ReplaceRepeated,而是使用Replace,而是在从0 到Infinity 的所有级别进行替换:
Replace[expr, x_List :> DeleteCases[x, {}], {0, Infinity}]
从这个小例子中可以看出Replace 有效而ReplaceRepeated 无效的原因。在其TreeForm 中考虑expr = {a, {}, {b, {}}, c[d, {}]};
Replace 首先从最里面的表达式开始工作,即List[b,{}] 和c[d,{}],然后向上工作到顶部节点。在每个级别,检查头部就像查看正上方的节点并查看它是否匹配List 一样简单。如果是,则应用规则并向上移动一个级别,否则什么都不做并向上移动一个级别。这会产生最终的树:
ReplaceRepeated(另一方面,//.) 从最顶层的节点开始向下遍历树。之前的解决方案首先检查第一个节点是否为List,如果是,然后DeleteCases被应用,它沿着树向下移动,无情地替换它可以找到的每个{}。注意它不检查内部表达式的头部是否也匹配List,因为这个遍历是由@987654351完成的@,而不是ReplaceRepeated。当//.移动到后续的较低节点时,没有什么可以替换的,它很快就退出了。这是用之前的解决方案得到的树:
请注意,c[d, {}] 中的 {} 也已被删除。这完全是因为DeleteCases(使用级别规范{0,Infinity} 向下移动了树。事实上,如果第一个头不是List,它会跳过它并移动到下一个级别,其中只有{b, {}}中的{}是匹配的。用expr2 = f[a, {}, {b, {}}, c[d, {}]]演示,我们得到
请注意,在当前使用Replace 的解决方案中,我们将DeleteCases 与默认级别规范一起使用,这只是第一级。因此,它不会检查和删除比第一层更深的空列表,这正是我们这里需要的。
虽然我们使用第一个节点来解释它失败的原因,但推理对每个节点都适用。 Leonid 在his book
中更详细地解释了这些概念【讨论】:
blah[a, b, {{{}}}, d, {e, f, {}, g}] //. x_List :> DeleteCases[x, {}] 上无法正常工作==> blah[a, b, {{{}}}, d, {e, f, g}]。将Infinity 添加到DeleteCases。
Replace:Replace[expr, x_List :> DeleteCases[x, {}], {0, Infinity}]。由于Replace 从下到上(深度优先),与ReplaceRepeated 不同,问题自然得到解决。然而,它比DeleteCases[expr,{}] 版本慢得多。作为副产品,您不需要重复的规则应用程序。我在这里讨论了Replace 和ReplaceRepeated 之间的区别:mathprogramming-intro.org/book/node218.html。这似乎是一个很好的问题来说明它。
{{{}}} 或blah[a, b, {{{}}}, d, {e, f, {}, g}]。问题是,如果没有进行评估(这些测试用例就是这种情况),Replace 引起的子评估会恢复 Unevaluated 包装器。因此,对于惰性表达式,Unevaluated 不会给你买任何东西,只会破坏东西。如果你想对一段可能求值的代码执行列表删除操作,这是一个单独的问题。