Head and everything except Head?

53

15

I have been working on picking expressions apart using Head and Part and encountered a little mystery. Consider the canonical example

a + b + c

which has FullForm

Plus[a, b, c]

We expect, then, (a+b+c)[[0]] === Plus === Head[a+b+c] to be True, and it is. But then, we would expect (a+b+c)[[1;;3]] would be {a, b, c}, wouldn't we? But it isn't. The following is true:

(a + b + c)[[1;;3]] === (a + b + c)

Somehow, [[1;;3]], which is supposed to pick off elements 1 through 3 of its argument and put them in a List, doesn't get rid of the Head, which is element 0!

The questions are, then (and I will be grateful for hints and answers!)

  1. Why doesn't (a + b + c)[[1;;3]] get rid of (a + b + c)[[0]], the Head?

  2. What is the right way to get rid of the Head?

  3. (a + b + c)[[0;;3]] produces 0. I would expect it to produce {Plus, a, b, c}. Instead, it produces 0. This just deepens the mystery for me! Why?

Reb.Cabin

Posted 2012-02-29T06:19:00.627

Reputation: 8 501

Answers

56

Point #1

Part always wraps element sequences with the original head of the expression.

expr = Hold[1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5];

expr[[{2, 3}]]
Hold[2 + 2, 3 + 3]

For this purpose a single part e.g. 1 is not a sequence but {1} and 1 ;; 1 are:

expr[[1]]

expr[[{1}]]

expr[[1 ;; 1]]
2

Hold[1 + 1]

Hold[1 + 1]

This applies at every level of the extraction:

exp2 = g[h[1, 2], i[3, 4]];

exp2[[  2 , 1  ]]
exp2[[ {2}, 1  ]]
exp2[[  2 ,{1} ]]
exp2[[ {2},{1} ]]
3

g[3]

i[3]

g[i[3]]

I used this nontrivially for Elegant manipulation of the variables list.

Point #2

Consider instead using Extract which wraps sequences in List:

Extract[expr, {{2}, {3}}]
{4, 6}

The third argument of Extract can be used to specify a function to apply to individual elements before they are evaluated:

Extract[expr, {{2}, {3}}, HoldForm]
{2 + 2, 3 + 3}

If you want all parts you can also use Level:

Level[(a + b + c), {1}, Heads -> True]
{Plus, a, b, c}

Or Cases:

Cases[(a + b + c), _, Heads -> True]
{Plus, a, b, c}

Or Replace/ReplaceAll:

(a + b + c) /. head_[body___] :> {head, body}
{Plus, a, b, c}

Point #3

The last point is more tricky and I had to check it myself. There is a behavior that I also did not expect:

Range[5][[0 ;; 5]]
{}

What I expected was an error as seen here:

Range[5][[0 ;; 4]]

and here:

Range[5][[0 ;; 6]]

When Span is used in Part[x, 0 ;; n] where n is the length of x, Part returns the head of the expression. Therefore (a + b + c)[[0;;3]] returns Plus[] and Plus[] evaluates to 0.


I believe Span behaves this way because of how it handles non-positive values, and zero-length spans. Consider:

Range[10][[-2 ;; 10]]
{9, 10}

You can see that it wraps around. Now consider:

Range[10][[5 ;; 4]]
{}

An empty span returns the head of the expression with no arguments.

Using 0 ;; n where n is the last element in the list, or 0 ;; -1, is also an empty span wrapping around the open end of the list.

Range[10][[0 ;; -1]]
{}

Mr.Wizard

Posted 2012-02-29T06:19:00.627

Reputation: 259 163

2Ah, I see that you can answer #1 as well, not just Leonid :) – Verbeia – 2012-02-29T06:26:49.563

1

Regarding point #3, you might find this discussion relevant (if not too enlightening).

– Szabolcs – 2012-02-29T11:47:00.177

@Szabolcs That's important information. I cannot recall a situation where it would affect me, but it would be bloody annoying to track it down if it did. – Mr.Wizard – 2012-02-29T16:36:39.087

1And another place for the all-powerful Cases: Cases[{a + b + c}, head_[args___] -> {head, args}] produces {{Plus, a, b, c}} – Reb.Cabin – 2012-03-08T23:04:09.173

2@Reb doing that route you don't even need Cases: a + b + c /. head_[args___] :> {head, args} – Mr.Wizard – 2012-03-09T00:06:08.987

@Mr.Wizard +1 yields good answer ! – Artes – 2012-03-20T17:46:34.890

@Artes Thank you very much! :D – Mr.Wizard – 2012-03-20T18:05:41.047

16

Perhaps only the developers (or Leonid) can answer #1 and #3 and "why".

The quick answer to #2 is Apply, remembering that List is a Head like any other:

List @@ (a + b + c)

{a, b, c}

On #3, I can't explain why ;; syntax gives 0 when

(a + b + c)[[Range[0, 3]]]

gives

a + b + c + Plus

So you could try

List @@ ((a + b + c)[[Range[0, 3]]])

{a, b, c, Plus}

Verbeia

Posted 2012-02-29T06:19:00.627

Reputation: 33 191

2In some sense, Sequence@@(a+b+c) comes closer to "everything but Head" than List@@(a+b+c): While List is just another head (with a special input/display syntax associated with it), Sequence is the closest Mathematica has to a headless expressions: If you insert it anywhere (except functions with attribute HoldAllComplete) it just gets interpolated. – celtschk – 2012-05-21T19:35:59.610

1@celtschk For completeness the attribute SequenceHold also prevents the flattening of Sequence expressions, just as HoldAllComplete does, while allowing other evaluation. – Mr.Wizard – 2017-08-14T06:13:13.600

There is something broken in the way the site previews answers. I have been amending my answer for the last twenty minutes and only now do I see that you also posted an answer. – Mr.Wizard – 2012-02-29T06:48:37.537

@Mr.Wizard You don't get notified of a new answer when editing your already existing answer... – rm -rf – 2012-02-29T08:15:44.960

1The List @@ answer has a deep, hackish charm, coming closest to answering "... and everything but Head.". The Extract and Level answers have more versatility and clearly enable complete analysis and compilation of expressions. A metacircular evaluator for MMA would not seem too remote a possibility. – Reb.Cabin – 2012-02-29T08:17:23.773

@R.M I would expect that when you save the edit to your post, the page would refresh, showing any new answers that were posted while you were working. This is not the case. – Mr.Wizard – 2012-02-29T16:35:27.390