14
$\begingroup$

Recently I had the need to redefine a certain symbol in my init.m so it would be automatically omitted from any lists it appears in. I decided to redefine it to an empty sequence, e.g. Locked = Sequence[], but that got me thinking. What if I wanted to return a sequence (not necessarily an empty one) in a := definition? Return doesn't have the SequenceHold attribute, and adding it in a package might cause problems, so what would I do?

EDIT: I think I've figured out what exactly causes me to have the problem. I've defined it to display a Message first to let me know whenever a package I'm importing attempts to "attack my computer". (It is trying to cause my computer to behave in a manner not consistent with my wishes, after all.) So I defined it as Locked := (Message[Locked::nope]; Sequence[]), but strangely it just returns Null. (It doesn't show a return value, but if I do {Locked}, it returns {Null}, and if I try to set it as an attribute it says that Null is not a valid attribute and doesn't set any of them.)

$\endgroup$
5
  • 4
    $\begingroup$ Locked is itself Locked in version 9, precisely to prevent people doing what you are trying to do here, which is the oldest trick in the book. ;) About the question, though: Return is rarely needed and you can anyway return results wrapped in any head that is either SequenceHold or HoldAllComplete to achieve what you want. I'd suggest Unevaluated as a reasonable choice, useful also for its habit of disappearing. $\endgroup$ Feb 3, 2013 at 7:39
  • 1
    $\begingroup$ Yes, I know Locked is locked in Mathematica 9, which is precisely why I keep a copy of Mathematica 8 installed on one computer. I believe I've tried using Unevaluated. Do you mean something like Return[Unevaluated[Sequence[]]]? If not, could you please give an example of what you do mean? By the way, take a guess what I used to prevent packages from erasing my definition of Locked. ;-) $\endgroup$
    – Sparkette
    Feb 3, 2013 at 10:24
  • 1
    $\begingroup$ I mean, look at the answers below--nobody is using Return. It's really only necessary when you want to bail out of a construct like While that ordinarily wouldn't have any return value, or which wouldn't terminate except in the case of a manual return. If you encounter such a situation then Return@Unevaluated@Sequence[...] is indeed fine (though you might need the second argument of Return). Otherwise, just use Unevaluated@Sequence[...] in place of the usual return value (typically the last position of constructs such as CompoundExpression or Module). $\endgroup$ Feb 4, 2013 at 2:37
  • 2
    $\begingroup$ Re: your edit... what's the problem with Unevaluated@Sequence[] in this case? Incidentally if you consider the use of Locked to be malicious then I might suggest that the best course of action is not to use such packages in the first place. We have discussed this issue here before and the community concensus was that it harms us all to openly discuss how to circumvent Locked or Encode. Given the way this discussion is going, I would suggest removing that context or otherwise the question is likely to be deleted. $\endgroup$ Feb 4, 2013 at 15:17
  • $\begingroup$ I'm not sure what you are trying to accomplish with Locked := (Message[Locked::nope]; Sequence[]), as as OleksandrR. mentioned, Locked is Locked, but note that in your code Sequence[] occurs inside CompoundExpression, which does not have SequenceHold, so it is immediately removed, which is probably also not what you intended. $\endgroup$ Dec 13, 2015 at 21:17

4 Answers 4

8
$\begingroup$

Calculate the List of results you wish to return and use Apply to replace the head:

listFn[a_, b___] := If[a > 0, {b}, {0}];
seqFn[args___] := Sequence @@ listFn[args];
f[1, seqFn[2, 3, 4, 5], 6]
f[1, seqFn[-2, 3, 4, 5], 6]

(*--> f[1, 3, 4, 5, 6] *)
(*--> f[1, 0, 6] *)

Here listFn represents the calculation of the results and does not need to be a separate function. The particular example above can be written more simply as

seqFn[a_, b___] := Sequence @@ If[a > 0, {b}, {0}]

Edit

It should be stressed that the method assumes listFn actually evaluates to the List of desired inputs; if not, the Head of the expression returned will be replaced with Sequence, perhaps with undesired results. In such a case, a method such as @Jens's may be used.

For instance if a is non-numeric, say a Symbol, in the example above, then you get

f[1, seqFn[x, 3, 4, 5], 6]
(*-> f[1, x > 0, {3, 4, 5}, {0}, 6] *)

Often one can construct a List of the actual results, though. (With thanks to @Mr.Wizard.)

$\endgroup$
7
  • $\begingroup$ That's the easiest way, I think. $\endgroup$
    – Jens
    Feb 3, 2013 at 16:30
  • $\begingroup$ This worked great for me. Thanks a lot! Accepted. $\endgroup$
    – Sparkette
    Feb 4, 2013 at 6:15
  • 1
    $\begingroup$ There is a problem with this method. Apply (@@) is used whether or not If evaluates, therefore if you enter x =.; seqFn[x, 1, 2, 3] you get Sequence[x > 0, {1, 2, 3}, {0}] which is wrong and confusing. You could force If to evaluate using TrueQ or the fourth argument but it would often be better if delayed evaluation was possible, as is the case with many built-in functions. To that end it's better and shorter to write: seqFn[a_, b___] := If[a > 0, ## &[b], {0}]. Now seqFn[x, 1, 2, 3] returns If[x > 0, (##1 &)[1, 2, 3], {0}] which is correct if x acquires a value later. $\endgroup$
    – Mr.Wizard
    Feb 4, 2013 at 7:42
  • $\begingroup$ @flarn See my caveat above before you put this into use. $\endgroup$
    – Mr.Wizard
    Feb 4, 2013 at 7:43
  • $\begingroup$ @Mr.Wizard Good point. I should probably stress in the answer that one has to have actually calculated the List. See edit. $\endgroup$
    – Michael E2
    Feb 4, 2013 at 13:17
