20
$\begingroup$

I thought that assigning Sequence[] to a part of an expression is an effective way to remove it. For example, the following code yields {2,3,4,5}:

xx = Range[5]; xx[[1]] = Sequence[]; xx

Thus, I expected the output of the following code to be {3,4,5}; but, I get {2,3,4,5} instead:

xx = Range[5]; xx[[1]] = Sequence[]; xx[[1]] = Sequence[]; xx
$\endgroup$
10
  • $\begingroup$ Take a look at xx = Range[5]; xx[[1]] = Sequence[]; OwnValues@xx $\endgroup$
    – Kuba
    Commented Jun 10, 2016 at 6:30
  • $\begingroup$ @Kuba: That is very strange. It contradicts the help "Sequence objects will automatically be flattened out in all functions except those with attribute SequenceHold or HoldAllComplete. " (italics mine) $\endgroup$
    – Hector
    Commented Jun 10, 2016 at 6:33
  • 2
    $\begingroup$ During evaluation I suppose but here it's trickier, but I would rather wait for someone with deeper understanding of the issue to explain. $\endgroup$
    – Kuba
    Commented Jun 10, 2016 at 6:35
  • 2
    $\begingroup$ Yes, Sequence objects are flattened upon evaluation. They won't be flattened without evaluation of the expression. $\endgroup$ Commented Jun 10, 2016 at 6:37
  • 1
    $\begingroup$ Just toss an xx=xx between your ...=Sequence[] operations... $\endgroup$
    – ciao
    Commented Jun 10, 2016 at 6:38

2 Answers 2

21
$\begingroup$

What happens to the list after we assign an element to Sequence[]

The answers given so far already contain most of the pieces needed to explain this behavior, but I decided to still add this answer.

This topic has been discussed many times in the past, although not on this site. Here is one such discussion, where I also contributed an answer. I will take my answer from there, and adopt it to this case.

Consider your example:

xx = Range[5]; xx[[1]] = Sequence[]; xx

Now, let us see what is in xx:

?xx
Global`xx

xx={Sequence[],2,3,4,5}

We see that the first element is still there.

Some reasoning behind this behavior

We don't really change the size of a list (as stored in OwnValuesetc) by assigning Sequence[] to some of its elements - Sequence[] is just as good an element as anything else.

The important part to realize is that all the Sequence[] magic happens at evaluation stage - the size of an evaluated list becomes effectively smaller as a result of evaluation (during which Sequence-s inside heads without SequenceHold or HoldAllComplete attribute are spliced).

This seems entirely consistent, given that lists are internally implemented as arrays and then to really change the size of a list as it is stored, we would generally need O(n) operations where n is the size of the list. OTOH, operations like Part and assignments should not be concerned with anything else than just assigning parts of expressions, and such list rearrangements should be beyond their scope IMO.

Back to the example

Returning to your example, all subsequent assignments simply re-assign Sequence[] to the first element. Since the list is not evaluated during the part assignment, Sequence splicing does not happen.

Here is one way to get the elements removed, which would further clarify things.

xx = Range[5];
xx[[1]] = Sequence[];
xx = xx
xx[[1]] = Sequence[];
xx = xx
xx[[1]] = Sequence[];
xx = xx

(*

  {2, 3, 4, 5}

  {3, 4, 5}

  {4, 5}
*)

In this case, the code does what we need - removes the first element each time. The key part here is the assignment xx = xx. In this case it is non-trivial, because we do evaluate xx on the r.h.s. during the assignment, the Sequence splicing proceeds as it should, and then we assigned the result (which no longer contains the original first element, and is one element smaller now), to the same variable.

Note however, that xx = xx is an O(n) operation if xx stores a list of length n, which contains one or more Sequence[] inside it. So, this method isn't really efficient. There is no magic here either, because there is no way you can remove an element of an array in O(1) time - you'd need a different data structure if you need fast element removals (such as a linked list, or actually an Association). That the element removal is hidden in this method by a high-level construct like Sequence does not change that fundamental fact.

Summary

What you have observed is a result of an interplay between evaluation and the way part assignments are performed. Here are some things to keep in mind regarding this:

  • Part assignments fundamentally can not remove elements from an expression stored in a variable.
  • Assigning Sequence[] (or Nothing, for that matter) to a part of an expression will not remove that part, but rather put there Sequence[] or Nothing. Indeed, this seems to be the only sensible behavior.
  • Shrinking of expressions containing Sequence[] is an evaluation-time effect, while expressions stored in variables are kept unevaluated, also during part assignments.

These points together explain your observation. In fact, this is just one reason to avoid using Sequence as an element-removing device, when possible. There are other options available for that. If you really do want to change your expression (stored in a symbol) via Sequence part assignment, one way to do that is to reassign the expression to the variable, using var = var idiom, allowing it to evaluate.

Note however, that var = var is an O(n) operation, so that won't be very efficient if you only remove one or a few elements in a large list. Which is another argument against using Sequence, because it hides the underlying algorithmic complexity and may therefore result in performance surprises for the uninitiated.

$\endgroup$
5
$\begingroup$

Noted the documentation of the Sequence's Details:

Sequence objects will automatically be flattened out in all functions except those with attribute SequenceHold or HoldAllComplete.

xx= Range@5;
Do[xx[[1]] = Nothing, 3];xx

{2, 3, 4, 5}

It'll return a list just without first element.You should activate to remove the Sequence[] use xx=xx

xx = Range@5;
Do[xx[[1]] = Nothing; xx = xx, 3]; xx

{4, 5}

As your case,you should add a xx=xx in the expression

xx = Range[5]; xx[[1]] = Sequence[]; xx = xx; xx[[1]] = Sequence[]; xx

{3, 4, 5}

$\endgroup$
2
  • $\begingroup$ Subjective comment: I think I wouldn't I understand it better as a newbie. First of all Nothing behaves the same way in this context. Moreover, "activate" is vague and leads to confusion with Activate. At the end, the last example does xx = xx once, is it on purpose or not, one could ask. $\endgroup$
    – Kuba
    Commented Jun 10, 2016 at 7:25
  • $\begingroup$ @Kuba A same confusion to me actually after I post this answer.I have tried Activate and Evaluate,but don't work both.I just give a work-around maybe.Wait better thinking can explain this behavior. $\endgroup$
    – yode
    Commented Jun 10, 2016 at 7:32

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.