If
does not have the attribute SequenceHold
; use the "vanishing function" ##&[]
instead of Sequence[]
:
If[# > 5, #, ## &[]] & /@ Range[10]
{6, 7, 8, 9, 10}
See this and this for other uses, and SlotSequence if you are confused by ##
.
To further explain the method above, Sequence
is flattened when it appears as an argument (level one) in an expression with a head that does not have either the SequenceHold
or HoldAllComplete
attributes.
For our purpose in If
one can use any expression which:
- is held unevaluated by
Hold
(More specifically the HoldRest
attribute.)
- does not have the head
Sequence
- evaluates to
Sequence[]
All of these heads meet this requirement:
## &
Unevaluated
- Symbol (e.g.
seq = Sequence[]
)
Apply
(e.g. Sequence @@ {}
)
Observe:
seq = Sequence[8, 9];
h = Hold[1, Sequence[2, 3], ## &[4, 5], Unevaluated[6, 7], seq, Sequence @@ {10, 11}]
Hold[1, 2, 3, (##1 &)[4, 5], Unevaluated[6, 7], seq, Sequence @@ {10, 11}]
Note that Sequence[2, 3]
is flattened while all others remain. An application of List
will invoke additional evaluation:
List @@ h
{1, 2, 3, 4, 5, Unevaluated[6, 7], 8, 9, 10, 11}
One sees that Unevaluated[6, 7]
remains, which is an important distinction, but this is acknowledged behavior* for inert functions like List
and it will evaluate in other applications:
Plus @@ h == Plus @@ Range[11]
True
*Working with Unevaluated Expressions - Robby Villegas
Note: You may notice red highlighting in the Front End in the expression Unevaluated[6, 7]
; the multi-argument (and null-argument) form of Unevaluated
is not documented but it is used internally by Mathematica. You can adjust the highlighting to match by setting:
Unprotect[Unevaluated];
SyntaxInformation[Unevaluated] = {"ArgumentsPattern" -> {___}};
Protect[Unevaluated];
Version 10.2
Version 10.2 introduces the function Nothing
which is applicable to a subset of this problem. Specifically Nothing
vanishes only if the surrounding head is List
or if it is a non-delayed Value within an Association
. Therefore one can write:
If[# > 5, #, Nothing] & /@ Range[10]
{6, 7, 8, 9, 10}
However it will remain in the output of:
If[OddQ[#], #, Nothing] & /@ foo[1, 2, 3, 4]
foo[1, Nothing, 3, Nothing]
Performance
Oleksandr expressed concern about the performance of ## &[]
:
I am not extremely keen on ##&[]
. It is concise (and I think clear enough after one understands it), but Function
invocation is not the fastest operation in Mathematica. In most cases (except where the subtle differences actually matter), I would prefer Unevaluated@Sequence[]
for explicitness and avoiding an unnecessary function call. Simon Woods's suggestion of Unevaluated[]
could be the best compromise.
Here is a benchmark in 10.0.2 comparing the various methods.
SetAttributes[test, HoldFirst]
test[form_][list_] := If[#, 1, form] & /@ list
seq = Sequence[];
Needs["GeneralUtilities`"]
BenchmarkPlot[
{
test[Unevaluated @ Sequence[]],
test[Sequence @@ {}],
test[seq],
test[## &[]],
test[Unevaluated[]]
},
RandomChoice[{True, False}, #] &
]
In this log-log scale the methods are so close that it is hard to distinguish them. Here is another presentation using the average of five runs:
rand = RandomChoice[{True, False}, 1*^6];
Mean @ Table[First @ Timing @ # @ rand, {5}] & /@
{
test[Unevaluated @ Sequence[]],
test[Sequence @@ {}],
test[seq],
test[## &[]],
test[Unevaluated[]]
}
{0.430563, 0.564724, 0.377522, 0.508563, 0.514803}
At least on my system ## &[]
appears to be slightly faster than Unevaluated[]
contrary to Oleksandr's assertion. Both are a bit slower than Unevaluated @ Sequence[]
because of an additional transformation step. (##&[]
and Unevaluated[]
both evaluate to Sequence[]
.) The assignment method (seq = Sequence[]
) is the fastest. However this Function
is extremely simple; in any nontrivial use the overhead of ## &[]
is likely insignificant.
If the slight overhead or cryptic nature of ## &[]
bothers you I propose using:
vanish[] = Sequence[];
Null
is usually no problem, the FrontEnd will usually not even create an output cell for that return value... $\endgroup$