12
$\begingroup$

Here is one idea:

Clear[sf, mySequence]

sf[x_] := If[x > 0, mySequence[8, 9], 0]

mySequence /: (h : Except[If])[x___, mySequence[y___], 
  z___] := h[x, y, z]

f[1, 2, sf[1], 4]

(* ==> f[1, 2, 8, 9, 4] *)

So I defined a sf function that returns the sequence as the result of an If statement. This is just an example, illustrating the general scenario that the result will be returned by some evaluation of the form returningHead[...,mySequence,...].

What gets returned is not a Sequence but instead an inert wrapper mySequence, which will be converted into a Sequence by the definition following mySequence /: - the only restriction being that I don't want it to be converted when it's in the last expression of my function, whose head was returningHead (specifically If in this example). In all other cases, mySequence will be converted to Sequence, as is illustrated by the last line.

Edit

The advantage of this wrapper approach combined with TagSetDelayed (the /: definition) is that you then have more fine-grained control over when mySequence gets delivered as a sequence. For example, you may be calling sf from inside a List as in the above example, but sometimes from inside a function f2 that can't deal with sequences. Then you can add f2 to the list in the Except statement to avoid errors in the processing of sf, until its output gets placed into the correct context.

$\endgroup$
5
  • $\begingroup$ Cool, that's a good idea. I'm just surprised it doesn't have a much simpler method built in. Thanks! :-) $\endgroup$
    – Sparkette
    Feb 3, 2013 at 7:32
  • $\begingroup$ Maybe there is a simpler method - you may want to wait before accepting this answer to give others some motivation to chime in... $\endgroup$
    – Jens
    Feb 3, 2013 at 7:33
  • $\begingroup$ Okay, I unaccepted it. Sorry, but you did basically ask me to. Of course, if nobody else posts a simpler method I'll be sure to mark it as accepted again. $\endgroup$
    – Sparkette
    Feb 3, 2013 at 7:34
  • $\begingroup$ That's perfectly fine! $\endgroup$
    – Jens
    Feb 3, 2013 at 7:35
  • $\begingroup$ I have sometimes needed that sort of control. Thanks for the edit. $\endgroup$
    – Michael E2
    Feb 3, 2013 at 18:36
6
$\begingroup$

Maybe I've missed the point, but a sequence in most ways is just another Mathematica expression, so consider just returning a sequence.

f[args___] := args
g[x, f[a, b, c], y]

g[x, a, b, c, y]

g[x, f[], y]

g[x, y]

Update

I have edited this answer to incorporate Mr. Wizard's observation that args in f[args___] := args is already a sequence and doesn't need to be wrapped with Sequence.

$\endgroup$
7
  • 1
    $\begingroup$ This can be written more simply: f[args___] := args. Sequence is automatically applied to the raw sequence args if no surrounding head is found. The advantage of Jens' method over either of these is in the case of SetAttributes[g, SequenceHold]. $\endgroup$
    – Mr.Wizard
    Feb 3, 2013 at 14:54
  • $\begingroup$ @Mr.Wizard this seems more native to me than Jens's, and I vote this as the obvious #1 answer. That advantage doesn't seem big to me given that symbols with SequenceHold probably have that attribute for a reason. Furthermore, it could be in some cases less than the disadvantage of giving other upvalues the chance of "winning". Finally, there's also the possibility to do what Jens did but straight with Sequence, say Sequence /: (h : Except[If])[b___, HoldPattern@HoldPattern@Sequence[m___], a___] := h[b, m, a] $\endgroup$
    – Rojo
    Feb 4, 2013 at 17:30
  • $\begingroup$ @Rojo I like this one too but I have been holding my vote hoping that Mr. Goldberg would amend his answer to use or at least include the shorter syntax I proposed. :^) $\endgroup$
    – Mr.Wizard
    Feb 6, 2013 at 3:31
  • $\begingroup$ @Mr.Wizard. I made the change. I would have done it before except that I felt your comment counted as change :-) $\endgroup$
    – m_goldberg
    Feb 6, 2013 at 5:32
  • 1
    $\begingroup$ The change that you made is not what @Mr.Wizard meant mainly, hehe $\endgroup$
    – Rojo
    Feb 6, 2013 at 9:30
3
$\begingroup$

I hardly ever use this kind of stuff anymore, but here are some ideas

SetDelayed

To return a sequence using a function defined by SetDelayed, simply try

seqFu[] := Sequence[]

Or even

Clear[seqFu]
seqFu[args___] := Sequence[args];

There is a trap that you should be wary of here. The following may seem to work as expected

seqFu2[args___]:= Unevaluated[args]

seqFu2[1,2]
Sequence[1,2]

but the following does not produce a sequence

seqFu2[1]
1 

Other

You can also create sequences on the fly. Sometimes Unevaluated is useful for this. For example, you can do

CompoundExpression[args, Unevaluated[Sequence[]]]
Sequence[]

Or

Identity[Unevaluated[Sequence[]]]
Sequence[]
$\endgroup$

Your Answer

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

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