18

Let's say I have a function

formula[x_List] := (x[[1]] - x[[2]]) + (x[[3]] - x[[4]]);

and I want to pass it a variable and get back the unevaluated formula with these inputs in place, so for example with input {1,2,3,4} I would get back

Out[]= (1-2)+(3-4)

I do not want it to return strings; if I copy the output and paste it somewhere, I want it to be executable. I also don't want it to do any math on the function; if x[[1]]==x[[2]], it should not collapse x[[1]]-x[[2]] to 0. All I want is pattern substitution.

I figure the answer involves Hold[], but I have not been able to get the results I want.

István Zachar
  • 47,032
  • 20
  • 143
  • 291
Michael Stern
  • 4,695
  • 1
  • 21
  • 37
  • 1
    Both the Mr. Wizard and Mike Honeychurch have solutions that come very close to what I want. In fact, at first I thought they both had it. I notice however that there is a case in which both methods fail. Both simplify out certain simple identities. For example, if formula[{a_, b_, c_, d_}] := HoldForm[abc*d], then both versions drop some 1s given as input (oddly, not all). formula[{1,1,1,1}] works as desired but formula[1,2,1,31] does not. – Michael Stern Jul 05 '12 at 00:37
  • Michael, I have addressed this problem in my answer. – Mr.Wizard Jul 05 '12 at 09:42

4 Answers4

18
formula[{a_, b_, c_, d_}] := Defer[(a - b) + (c - d)]

formula[{1, 2, 3, 4}]
(1 - 2) + (3 - 4)

Addressing your comment below the question, Defer (also HoldForm) does work, but the output formatting engine is changing the appearance of your result. You can see this by using InputForm:

Defer[1 * 2 * 1 * 31] // InputForm
Defer[1*2*1*31]

The hold functions, even HoldComplete, do not prevent the formatting engine form going to work:

HoldComplete[1 * 2 * 1 * 31]

Mathematica graphics

You need to attack the problem at its source, by Blocking Times during Box creation:

SetAttributes[defer, HoldAll]

MakeBoxes[defer[args__], fmt_] :=
  Block[{Times},
    MakeBoxes[Defer[args], fmt]
  ]

You can now use defer as you would Defer but Times will not be formatted:

formula[{a_, b_, c_, d_}] := defer[a*b*c*d]

formula[{1, 2, 1, 31}]
1 * 2 * 1 * 31
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
10

You are looking for partial evaluation. Perhaps, there is a shorter way, but here is one (rather subtle):

Clear[evalAtPattern]
evalAtPattern[expr_, p_] :=
   expr /. pt : p :> With[{pp = pt}, pp /; True];

Clear[formula];
formula[x_List] :=
  evalAtPattern[
    Defer[(x[[1]] - x[[2]]) + (x[[3]] - x[[4]])],
    HoldPattern[x[[_]]]];

Now,

formula[{1, 2, 3, 4}]

(* (1 - 2) + (3 - 4) *)

And the output code can be executed. The solution combined injecting evaluated pieces inside held expressions via Trott - Strzebonski technique, and the use of Defer.

Szabolcs
  • 234,956
  • 30
  • 623
  • 1,263
Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
4
formula[{a_, b_, c_, d_}] := HoldForm[(a - b) + (c - d)]

formula[{1, 2, 3, 4}]
(1 - 2) + (3 - 4)

"...if I copy the output and paste it somewhere, I want it to be executable..."

You would need to release the hold.

ReleaseHold[(1 - 2) + (3 - 4)]
Mike Honeychurch
  • 37,541
  • 3
  • 85
  • 158
  • This works for me, as does the Defer[] solution above; I've credited Mr.Wizard for the answer only because he got in first. – Michael Stern Jul 05 '12 at 00:06
  • I accepted, and then withdrew when I found a problem (discussed in an edit to my question, above). Mr. Wizard has since addressed this problem, so scores the point. – Michael Stern Jul 05 '12 at 11:07
1

How about

formula[x_List] := 
    With[{a = x[[1]], b = x[[2]], c = x[[3]], d = x[[4]]}, Defer[(a - b)/(c - d)]]

?

Life
  • 660
  • 3
  • 12