RE: Functional Expression Meaning (was:A Functional Expression Trick)
- To: mathgroup at smc.vnet.net
- Subject: [mg23885] RE: Functional Expression Meaning (was:[mg23859] A Functional Expression Trick)
- From: Wolf Hartmut <hwolf at debis.com>
- Date: Thu, 15 Jun 2000 00:51:22 -0400 (EDT)
- Sender: owner-wri-mathgroup at wolfram.com
Dear David, please see my answer below >>>> -----Original Message----- From: David Park [SMTP:djmp at earthlink.net] To: mathgroup at smc.vnet.net Sent: Monday, June 12, 2000 7:18 AM To: mathgroup at smc.vnet.net Subject: [mg23859] A Functional Expression Trick Dear MathGroup, Mathematica provides for functional expressions, but the standard exposition (Section 2.2.5 - Section 2.2.9 in the Mathematica Book) does not make it easy to work with them. The principal problem is that an expression of pure functions is not itself a pure function. Here are two pure functions: f = #1*#3^2 & ; g = Cos[#2]*#3 & ; I defined these functions so that they use consistent variables. The slot numbers refer to the same variables in the two functions. If we form an expression from these pure functions, we obtain an object that is not easy to work with. fexpr = f*g^2 + 2*f + Sin[f*g] 2*(#1*#3^2 & ) + (Cos[#2]*#3 & )^2*(#1*#3^2 & ) + Sin[(Cos[#2]*#3 & )*(#1*#3^2 & )] For example, try to evaluate: fexpr[x, y, z] 2*(#1*#3^2 & ) + (Cos[#2]*#3 & )^2*(#1*#3^2 & ) + Sin[(Cos[#2]*#3 & )*(#1*#3^2 & )])[x, y, z] You can try to use Through on this expression, but you probably won't like the results. (I developed the PushThrough package to handle cases like this, but now I am going to present a different method.) Or suppose we wish to take derivatives of fexpr: Derivative[1, 0, 1][fexpr][x, y, z] Derivative[1, 0, 1][2*(#1*#3^2 & ) + (Cos[#2]*#3 & )^2*(#1*#3^2 & ) + Sin[(Cos[#2]*#3 & )*(#1*#3^2 & )]][x, y, z] This won't evaluate because Mathematica expects fexpr to be a pure function or function name. If we have defined our pure functions with consistent slot numbers, then it is simple enough to convert the expression into a pure function. Simply remove all the &s in the middle and put one at the end. The following function does this: funexpr[expr_] := Function[Evaluate[expr /. Function -> Identity]] Now, it is very easy to evaluate expressions involving fexpr. funexpr[fexpr][x, y, z] 2*x*z^2 + x*z^4*Cos[y]^2 + Sin[x*z^3*Cos[y]] Derivative[1, 0, 1][funexpr[fexpr]][x, y, z] 4*z + 4*z^3*Cos[y]^2 + 3*z^2*Cos[y]*Cos[x*z^3*Cos[y]] - 3*x*z^5*Cos[y]^2*Sin[x*z^3*Cos[y]] There is another standard method for converting a functional expression into a pure function but it involves much more typing and somewhat undermines the convenience of functional expressions. fgfunc = Function[{x, y, z}, f[x, y, z]*g[x, y, z]^2 + 2*f[x, y, z] + Sin[f[x, y, z]*g[x, y, z]]]; fgfunc[x, y, z] 2*x*z^2 + x*z^4*Cos[y]^2 + Sin[x*z^3*Cos[y]] Derivative[1, 0, 1][fgfunc][x, y, z] 4*z + 4*z^3*Cos[y]^2 + 3*z^2*Cos[y]*Cos[x*z^3*Cos[y]] - 3*x*z^5*Cos[y]^2*Sin[x*z^3*Cos[y]] David Park djmp at earthlink.net http://home.earthlink.net/~djmp/ <http://home.earthlink.net/~djmp/> >>>> There are several remarks to made: (1) I would have liked to receive this as "Functional Expression Meaning", such the renaming of this thread: If we have two functions (or Mappings, Operators) f and g what does f g mean after all? Nothing, we have to _define_ it! So often this is short for Composition[f, g] (beware sometimes it's Composition[g, f] !) (this just to show the need for definition). When I studied physics (long ago), we used f g (or f * g to be more explicit) as short for: (f * g)[x] ::= f[x] * g[x] where "*" denotes an operator in the image-space. Now here functions are lambda-expressions, so we have to build the lambda expression for (f * g) from those of f and g *and keep the semantics of the lambda expression*. In his "Programming in Mathematica" (2nd ed., p.112) Roman Maeder describes the latter as Function[x, body][arg] is evaluated essentially as ReleaseHold[ Hold[body] /. Literal[x] -> arg] (Literal now has become HoldPattern, regard: arg is evaluated before substitution). So Function[ x, (body(f) /. HoldPattern[var(f)] ->x) * (body(g) /. HoldPattern[var(g)] ->x)] is to be the lambda expression for (f * g). It's time to dig up a sample: x = 1; y = 2; z = 3; x2 = 4; y2 = 5; z2 = 6; (* to indicate possible errors *) ffa = Function[{z, y, x}, Print["high"]; z *x^2]; gga = Function[{x2, y2, z2}, Print["there"]; Cos[y2]*z2]; ffgga = (Function[{x, y, z}, Evaluate[rh[( Extract[ffa, {2}, Hold] /. {HoldPattern[z] :> x, HoldPattern[y] :> y, HoldPattern[x] :> z}) * ( Extract[gga, {2}, Hold] /. {HoldPattern[x2] :> x, HoldPattern[y2] :> y, HoldPattern[z2] :> z} )]] ]) /. rh -> ReleaseHold Function[{x, y, z}, ReleaseHold[Hold[Print["high"]; x*z^2] * Hold[Print["there"]; Cos[y]*z]]] (Of course the new arguments have not to be evaluated here!) We test it: ffgga[x, y, z] "high" "there" 27 Cos[2] ffa[x, y, z] * gga[x, y, z] "high" "there" 27 Cos[2] (comment: This procedure was done as to keep the semantics for certain, I'd more like to program it as: ffgga = Function[{x, y, z}, Times[#1, #2]] & @@ {Extract[ffa, {2}, Unevaluated] /. {HoldPattern[z] :> x, HoldPattern[y] :> y, HoldPattern[x] :> z}, Extract[gga, {2}, Unevaluated] /. {HoldPattern[x2] :> x, HoldPattern[y2] :> y, HoldPattern[z2] :> z}} Function[{x, y, z}, (Print["high"]; x*z^2) * (Print["there"]; Cos[y]*z)] /comment) Now the simple expression ... ffgga2 = Function[{x, y, z}, ffa[x, y, z] gga[x, y, z]] Function[{x, y, z}, ffa[x, y, z] gga[x, y, z]] ... certainly keeps the semantics ffgga2[x, y, z] "high" "there" 27 Cos[2] (the only difference to ffgga is, that this won't work any longer after the definitions of ffa or gga have been removed) Now clearly for "pure" functions fa = (Print["high"]; #1*#3^2) & (Print["high"]; #1*#3^2) & ga = (Print["there"]; Cos[#2]*#3) & (Print["there"]; Cos[#2]*#3) & ... fga2 = Function[{x, y, z}, fa[x, y, z] ga[x, y, z]]; fga2[x, y, z] "high" "there" 27 Cos[2] continues to work; but also the direct method (the variables are Slot[1], Slot[2], etc. and the body is in the first part): fga = Function[{x, y, z}, Times[#1, #2]] & @@ ({Extract[fa, {1}, Unevaluated], Extract[ga, {1}, Unevaluated]} /. {#1 :> x, #2 :> y, #3 :> z}) fga[x, y, z] "high" "there" 27 Cos[2] (comment: of course this could then be simplified to fga = Function @@ Hold[Times[#1, #2]] & @@ {Extract[fa, {1}, Unevaluated], Extract[ga, {1}, Unevaluated]} (Print["high"]; #1*#3^2) * (Print["there"]; Cos[#2]*#3) & /comment) Your solution however has a problem fg = Function[Evaluate[fa ga /. Function -> Identity]] "high" "there" Cos[#2]*#1*#3^3 & fg[x, y, z] 27 Cos[2] so you loose something! But I wanted to pick up your idea, so this repairs: fga = ReleaseHold[Evaluate[fa * ga /. Function -> Hold] &] (Print["high"]; #1*#3^2)*(Print["there"]; Cos[#2]*#3) & fga[x, y, z] "high" "there" 27 Cos[2] And for fa * ga you should be able to insert any (algebraic) expression containing pure functions. The more serious drawback seems to be that lambda expressions cannot be treated that way. This can be repaired too: Off[Pattern::"patv"] makeFunFromExpr[expr_] := ReleaseHold[ Evaluate[expr /. {Function[({var__} | var_), body_] :> Block[{var}, Hold[body] /. Thread[Rule[{var}, Array[Slot, {Length[{var}]}]]]], Function -> Hold}] &] ffga = makeFunFromExpr[ffa ga] (Print["high"]; #1*#3^2)*(Print["there"]; Cos[#2]*#3) & {ffa, ga} {Function[{z, y, x}, Print["high"]; z*x^2], (Print["there"]; Cos[#2]*#3) & } ffga[x, y, z] "high" "there" 27 Cos[2] Kind regards, your Hartmut Wolf