NeturalMath Quick Start Guide

Part 8: Creating Custom Functions

This document is not complete, and my represent features which are not yet implemented in the current version of NeturalMath.

In the previous section, we learned how it is possible to call built-in system functions. In this section, we will learn how to create new custom functions for use within our own NeturalMath programs. Declaring a function is much like declaring a variable, except that we add parenthesis and the names of any parameters that we wish to use inside the function. The example below shows how to create and call a custom function called "demo".
demo() = 2
print demo()

This example shows the simplest possible case for creating a custom function, however, the function is not very useful. We would be better off creating a variable named demo instead, or just using the constant 2 when we need it. A better example is a case where the function performs some type of repeatable behavior or returns a value based on a parameter. Parameters must follow the same naming rule as all other symbols. They may only consist of numbers and letters and must start with a letter. They are case-sensitive for purposes of identification.
addTwo(x) = x + 2
print addTwo(3)  // prints 5
y = addTwo(10)
print y = prints 12

Unlike a variable whose value may be set over and over, once a function is defined, its definition may not be changed. If you attempt to redefine a function which has already been defined elsewhere, you will get an error.
myFunc(a) = a + 2
// try to redefine and it throws an error
myFunc(a) = a + 5

Functions can have as many parameters as needed to perform the operation. The function below represents the solution for x to a quadratic equation a*x^2+b*x+c where a, b and c are all parameters.
quad(a,b,c) = (-b+^/(b^2-(4*a*c))) / (2 * a)

Providing Default Parameter Values

There may be cases in your programs where it may not make sense to always specify every parameter in a function. In this case, it may be desirable to allow certain parameters to be optional. It is possible to make parameters optional by providing a default value for the parameter. Once a parameter has been declared as optional, the function may be called without providing a value for the parameter. Providing a default value for a parameter is as simple as assigning it a value in the function declaration. The function below determines the velocity of an object that has been falling for a given period of time (without air resistance). It presumes that the majority of the time, we are going to assume that we are working in Earth-standard gravity (9.8 m/s), but will allow us to enter in alternative values for other planets. The rate of acceleration due to gravity is a well-known formula:
fall(time,gravity=9.8) = gravity * time ^ 2

print fall(10)  // falling in Earth gravity for 10 seconds
print fall(10,1.6) // falling for 10 seconds on the moon


Optional parameters must fall after any required parameters (they must be at the end of the list). Attempting to define an optional parameter before a required parameter will result in an error, as shown below:
// this will return an error
broken(a = 4, b) = a + b 

You may have cases where a parameter is optional, but there is no logical default value for it to fall back on. In other words, you may have a parameter that you wish to use if it has a value, but do not wish to use if it does not. In this case, you may wish to use the Void Keyword as the default value. More information on the void keyword and its usage will be covered in later tutorials.

Multiline Functions

So far in our tutorials, we have only covered expressions that fit on a single line. It is possible to bundle expressions together to create larger, multiline statements. This may be done in order to make the function easier to read, or it may be done because there are multiple paths or alternatives that can be taken in the function. We use curly braces ({})to signify that we want to make a function multi-line. Each line within the function will be evaluated every time the function is called. The curly braces help "hold everything together" and signifies that all of the statements are a related group. We can take the quadratic example from above and make it easier to read, as shown below. Notice how MathConsole will indent the input cursor (:>) once you type the curly brace in. This is to indicate that the statements inside the curly braces are in a different scope. Scoping will be covered in much greater detail in a later tutorial.
quad(a,b,c) = {
  top = -b+^/(b^2-(4*a*c))
  bottom = 2 * a
  return $top / $bottom
  }

The return keyword

Notice how the last line of code in our function example above has the Return Keyword? Remember from a few lessons back that each expression either does, or does not return a value. It would be possible to forgo the return statement and just use the expression top / bottom, but this makes the code less readable, and can yield some unexpected results as well. Expressions which return a value, like the last line above will be printed to the MathConsole window when they are executed in addition to being passed along as the result of evaluating the expression. Try the example below to get a better understanding of how this can be a problem:
bad(a) = {
  a + 2
  } 

bad(1)  // causes the console to print 3
x = bad(10)
print x // prints 12 twice

print bad(8) // prints 10 twice

Why does it only print once when the function bad() is called by itself, but twice in the other two cases? The reason is the same reason why typing 2+2 into the command window prints out 4. Whenever a line of code performs a computation that returns a value, but is not assigned anywhere, that value is sent to the console for output. It is only in the case of setting a variable when a value (or expression) is passed into the variable instead of the console. Functions will try to evaluate every line of code within the function body. The last line of code to return a value gets used as the output value of the function. Because of the way that the statement is written, it passes the value as the return value of the function, but also prints it as output. Another way of looking at this is that the function above has the same behavior as writing this:
bad(a) = {
  print a + 2
  return a + 2
  }

The return keyword serves two purposes. First, it suppresses the annoying double-print problem that can be created by improperly written expressions, and second it forces the execution of the function to stop and the value at the return statement to be used as the value of the function. This is needed when a function may have more than one branch. Branching logic and looping logic will be explained in a later tutorial, but for now, this example should illustrate how to return a value from a branch within a function that returns a positive one if the provided value is positive, or a negative one if the value is less than zero:
branch(n) = {
  if n >= 0 return 1
  else return 0
  }

Manipulating Functions with Symbols

Just like with variables, we can use symbolic notation to manipulate function definitions. The simplest use case for this is cloning a function. More advanced uses include extending a function or passing the function definition into another function.
myFunc(x) = x * 10

// clone a function
clone(x)  = $myFunc

// extend a function
ext(x) = $myFunc + 5


Thus far this guide has provided examples of how to create perform many kinds of math operations, create variables and functions, and manipulate them symbolically. These concepts provide the building blocks of NeturalMath applications. The next two sections in this tutorial will discuss how to use MathConsole to create, edit and run your own programs. Creating Programs in MathConsole

Last edited Nov 29, 2010 at 3:49 PM by zanethorn, version 7

Comments

No comments yet.