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