15
$\begingroup$

Normal documentation reads:

Normal[expr] converts expr to a normal expression, from a variety of special forms.

But is there a way to control which special forms it converts and which it does not?

$\endgroup$
15
$\begingroup$

Yes.

It turns out that Normal accepts an undocumented second parameter which may be a Symbol or a list of Symbols and it will only affect those forms. Plain application converts all these forms (and more):

abby = {
  SparseArray[{3 -> "a", 5 -> "b"}],
  <|1 -> "a", 2 -> "b", 3 -> "c"|>,
  Series[Exp[x], {x, 0, 5}],
  Quantity[1, "Percent"]
 };

abby // Normal
{{0, 0, "a", 0, "b"},
 {1 -> "a", 2 -> "b", 3 -> "c"}, 
 1 + x + x^2/2 + x^3/6 + x^4/24 + x^5/120,
 1/100}

We can target the specific forms like this:

abby ~Normal~ SparseArray
abby ~Normal~ Quantity
{{0, 0, "a", 0, "b"},
 <|1 -> "a", 2 -> "b", 3 -> "c"|>,
 SeriesData[x, 0, {1, 1, 1/2, 1/6, 1/24, 1/120}, 0, 6, 1],
 Quantity[1, "Percent"]}

{SparseArray[< 2 >, {5}],
 <|1 -> "a", 2 -> "b", 3 -> "c"|>,
 SeriesData[x, 0, {1, 1, 1/2, 1/6, 1/24, 1/120}, 0, 6, 1],
 1/100}

Or multiple types with:

abby ~Normal~ {SeriesData, Association}
{SparseArray[< 2 >, {5}],
 {1 -> "a", 2 -> "b", 3 -> "c"}, 
 1 + x + x^2/2 + x^3/6 + x^4/24 + x^5/120,
 Quantity[1, "Percent"]}

Usage note and correction

Previously I wrote that Normal passes over held expressions. It does not.

Some cases could be handled equivalently with a simple replacement like:

expr /. sa_SparseArray :> Normal[sa]

However evaluation is not equivalent such as within a held expression:

held = Hold @@ abby

held /. sa_SparseArray :> Normal[sa]

held ~Normal~ SparseArray
Hold[
 Normal[SparseArray[< 2 >, {5}]],
 <|1 -> "a", 2 -> "b", 3 -> "c"|>,
 SeriesData[x, 0, {1, 1, 1/2, 1/6, 1/24, 1/120}, 0, 6, 1],
 Quantity[1, "Percent"]
]

Hold[
 {0, 0, "a", 0, "b"}, 
 <|1 -> "a", 2 -> "b", 3 -> "c"|>,
 SeriesData[x, 0, {1, 1, 1/2, 1/6, 1/24, 1/120}, 0, 6, 1],
 Quantity[1, "Percent"]
]
$\endgroup$
  • 1
    $\begingroup$ Igor approves. +1 $\endgroup$ – ciao May 30 '15 at 5:39
  • $\begingroup$ MapAt[Normal,abby,1]? Just kidding, I can see the potential if you don't know what kind of data types will be returned and you want to get rid of a specific one (Quantity would be my target, I guess). $\endgroup$ – Sjoerd C. de Vries May 30 '15 at 8:38
  • $\begingroup$ @Sjoerd To catch expressions at all levels one would need something like expr /. q_Quantity :> Normal[q] which is fine but a bit less clean. There is also a difference in held expressions, e.g. Hold[Quantity[5, "Percent"]] would return Hold[Normal[Quantity[5, "Percent"]]] with the rule above while Normal would simply pass over that expression; one would need to add a skip rule like /. {x : _Hold | _HoldForm | _HoldComplete :> x, q_Quantity :> Normal[q]} to approach that behavior and it would still miss user functions with a hold attribute so that would require further testing. $\endgroup$ – Mr.Wizard May 30 '15 at 15:42
  • 1
    $\begingroup$ As of version 10.x, this feature appears to be documented $\endgroup$ – QuantumDot Dec 23 '16 at 19:38
  • $\begingroup$ @QuantumDot Thanks. That is not in the version 10.1.0 documentation. I wonder when it was added. $\endgroup$ – Mr.Wizard Dec 23 '16 at 19:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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