Returning an unevaluated expression with values substituted in

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.

-
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 5 '12 at 0:37
Michael, I have addressed this problem in my answer. – Mr.Wizard Jul 5 '12 at 9:42

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]


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

-
+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

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.

-
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 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
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)]

-
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

formula[x_List] :=

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