/** This class contains functions for working with parts of a function, or
transforming functions, like taking their derivatives.
See functionUtilsTest.frink for an example of its usage.
*/
/** This returns the body of a function as an expression.
The argument passed in can be a named function (a FunctionDescriptor) or
an anonymous function (Function).
For functions defined in Java, there will be no child 1 and this will
return undef.
*/
functionBody[f] :=
{
func = undef
if type[f] == "FunctionDescriptor" // A function with a name. Child 0 is name
func = getChild[f,1] // Child 1 is Function
else
if type[f] == "Function"
func = f
// This returns the actual body of the function. For functions defined
// in Java, there will be no child 1.
if childCount[func] >= 2
return getChild[func,1]
else
return undef
}
/** This returns the name of a function as a string, if it has one, otherwise
returns undef.
The argument passed in can be a named function (a FunctionDescriptor) or
an anonymous function (Function)
*/
functionName[f] :=
{
if type[f] == "FunctionDescriptor" // A function with a name.
return getChild[f,0] // Child 0 is name
else
return undef
}
/** This returns an array of function argument names as strings. */
functionArguments[f] :=
{
func = undef
if type[f] == "FunctionDescriptor"
func = getChild[f,1] // Child 1 is Function
else
if type[f] == "Function"
func = f
args = getChild[func, 0] // This is an array of FunctionArguments
retval = new array
for arg = args
retval.push[getChild[arg, 0]] // Argument name as string
return retval
}
/** This returns an array of function arguments as Symbols. */
functionArgumentsAsSymbols[f] :=
{
retval = new array
for arg = functionArguments[f]
retval.push[makeSymbol[arg]]
return retval
}
/** This returns the number of function arguments as an integer. */
argumentCount[f] :=
{
func = undef
if type[f] == "FunctionDescriptor"
func = getChild[f,1] // Child 1 is Function
else
if type[f] == "Function"
func = f
return length[getChild[func, 0]] // This is an array of FunctionArguments
}
/** Create a symbol with the name of the given string. */
makeSymbol[s] :=
{
if type[s] == "Symbol"
return s
else
return constructExpression["Symbol", [s]]
}
/** Create an array of symbols from an array of arguments. */
makeSymbols[list] :=
{
retval = new array
for arg = list
retval.push[makeSymbol[arg]]
return retval
}
/** Create a derivative expression of the specified function. It currently
assumes that the function only has one argument.
This will create a function call expression of the form:
D[expr, symbol]
Which can then be passed to transformExpression[]
(once you have loaded a file like derivatives.frink) to actually
symbolically evaluate the derivative. At the moment, we don't do that in
this library so as not to create a dependency on a file that may change.
This, of course, will not behave properly if the function body is not
simple and directly differentiable.
*/
makeDerivative[f, times=1] :=
{
body = functionBody[f]
if body != undef
makeDerivativeFunction[body, functionArgumentsAsSymbols[f]@0, times]
else
{
// This function was defined in Java probably. It may not have argument
// names.
argSym = makeSymbol["arg1"]
funcCall = constructExpression["FunctionCall", [functionName[f], argSym]]
return constructExpression["FunctionCall", ["D", funcCall, argSym, times]]
}
}
/** Create a Derivative function of the specified expression and symbol to
take the derivative with respect to. This will create a function call
expression of the form:
D[expr, symbol]
Which can then be passed to transformExpression[]
(once you have loaded a file like derivatives.frink) to actually
symbolically evaluate the derivative. At the moment, we don't do that in
this library so as not to create a dependency on a file that may change.
REMINDER: You may need to wrap expr or symbol in a noEval[] block if
passing in a literal expression
*/
makeDerivativeFunction[expr, symbol, times=1] :=
{
symbol = makeSymbol[symbol]
return constructExpression["FunctionCall", ["D", expr, symbol, times]]
}
/** Create a integral expression of the specified function. It currently
assumes that the function only has one argument.
This will create a function call expression of the form:
Integrate[expr, symbol]
Which can then be passed to transformExpression[]
(once you have loaded a file like integrals.frink) to actually
symbolically evaluate the integral. At the moment, we don't do that in
this library so as not to create a dependency on a file that may change.
This, of course, will not behave properly if the function body is not
simple and directly integrable.
*/
makeIntegral[f] :=
{
body = functionBody[f]
if body != undef
makeIntegralFunction[body, functionArgumentsAsSymbols[f]@0]
else
{
// This function was defined in Java probably. It may not have argument
// names.
argSym = makeSymbol["arg1"]
funcCall = constructExpression["FunctionCall", [functionName[f], argSym]]
return constructExpression["FunctionCall", ["Integrate", funcCall, argSym]]
}
}
/** Create an Integral function of the specified expression and symbol to
take the derivative with respect to. This will create a function of the
form:
Integrate[expr, symbol]
Which can then be passed to transformExpression[]
once you have loaded a file like integrals.frink
REMINDER: You may need to wrap expr or symbol in a noEval[] block if
passing in a literal expression
*/
makeIntegralFunction[expr, symbol] :=
{
if type[symbol] == "String"
symbol = makeSymbol[symbol]
return constructExpression["FunctionCall", ["Integrate", expr, symbol]]
}
/** Make a solve function with the specified left and right hand side and
variable to solve for.
In other words, this makes something that looks like:
solve[left === right, x]
Which can then be passed to transformExpression[]
once you have loaded a file like solvingTransformations.frink
REMINDER: You may need to wrap each argument into a noEval[] block if
passing in a literal expression
*/
makeSolve[left, right, variable] :=
{
// Create the === part
solve = constructExpression["Solve", [left, right]]
return constructExpression["FunctionCall",
["solve", solve, makeSymbol[variable]]]
}
/*
Make a solve function with the specified equation (in either the form
left = right or the (better) left === right, and the variable to solve
for.
In other words, this makes something that looks like:
solve[left === right, x]
Which can then be passed to transformExpression[]
once you have loaded a file like solvingTransformations.frink
REMINDER: You may need to wrap each argument into a noEval[] block if
passing in a literal expression
*/
makeSolve[equation, variable] :=
{
// Turn any assignments "=" into solve "==="
if type[equation] == "Assignment"
equation = substituteExpression[equation,
noEval[_a = _b], // Note use of pattern
noEval[_a === _b]]
return constructExpression["FunctionCall",
["solve", equation, makeSymbol[variable]]]
}
/** Makes an anonymous function given a list of arguments
(which can be specified as either string names or Symbols.) and a body.
REMINDER: You may need to wrap each argument into a noEval[] block if
passing in a literal expression.
*/
makeAnonymousFunction[args, body] :=
{
args = makeSymbols[args]
return ConstructExpression["Function", [args, body]]
}