Using Sequence to delete a part of an expression

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.


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}