61
$\begingroup$

I need to use If but with only one option that is if "a" then do "b", else do nothing. So I wrote If[a,b] but the problem is that if it is not a it returns Null in my output. How to avoid this?

Here is the specific example I was working on.

I am looking for the number of comparisons performed by the quick sort algorithm. Here is the code from Rosetta code with my additions

QuickSort[x_List] := 
 Module[{pivot, aa = 0, bb = 0}, If[Length@x <= 1, Return[x]];
  pivot = First[x];
  aa = If [Length[Cases[x, j_ /; j < pivot]] > 1, 
    Length[Cases[x, j_ /; j < pivot]] - 1 , Sequence[]];
  bb = If [Length[Cases[x, j_ /; j < pivot]] > 1, 
    Length[Cases[x, j_ /; j > pivot]] - 1 , Sequence[]]; 
  count = count + aa + bb;
  Flatten@{QuickSort[Cases[x, j_ /; j < pivot]], 
    Cases[x, j_ /; j == pivot], 
    QuickSort[Cases[x, j_ /; j > pivot]]} ; Return[count] ]

Now if you run QuickSort[{4, 3, 2, 1, 5}] you will get 2+2 Null instead of 4.

$\endgroup$
2
  • 3
    $\begingroup$ What is the problem with that, returning Null is usually no problem, the FrontEnd will usually not even create an output cell for that return value... $\endgroup$ Commented Mar 30, 2012 at 8:43
  • $\begingroup$ ok i my case i could just add 0, in the "else" case. but i wonder why proposal of "kguler" and "ruebenko" that i also tested didn't work in my case $\endgroup$
    – Raph
    Commented Mar 30, 2012 at 9:33

8 Answers 8

33
$\begingroup$

It depends what you consider nothing, but you could try something like this

If[a, b, Unevaluated[Sequence[]]]

for example

3 + If[False, 1, Unevaluated[Sequence[]]]

returns 3. Wrapping an argument of a function in Unevaluated is effectively the same as temporarily setting the attribute Hold for that argument meaning that the argument isn't evaluated until after it's inserted in the definition of that function.

By the way, in your definition of QuickSort you're calling Cases[x, j_ /; j < pivot] six times. It's probably more efficient to assign Cases[x, j_ /; j < pivot] to a dummy variable and use that instead.

$\endgroup$
4
  • $\begingroup$ Cases[x, j_ /; j < pivot] 4 times, Cases[x, j_ /; j == pivot] and Cases[x, j_ /; j > pivot] each once. But, a variable is definitely needed. $\endgroup$
    – rcollyer
    Commented Mar 30, 2012 at 11:24
  • $\begingroup$ @rcollyer you're right, I was a bit hasty in scanning the code. $\endgroup$
    – Heike
    Commented Mar 30, 2012 at 11:36
  • $\begingroup$ Unevaluated[] == Unevaluated[Sequence[]]? $\endgroup$
    – chyanog
    Commented Jul 19, 2013 at 6:43
  • 1
    $\begingroup$ @chyanog I believe this is true: one may use Unevaluated[] in any place were Unevaluated[Sequence[]] is used. I did not recommend it because ##&[] is shorter, but it should be understood that ##&[] and Unevaluated[] behave differently, though both are appropriate for this problem. I added a section to my answer that hopefully explains this. $\endgroup$
    – Mr.Wizard
    Commented Aug 13, 2013 at 1:13
63
$\begingroup$

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}, #] &
]

enter image description here

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[];
$\endgroup$
7
  • 1
    $\begingroup$ If you look at it with Trace, you will see that you are just hiding it: If[# > 5, #, ## &[]] & /@ Range[10] // Trace. In have a year you will not remember what your code does. $\endgroup$
    – user21
    Commented Mar 30, 2012 at 10:24
  • 3
    $\begingroup$ @ruebenko I really don't understand what you are trying to say. Using & effectively prevents (holds) evaluation in If. In a year I most certainly will remember what this code does, barring traumatic brain injury. What's your point? $\endgroup$
    – Mr.Wizard
    Commented Mar 30, 2012 at 10:28
  • $\begingroup$ I have the feeling this is in essence replacing the Null with a Sequence; and subjectively makes it harder to read. $\endgroup$
    – user21
    Commented Mar 30, 2012 at 12:24
  • $\begingroup$ @ruebenko re: "in essence replacing the Null with a Sequence" -- isn't that the idea? The OP has If[. . ., Sequence[]] but it undesirably evaluates to If[. . ., Null]. Using ##&[] fixes it. What am I still missing? $\endgroup$
    – Mr.Wizard
    Commented Mar 30, 2012 at 12:50
  • 2
    $\begingroup$ @RunnyKine Thanks! p.s. watch your syntax; it is: ##&[] $\endgroup$
    – Mr.Wizard
    Commented Nov 29, 2013 at 12:39
12
$\begingroup$

I note that all answers so far try to solve the problem of assigning a potential Null value by manipulating the return value. I feel it would be more appropriate to make the whole assignment conditional. Like this:

If[condition, aa = value]

There's also a small bug in your program (count isn't initialized), and, of course, it doesn't sort at the moment. I assume that you aware of that and that the Return value is used for testing.

