/** 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] := { 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]] } /** Makes an anonymous function given an expression with one symbol which makes up the body of the function. The single symbol is turned into the argument. If the function has any number of symbols not equal to 1, this returns the special value undef. This is useful when writing an argument to a function like select. For example, this turns x^2 into the not-as-easy-to-type {|x| x^2} REMINDER: You may need to wrap each argument into a noEval[] block if passing in a literal expression. */ makeSingleArgumentFunctionFromExpression[expr] := { syms = getSymbols[expr] len = length[syms] if len != 1 { println["makeSingleArgumentAnonymousFunction: expression \"" + inputForm[expr] + "\" has $len symbols ($syms) but requires exactly 1 symbol to automatically create a single-argument function."] return undef } else return makeAnonymousFunction[syms, expr] }