functionUtils.frink

Download or view functionUtils.frink in plain text format


/** 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]]
}


Download or view functionUtils.frink in plain text format


This is a program written in the programming language Frink.
For more information, view the Frink Documentation or see More Sample Frink Programs.

Alan Eliasen was born 19944 days, 17 hours, 4 minutes ago.