The code would then be:

QuickSort[x_List] := 
 Module[{pivot, aa = 0, bb = 0, count = 0}, 
  If[Length@x <= 1, Return[x]];
  pivot = First[x];
  If[Length[Cases[x, j_ /; j < pivot]] > 1,
     aa = Length[Cases[x, j_ /; j < pivot]] - 1
  ];
  If[Length[Cases[x, j_ /; j < pivot]] > 1, 
     bb = Length[Cases[x, j_ /; j > pivot]] - 1
  ];
  count = count + aa + bb;
  Flatten@{QuickSort[Cases[x, j_ /; j < pivot]], 
    Cases[x, j_ /; j == pivot], QuickSort[Cases[x, j_ /; j > pivot]]};
   Return[count]] 

Remove ;Return[count] to let it sort again.

I'm not sure about the j < pivot test in the second If. It depends on your intention with bb, but I guess the test should be j > pivot.

$\endgroup$
8
$\begingroup$

I usually use:

a = {1, 2, Null};
a = DeleteCases[a, Null];

May not be efficient, but it works.

$\endgroup$
1
  • 2
    $\begingroup$ Indeed it does work. Also, since Null can be implicit you can use a = DeleteCases[a,] which is rather nice to those of us who like terse code. $\endgroup$
    – Mr.Wizard
    Commented Jul 18, 2013 at 23:46
7
$\begingroup$

In Mathematica, “not returning anything” is not possible. An expression that does not return anything has a value of Null, even though you only actually see this Null in certain circumstances:

Null
    is a symbol used to indicate the absence of an expression or a result. It is not displayed in ordinary output.

When Null appears as a complete output expression, no output is printed.

So, Null is always returned, but whether it's displayed depend on what you do with this returned value. For example:

In[1]:= If[x > 0, 1]; /. x -> -1
(* nothing is output *)
In[2]:= {If[x > 0, 1]; /. x -> -1, 42}
Out[2]= {Null, 42}
$\endgroup$
7
$\begingroup$

The semantics for if is If[cond,t,f] if f (False) is not given Null is returned.

I am guessing that you need this in a pattern, then you can use Condition:

f[x_] := ppp[x] /; x > 0
f[5]
f[-6]

OK, here is a different version of quick sort (I found on my disk - I am not sure how the author is...)

ClearAll[quickSort1]
quickSort1[lst0_] := 
 Block[{left, right, pivot, lst, $RecursionLimit = 10^6},
  pivot = First[lst0];
  lst = Rest[lst0];
  left = Select[lst, # <= pivot &];
  If[left =!= {}, left = quickSort1[left]];
  right = Select[lst, # > pivot &];
  If[right =!= {}, right = quickSort1[right]];
  Join[left, {pivot}, right]
  ]

If someone feels like adding the counter... my brain feels like gum this morning... please go ahead. The point in the above code is to not use the return value of If but to set it to a variable.

$\endgroup$
5
$\begingroup$

Wow, Looks like I'm more than a year late to this party, but I thought this would be nice to post. The Sequence @@ {} trick. Here it is using Mr. Wizard's example:

If[# > 5, #, Sequence @@ {}] & /@ Range[10]

{6, 7, 8, 9, 10}

$\endgroup$
7
  • $\begingroup$ This does work, and was recommended by Leonid at one time but isn't it better to use ##&[] now? Do you prefer Sequence @@ {} or are you just posting it for completeness? $\endgroup$
    – Mr.Wizard
    Commented Sep 21, 2013 at 23:30
  • $\begingroup$ @Mr.Wizard, I didn't know about it until recently I needed to do a time consuming computation and found it in Michael Trott's Programming guidebook. I came across this post but realized it wasn't posted so decided to post it for completeness. I actually liked your ##&[] trick better. $\endgroup$
    – RunnyKine
    Commented Sep 22, 2013 at 1:05
  • $\begingroup$ Okay. Completeness is good, so +1. (I also included this in my answer which I am attempting to make complete itself.) $\endgroup$
    – Mr.Wizard
    Commented Sep 22, 2013 at 1:09
  • $\begingroup$ @Mr.Wizard. Do you know why none of those methods work with Compile? Is there a way to get around it? $\endgroup$
    – RunnyKine
    Commented Sep 22, 2013 at 1:28
  • $\begingroup$ Without trying it I expect that it won't work with Compile because the possible return types must match. A work-around would be to use something like -1 (for a list of otherwise positive integers) or perhaps Developer`$MaxMachineInteger for the "sequence" return and then DeleteCases at the end. This can create edge cases but I don't know of a better way off-hand. $\endgroup$
    – Mr.Wizard
    Commented Sep 22, 2013 at 4:18
2
$\begingroup$

I am well aware that I am reviving an old question here, however I have been using Nothing as the third argument of my If functions and it has been doing a great job for me.

Table[If[Or[i == 3, i == 5], i, Nothing], {i, Range[10]}]
{3, 5}
$\endgroup$
1
  • 2
    $\begingroup$ Nothing only helps with lists: 3 + If[False, 2, Nothing] $\endgroup$
    – Kuba
    Commented Nov 10, 2018 at 12:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.