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, 10 hours, 50 minutes ago.