19
$\begingroup$

Since Mathematica deals with Head[a, b] as an expression in this way

  • The first Part of Head[a, b] is a
  • The second Part of Head[a ,b] is b
  • The zeroth Part of Head[a, b] is Head

it is natural to look at the expression in a List[] way as List[Head, a, b], which contains all the information the Mathematica Kernel requires. Based on such thinking, I want to construct, from any Mathematica expression, a list of this form so said expression is a list that can be manipulated easier providing we have sufficient knowledge of list manipulation.

For example, if I have a list

list = {{x, 1, 10}, Plot, Sin[x]}

I thought I could construct a plot-command Plot[Sin[x], {x, 1, 10}] by evaluationg

list[[2]][list[[3]], list[[1]]]

or

Extract[#, {2}][Extract[#, {3}], Extract[#, {1}]] &[list] 

But these are invalid for reasons that I have no knowledge about. Could anyone explain the reason why my ideas failed in Mathematica.

By the way, the expression

f[Extract[#, {3}], Extract[#, {1}]] &[list] /. f -> Extract[#, {2}] &[list]

works. Why do the former two examples before this one not work?

$\endgroup$
3
  • $\begingroup$ I started working on a new section of my answer but I realize that to make it constructive I need some examples of the kinds of manipulations you wish to do on this "list" of expression parts. Could you describe your intentions and provide an example or two? $\endgroup$
    – Mr.Wizard
    Commented Feb 27, 2013 at 9:48
  • $\begingroup$ Thanks a lot! You really help me a lot. Frankly speaking, to rewrite Mathematica expression as List has no special intentions. But since Mathematica expression as a List even without considering the sequence of elements in the List can work as usual, maybe Wolfram|Alpha can manipulate Mathematica Expressions more easier based on such thinking. $\endgroup$ Commented Feb 27, 2013 at 13:44
  • $\begingroup$ Related: Leonid Shifrin's magnificent synopsis of metaprogramming in Mathematica. $\endgroup$
    – Mr.Wizard
    Commented Mar 13, 2014 at 13:28

1 Answer 1

18
$\begingroup$

That's an interesting first question. Welcome. :-)

From a simplistic perspective this should work, but as you observe there are evaluation properties that are more complex. Here is a reference for most (but not all) behavior:

The Standard Evaluation Sequence

Let's follow those steps for your example.

Heads are evaluated first

Evaluate the head h of the expression.

The first actual step of evaluation is to evaluate the head of any expression. Therefore:

list[[2]][ (* anything *) ]

Evaluates to:

Plot[ (* anything *) ]

Hold attributes

Evaluate each element of the expression in turn. If h is a symbol with attributes HoldFirst, HoldRest, HoldAll, or HoldAllComplete, then skip evaluation of certain elements.

See these documentation pages for reference: HoldAll, HoldFirst, HoldRest, SequenceHold, HoldAllComplete.

These Attributes control the evaluation of arguments within a function, as stated above. Let's check the attributes of Plot:

Attributes[Plot]
{HoldAll, Protected}

We see that it has HoldAll. Because of this its arguments, in the case of your example list[[3]] and list[[1]] are not evaluated, and because these expressions are not valid input (do not match the pattern in the function definition) for Plot evaluation stops and an error message is issued:

Plot::pllim: Range specification list[[1]] is not of the form {x, xmin, xmax}. >>


An apparent solution for the simple case

Now we can follow the method by which your working code succeeds. If we build an expression with head f, which does not have a Hold attribute, the arguments evaluate:

f[list[[3]], list[[1]]]
f[Sin[x], {x, 1, 10}]

We could also use List in place of f, which is the most frequent choice for this particular operation and which I will use hereafter. At this point it is possible to replace f or List with another head, done most easily with Apply:

list[[2]] @@ { list[[3]] , list[[1]] }

Mathematica graphics

Which appears to work. But does it?

Let us now set a value for x beforehand:

x = 0;

This does not disrupt Plot:

Plot[Sin[x], {x, 1, 10}]  (* produces graphic *)

But see what happens when we try our reconstructed expression now:

list[[2]] @@ { list[[3]] , list[[1]] }

During evaluation of In[142]:= Plot::itraw: Raw object 0 cannot be used as an iterator. >>

 Plot[0, {0, 1, 10}]

Clearly not what we intended. You see that Plot has HoldAll for a reason: to prevent the premature evaluation of its arguments.

A more robust solution

You may also see that defining the parts of an expression to reconstruct in a bare list is probably not a good idea, so let's do something about that now:

exprs = Hold[{x, 1, 10}, Plot, Sin[x]]

Now despite the global value assigned to x we can manipulate these expressions:

exprs[[{3}]]
Hold[Sin[x]]

Note that because of the brackets { } and for the reason described here the expression Sin[x] is returned wrapped in Hold, and x does not evaluate. (If we used exprs[[3]] we would get the raw expression and evaluation would continue.)

We can now attempt to reconstruct our Plot expression:

exprs[[{2}]][exprs[[{3}]], exprs[[{1}]]]
Hold[Plot][Hold[Sin[x]], Hold[{x, 1, 10}]]

We see that the Part calls have evaluated as desired and that the expressions themselves have not evaluated, also as we desired. At this time we can use the function ReleaseHold which will strip one level of Hold from each expression, yielding our final result:

% // ReleaseHold

Mathematica graphics


Other considerations

So far I have discussed only the first two steps (after the non-evaluation rule for raw objects) of the Standard Evaluation Sequence. They happen to be the most relevant, but they are not complete.

UpValues

Within the standard evaluation sequence we have at least one more issue to address: UpValues. These are definitions created using UpSet, UpSetDelayed, TagSet, or TagSetDelayed. The definitions (rules) are attached to Symbols that when used inside another function (at the first level) cause execution (matching and replacement) of the rule before the definitions for the head of the main expression. As an example we can make a very general (and dangerous) definition that will replace (nearly) any expression which has as one of its arguments up[] with "UpValue triggered!":

up /: _[___, up[], ___] := "UpValue triggered!"

We can see that Hold does not contain this:

Hold[1, 2, up[], 3]  (* outputs: "UpValue triggered!" *)

This is the purpose of HoldComplete and the attribute HoldAllComplete:

HoldComplete[1, 2, up[], 3]  (* does not evaluate *)

We should therefore use these functions when attempting to extend this reconstruction method to arbitrary expressions.

Other methods for building an expression

Leaving aside whatever manipulations you wish to do with this "list" expression parts it may be more convenient assemble them using something other than Part or Extract. Two methods are: Function, and replacement patterns (see "injector pattern").

The replacement pattern has the advantage of being concise and, to me, readable. Specifically one does not need to juggle additional intermediate functions and attributes as the replacement is done structurally, before evaluation continues.

exprs /. Hold[arg2_, head_, arg1_] :> head[arg1, arg2]  (* produces graphic *)

The other method is more verbose, using the third parameter of Function to set an attribute so that the arguments of the created function (the expression pieces) do not pre-evaluate:

Function[{arg2, head, arg1}, head[arg1, arg2], HoldAllComplete] @@ exprs

Or using an undocumented syntax (Null as first argument; ignore the syntax highlighting):

Function[Null, #2[#3, #1], HoldAllComplete] @@ exprs
$\endgroup$
0

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.