/** 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. */ makeAnonymousFunction[args, body] := { args = makeSymbols[args] return ConstructExpression["Function", [args, body]] }