Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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.

share|improve this question
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[a*b*c*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 5 '12 at 0:37
    
Michael, I have addressed this problem in my answer. –  Mr.Wizard Jul 5 '12 at 9:42
add comment

4 Answers 4

up vote 14 down vote accepted
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
share|improve this answer
    
+1 for simplicity of interpretation and solution. –  Leonid Shifrin Jul 4 '12 at 16:38
    
Very very close, but per my note above, there are cases in which this collapses 1*x to x. Any way to prevent this? –  Michael Stern Jul 5 '12 at 1:15
add comment

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.

share|improve this answer
    
It looks like Leonid had a different interpretation. –  Mr.Wizard Jul 4 '12 at 16:37
    
@R.M. His answer is simpler. It really depends on how general one wants to be. I tried to have code which can keep the original definition minimally modified. –  Leonid Shifrin Jul 4 '12 at 16:37
1  
+1 for complexity of interpretation and solution. ps why not expr /. pt : p :> RuleCondition[pt] ? –  Mr.Wizard Jul 4 '12 at 16:40
    
@Mr.Wizard Thanks :) No reason, really, just old habits. –  Leonid Shifrin Jul 4 '12 at 16:41
add comment
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)]
share|improve this answer
    
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 5 '12 at 0:06
    
@MichaelStern Well to me it looks like you haven't accepted an answer yet. –  Ajasja Jul 5 '12 at 10:45
    
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 5 '12 at 11:07
add comment

How about

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

?

share|improve this answer
    
@rm -rf Thanks! –  Life Jun 23 '13 at 17:57
    
This does not match the specification in the question as you cannot copy & paste the output as evaluable input; that is best done with Defer, introduced in version 6. –  Mr.Wizard Jun 23 '13 at 20:40
    
Thanks!@Mr.Wizard The function Defer is really amazing. It is like HoldForm with ReleaseHold command if copy & paste it. –  Life Jun 24 '13 at 4:09
    
I have modified it by replacing HoldForm into Defer. –  Life Jun 24 '13 at 11:30
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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