Эндрю и Алексей указывают, что использование expr //. x_List :> DeleteCases[x, {}, Infinity]
, как было в моем предыдущем ответе, также удалит {}
в blah[{f[{}]}]
, тогда как он должен оставить его нетронутым, поскольку его голова f
, а не List
.Решение, благодаря Леониду, состоит в том, чтобы не использовать ReplaceRepeated
, а Replace
вместо того, чтобы производить замены на всех уровнях от 0
до Infinity
:
Replace[expr, x_List :> DeleteCases[x, {}], {0, Infinity}]
Причинапочему Replace
работает, а ReplaceRepeated
- не видно из этого небольшого примера.Рассмотрим expr = {a, {}, {b, {}}, c[d, {}]};
в его TreeForm
![enter image description here](https://i.stack.imgur.com/W4qP2.png)
Replace
работах, начиная сначала с самых внутренних выражений, т. Е. List[b,{}]
и c[d,{}]
, иработает вверх до верхнего узла.На каждом уровне проверить голову так же просто, как посмотреть вверх на узел прямо вверху и посмотреть, соответствует ли он List
.Если это так, примените правило и поднимитесь на уровень, иначе ничего не делайте и поднимитесь на уровень.В результате получается окончательное дерево:
![enter image description here](https://i.stack.imgur.com/FiGfG.png)
ReplaceRepeated
(//.)
, с другой стороны, работает, начиная с самого верхнего узла и проходя вниз по дереву.предыдущее решение начинается с проверки, является ли первый узел List
, а если да, то применяется DeleteCases
, и оно перемещается вниз по дереву, беспощадно заменяя все найденные {}
. Обратите внимание, что оно не проверяет, является лиглавы внутренних выражений также совпадают с List
, потому что этот обход выполняется DeleteCases
, а не ReplaceRepeated
. Когда //.
перемещается к последующим нижним узлам, заменять больше нечего, и он быстро завершается.дерево, которое можно получить с помощью предыдущего решения:
![enter image description here](https://i.stack.imgur.com/d5bkm.png)
Обратите внимание, что {}
внутри c[d, {}]
также был удален. Это связано исключительно с тем, что DeleteCases
(с указанием уровня {0,Infinity}
перемещается вниз по дереву. Действительно, если бы первая голова была чем-то отличным от List
, она пропустила бы ее и перешла на следующий уровень, из которого только {}
в {b, {}}
соответствует.с помощью expr2 = f[a, {}, {b, {}}, c[d, {}]]
получаем
![enter image description here](https://i.stack.imgur.com/JkVJY.png)
Обратите внимание, что в текущем решении с Replace
мы используем DeleteCases
со спецификацией уровня по умолчанию, которая является первым уровнемтолько.Поэтому он не проверяет и не удаляет пустые списки глубже, чем на первом уровне, а это именно то, что нам здесь нужно.
Несмотря на то, что мы использовали первый узел, чтобы объяснить, почему он не работает, рассуждение справедливо длякаждый узел.Леонид объясняет эти понятия более подробно в своей книге