4
$\begingroup$

I have a function I wrap a lot of my numbers with to factor them and display them out a certain way. Usually I do this at the last step of a calculation, but sometimes it would be nice if I could designate that a number should be displayed one way, but handled another way for computation.

For example, suppose I wanted prime numbers in some list to be printed out bold. I might do that with:

nums = If[PrimeQ@#, Style[#,Bold], #]& /@ nums

The problem is that if I then want to, say, take all the elements in that list that are under 100, any code that hits Style instead of a number will freak out.

Is there something like a wrapper that designates an expression to be handled as some value for subsequent evaluation, but print out a certain way when it comes to display preparation? I know you could achieve this by messing with $PrePrint, but it seems like there might be a better way.

$\endgroup$
2
  • 1
    $\begingroup$ Have a look at Interpretation $\endgroup$
    – Jason B.
    Commented Oct 9, 2018 at 19:23
  • 1
    $\begingroup$ @JasonB. That doesn't work if you put the Interpretation wrapped numbers in a variable. Try nums = Interpretation[If[PrimeQ@#, Style[#, Red], #], #] & /@ Range[15] and then 2*nums. $\endgroup$ Commented Oct 9, 2018 at 19:55

3 Answers 3

5
$\begingroup$

I have tried to do this before and found it ultimately to be a complex problem. Certainly there are easier ways to accomplish specific tasks, such as what Bob Hanlon showed, but that doesn't actually provide the functionality requested.

I believe you will need to choose either a "whitelist" or "blacklist" approach to how functions handle your wrapper. For example we could use UpValues (created with TagSetDelayed) to tell all functions besides If, List, and MakeBoxes to strip the wrapper. (These specific functions only because I need them for the example to function.)

Foundation:

MakeBoxes[hiddenStyle[x_, sty__], _] := ToBoxes @ Style[x, sty]

p1 = Except[If | List | MakeBoxes];

hiddenStyle /: (h : p1)[a___, hiddenStyle[x_, __], b___] := h[a, x, b]

Usage:

nums = If[PrimeQ@#, hiddenStyle[#, Red], #] & /@ Range[15]
2*nums

enter image description here

Here you see that your style is applied (by way of the MakeBoxes definition), but arbitrary functions outside of our p1 pattern see the wrapper hiddenStyle as transparent. This is only a minimal working example and is not intended to be robust.

The other approach is to overload each function that you want to use to see the wrapper as transparent. For example we could write:

ClearAll[hiddenStyle]
p2 = Plus | Times | Power;
MakeBoxes[hiddenStyle[x_, sty__], _] := ToBoxes @ Style[x, sty]
hiddenStyle /: (h : p2)[a___, hiddenStyle[x_, __], b___] := h[a, x, b]

nums = If[PrimeQ@#, hiddenStyle[#, Red], #] & /@ Range[15]
2*nums
foo /@ nums

enter image description here

With more work it is possible to create more complex behaviors such as passing on styling to output, but they often come at a significant cost of performance, e.g. interfering with Listable behavior.

If you describe your application in more detail I can try to provide additional examples.

$\endgroup$
5
$\begingroup$

Using $PrePrint is fairly easy.

Define a helper function to use with $PrePrint to deconflict multiple uses of Slot (#)

boldPrime = If[PrimeQ[#], Style[#, Bold], #] &;

$PrePrint = If[Head[#] === List, boldPrime /@ #, #] &;

boldPrime is mapped onto lists

nums = Range[25]

enter image description here

nums can be used subsequently

nums^(1/2)

enter image description here

RandomInteger[{0, 100}, 20]

enter image description here

It is not mapped onto other forms of expressions

3 x^5

enter image description here

$\endgroup$
5
$\begingroup$

Why not create a form wrapper to output primes in bold? For example:

MakeBoxes[myForm[expr_],StandardForm] ^:= Internal`InheritedBlock[{MakeBoxes},
    MakeBoxes[i_Integer,StandardForm] := If[PrimeQ[i], StyleBox[ToString[i],Red], ToString[i]];
    MakeBoxes[expr]
]

Unprotect[$OutputForms];
$OutputForms = DeleteDuplicates[Append[$OutputForms, myForm]];
Protect[$OutputForms];

(I used red instead of bold so that the difference is a bit more noticeable). Then:

foo /@ Range[10] //myForm

enter image description here

Because myForm is a member of $OutputForms, the myForm wrapper gets stripped before getting stored in Out:

%//InputForm

{foo[1], foo[2], foo[3], foo[4], foo[5], foo[6], foo[7], foo[8], foo[9], foo[10]}

Copy/paste of will also strip the myForm wrapper.

$\endgroup$

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.