Equations and Formulae
Many primitives allow you to enter equations that determine their values or behavior. To access the equation editor for a primitive, hover your mouse over the primitive and click the "=" sign that appears. You can also use the configuration panel on the right side of the main window to edit the equation when the primitive is selected.
The equation editor allows you to type in an equation. On the bottom of the editor are a list of functions built into Insight Maker. If you hover over a function, a tooltip will describe what it does. On the right side of the editor are a list of other primitives this primitive can reference. You can click on one of these to insert it into the equation.
These equations can be arbitrary mathematical or programming expressions. For instance, take the example of the following equation:
Sin(Years()*2) + [Rain Flow]^0.05
This example displays a number of features that the Insight Maker equation engine supports:- ‘Years’ refers to the current simulation time as measured in years.
- ‘[Rain Flow]’ refers to the value of another primitive. Primitives can reference each other using this square-bracket notation.
- ‘Sin’ applies the trigonometric sine function.
- ‘+’, ‘*’, and ‘^’ are all standard mathematical operators representing addition, multiplication and exponentiation respectively.
Built-In Functions
In addition to standard algebraic and logical operators, Insight Maker has many powerful built-in functions. The following is a list of these functions along with descriptions and sample usages.
Advanced Equations
It would be useful to discuss some of the more advanced programming constructs that Insight Maker's equation engine supports. These advanced constructs are particularly useful for developing complex logic for Macros or Agent Based Modeling.
Insight Maker allows the definition and modification of variables (in the programatic sense, not in sense of "Variable" primitives) using the "<-" operator. For instance, the following code will create two variables — x and xSquared — which will be 20 and 100 respectively at the end of the code's evaluation.
Comments
Insight Maker supports several forms of comments. Comments are parts of equations that will not be evaluated by the equation engine:
Insight Maker supports several forms for function definition. One is a short form that is shown below:
In addition to Insight Maker's standard ifThenElse() function, a multi-line form is also supported:
While Loops
While loops allow the repetition of expressions multiple times until as long as a logical condition is satisfied. For example:
For loops repeat some code a fixed number of times. For instance:
A return statement may be used to return values from functions or equations. If a return statement is not used, the last evaluated expression will automatically be returned.
For example:
Insight Maker uses an exception mechanism to handle errors. For instance, say you attempted to access an element of a vector that does not exist:
Supposed the getVector function returns a vector without any elements. The mean of an empty vector is undefined and Insight Maker throws an exception when this occurs. Normally, this would terminate your simulation. However, when an exception occurs in a Try-Catch block, the exception can be "caught" by the second part of the block.
If an exception occurs in our example, the exception is assigned to the variable immediately following the Catch. In this case the variable is err and if we tried to take the mean of an empty vector, err might be assigned the string "Must have at least one element to determine the mean of a vector.". Next the code following the Catch is executed. In our example here, this code is simple and just returns 0, but it could be more complex. Thus, our illustrative example attempts to calculate the mean of a vector; and if it cannot calculate the mean, it returns 0 instead.
In addition to Insight Maker's many built-in exceptions you can also create your own exceptions using the Throw keyword. For example we could do the following:
Destructuring Assignment
Insight Maker supports destructuring assignment. Though the name is fancy, the concept is simple. Basically, destructuring assignment provides a straightforward way to assign the elements of a vector to a set of variables. For example:
For instance, we could take the built in Mean() function, assign it to a variable and then apply it to a set of numbers:
Generally, when programming a function is given a name when it is created and it can later be referred to using that name. In functional programming, a key tool are anonymous functions: functions created without a name. Anonymous functions are defined much the same way as regular functions, but without an explicit name. For instance, the following creates an anonymous function and assigns it to the variable f:
Closure is a key tool for functional programming. Closure is a bit of a technical concept which basically means that functions declared within a scope continue to have access that scope even after it has been released. Let us look at an example that uses closure to generate counter functions:
Thus we can continue to use the countTally variable in this anonymous function when we call it later on. It is effect now a private variable that only the generated function can access. A new countTally variable is created for each call of MakeCounter, so we can keep track of separate counts. Closure is a powerful tool that has many uses for complex programs
The Elegance of Functional Programming
Functional programming techniques are really quite elegant for many practical programming uses. Take the following example which implements the Lotka-Volterra predator prey model using Insight Makers programming features. Euler's method is used to solve the differential equations. Due to the elegance of functional programming, once we have defined our system, the entire differential equation solver requires just a single loop containing only two lines of code!
Creating Objects
Object are based on named vectors. Let's take the following instance of a named vector as an example:
Unlike some object-oriented languages, in Insight Maker each object is both a fully usable object and also a class definition other objects can inherit from. We use the new keyword to create instances of existing objects.
For example, we could create two new people objects like so:
Constructors are functions that are called when a new instance of a class is created. Constructors are created by defining a property "constructor" in the object definition. If a constructor is available it will be called when the a new instance of an object is created. For instance, the following is a constructor that makes it easy to create people with a given name:
All classes in Insight Maker are fully dynamic. This means that you can add properties to the class definition and all instances (current and future) of that class will immediately have access to those properties. This is sometimes known as "Monkey Patching". This capability can be used to extend to Insight Maker's internal classes. For instance, all strings in Insight Maker inherit from the StringBase object and all vectors inherit from the VectorBase object. We can modify these objects to extend Insight Maker functionality.
As an example, let's extend Insight Maker strings with a Reverse() function. Such a function is not built into Insight Maker, but it might be nice to have. Let's give it a try:
The object an instance inherits from is known as its "Parent". A variable by this name is available in the object's functions in order to obtain a reference to the object's parent. This is especially useful for stringing constructors together. For instance, we may want to create a Student subclass of Person which calls its parent's constructor:
Basic Programming
Defining VariablesInsight Maker allows the definition and modification of variables (in the programatic sense, not in sense of "Variable" primitives) using the "<-" operator. For instance, the following code will create two variables — x and xSquared — which will be 20 and 100 respectively at the end of the code's evaluation.
x <- 10 xSquared <- x^2 # xSquared = 100 x <- x*2 # x = 20Insight Maker uses block scoping so variables first declared within a block will not be accessible outside that block.
Comments
Insight Maker supports several forms of comments. Comments are parts of equations that will not be evaluated by the equation engine:
- "#" will comment out the rest of the line:
1+2^3 # this is ignored
- "//" will comment out the rest of the line:
1+2^3 // this is ignored
- "/*" and "*/" will comment out a region:
1+/* this is ignored */2^3
Insight Maker supports several forms for function definition. One is a short form that is shown below:
myFn(a, b, c) <- sin((a+b+c)/(a*b*c))And the second is a longer form that can create multi-line functions:
function myFn(a, b, c) numerator <- a+b+c denominator <- a*b*c sin(numerator/denominator) # the last evaluated expression is returned from a function, you may also use the 'return' statement end functionFunctions also support default parameter values:
takePower(a, b = 2) <- a^b takePower(2, 1) # = 2 takePower(3, 3) # = 27 takePower(3) # = 9If Then Else Statements
In addition to Insight Maker's standard ifThenElse() function, a multi-line form is also supported:
if x > 10 then # do something else if x < 5 then # do something else else # some other action end ifYou may have as many "else-if"clauses as desired and the final "else" clause is optional.
While Loops
While loops allow the repetition of expressions multiple times until as long as a logical condition is satisfied. For example:
x <- 10 while x < 20 x <- x +x/10 end loop x # = 21.436For Loops
For loops repeat some code a fixed number of times. For instance:
total <- 0 for x from 1 to 10 total <- total + x end loop total # = the sum of the numbers from 1 to 10An optional "by" control parameter can be specified to change the step to some other value than one:
total <- 0 for x from 1 to 9 by 2 total <- total + x end loop total # = the sum of the odd numbers from 1 to 9A special form of the for loop, the for-in loop, exists for iterating across all elements of a vector:
total <- 0 for x in {1, 2, -5, 10} total <- total + x end loop total # sums up the elements in the vectorReturning Values
A return statement may be used to return values from functions or equations. If a return statement is not used, the last evaluated expression will automatically be returned.
For example:
1+1 2+2 3+3 # 6 will be returned by this equationOr:
1+1 return 2+2 # 4 will be returned by this equation 3+3 # this won't be evaluatedOr:
if 10 > 20 then 1*2 else 2*2 # 4 is returned from both the if-statement and then returned for the expression overall end ifError Handling
Insight Maker uses an exception mechanism to handle errors. For instance, say you attempted to access an element of a vector that does not exist:
{1, 4, 9}{5} # There is no element '5' in the vector!When this occurs, Insight Maker throws what is called an "exception". An exception is basically an error that will propagate up through the model ultimately aborting the simulation unless something handles the error. Handling the error is known as "catching" the error. You can catch errors in your equations using a Try-Catch block. An example of a Try-Catch block is below:
Try x <- getVector() mean(x) Catch err 0 End TryWhat this equation does is to first attempt to execute the code finding the mean of the vector. If that code executes successfully, everything between the Catch and the End Try are skipped and not evaluated. But, something very interesting happens if an error occurs when we attempt to calculate the mean.
Supposed the getVector function returns a vector without any elements. The mean of an empty vector is undefined and Insight Maker throws an exception when this occurs. Normally, this would terminate your simulation. However, when an exception occurs in a Try-Catch block, the exception can be "caught" by the second part of the block.
If an exception occurs in our example, the exception is assigned to the variable immediately following the Catch. In this case the variable is err and if we tried to take the mean of an empty vector, err might be assigned the string "Must have at least one element to determine the mean of a vector.". Next the code following the Catch is executed. In our example here, this code is simple and just returns 0, but it could be more complex. Thus, our illustrative example attempts to calculate the mean of a vector; and if it cannot calculate the mean, it returns 0 instead.
In addition to Insight Maker's many built-in exceptions you can also create your own exceptions using the Throw keyword. For example we could do the following:
x <- getVector() If x.length() = 0 then throw "The length of x must be greater than 0." end ifYour custom exceptions will be handled just like regular Insight Maker exceptions.
Destructuring Assignment
Insight Maker supports destructuring assignment. Though the name is fancy, the concept is simple. Basically, destructuring assignment provides a straightforward way to assign the elements of a vector to a set of variables. For example:
x, y <- {10, 20} x # = 10 y # = 20
Functional Programming
Functional programming is a approach to programming that focuses on the use of functions rather than variables and procedural logic. Insight Maker supports functional programming as its functions are first class objects that can be created on the fly, assigned to variables, and returned as the results of other functions.For instance, we could take the built in Mean() function, assign it to a variable and then apply it to a set of numbers:
myFunction <- mean
myFunction(1, 2, 3) # = 2
Similarly, we could use the Map function with a vector of functions to calculate summary statistics for a set of data values:{Min, Median, Max}.Map(x(7, 5, 8, 1, 6)) # = {1, 6, 8}Anonymous Functions
Generally, when programming a function is given a name when it is created and it can later be referred to using that name. In functional programming, a key tool are anonymous functions: functions created without a name. Anonymous functions are defined much the same way as regular functions, but without an explicit name. For instance, the following creates an anonymous function and assigns it to the variable f:
f <- function(x,y) Sqrt(x^2+y^2) end function f(3, 4) # = 5There is also a shorthand syntax available for single line anonymous functions:
f <- function(x,y) Sqrt(x^2+y^2) f(3, 4) # = 5Anonymous functions are very useful when using functions like Map() and Filter(). For instance:
{1, 2, 3}.map(function(value) cos(value^2) end function)Closure
Closure is a key tool for functional programming. Closure is a bit of a technical concept which basically means that functions declared within a scope continue to have access that scope even after it has been released. Let us look at an example that uses closure to generate counter functions:
function MakeCounter() countTally <- 0 function() countTally <- countTally+1 countTally end function end function c1 <- MakeCounter() c2 <- MakeCounter() {c1(), c1(), c2(), c2(), c1()} # = {1, 2, 1, 2, 3}Looking at this code we should recognize that countTally is a local variable to the MakeCounter function. Once the function is complete, the countTally variable goes out of scope and we cannot access it outside the function. However, due to closure, the function we declare within the MakeCounter function still has access to the countTally variable even after MakeCounter has finished
Thus we can continue to use the countTally variable in this anonymous function when we call it later on. It is effect now a private variable that only the generated function can access. A new countTally variable is created for each call of MakeCounter, so we can keep track of separate counts. Closure is a powerful tool that has many uses for complex programs
The Elegance of Functional Programming
Functional programming techniques are really quite elegant for many practical programming uses. Take the following example which implements the Lotka-Volterra predator prey model using Insight Makers programming features. Euler's method is used to solve the differential equations. Due to the elegance of functional programming, once we have defined our system, the entire differential equation solver requires just a single loop containing only two lines of code!
state <- { Predator: 20, Prey: 560 } derivatives <- { Predator: function(state) 0.0002*state.Prey*state.Predator-0.25*state.Predator, Prey: function(state) 0.25*state.Prey-0.008*state.Predator*state.Prey } startTime <- 0 endTime <- 20 timeStep <- 1 for t in startTime:timeStep:(endTime-timeStep) slopes <- derivatives.map(x(state)) state <- state + slopes*timeStep end loop alert(state)
Object Oriented Programming
Object oriented programming is a technique where objects are defined in a program. These objects are generally collections of properties and functions that may manipulate the object or carry out some behavior based on the object's state. Insight Maker's equation engine supports what is known as prototype-based object oriented programming. This is a flexible and powerful technique for building programs using objects.Creating Objects
Object are based on named vectors. Let's take the following instance of a named vector as an example:
Person <- { firstName: "John", lastName: "Smith" }This "object", which is what we refer to named vectors as in this section, represents a person named John Smith. We can access this person's first and last name using the following syntax:
Person.firstName # = "John" Person.lastName # = "Smith"Since Insight Maker's equation engine is a functional language with first class functions, we can also assign functions to the properties of this object. For instance, we could add a function to return the person's full name:
Person <- { firstName: "John", lastName: "Smith", fullName: function() "John Smith" end function }We would then obtain the full name of the person object like so:
Person.fullName() # = "John Smith"However, this function we wrote is not very smart. Our object already has all the information needed to find the person's full name, so repeating the name in the function is redundant. We can do better than this. To do so, we use a special variable: Self. When used in an object's function, Self refers to the object itself. Using this knowledge, we can rewrite our full name function to be smarter. In this new form, the full name function will give the correct full name even if we later change the object's first or last name.
Person <- { firstName: "John", lastName: "Smith", fullName: function() self.firstName+" "+self.lastName end function }Inheritance
Unlike some object-oriented languages, in Insight Maker each object is both a fully usable object and also a class definition other objects can inherit from. We use the new keyword to create instances of existing objects.
For example, we could create two new people objects like so:
chris <- new Person chris.fistName <- "Chris" john <- new Person chris.firstName # = "Chris" john.firstName # = "John"You can also create multiple levels of inheritance. For instance, imagine we wanted to create a Student class. The Student class will be a subclass of the person class with two new properties: school and grade.
Student <- new Person Student.grade <- 10 Student.school <- "Midfield High" chris <- new Student # Chris is both a Student and a PersonConstructors
Constructors are functions that are called when a new instance of a class is created. Constructors are created by defining a property "constructor" in the object definition. If a constructor is available it will be called when the a new instance of an object is created. For instance, the following is a constructor that makes it easy to create people with a given name:
Person <- { firstName: "John", lastName: "Smith", fullName: function() self.firstName+" "+self.lastName, constructor: function(first, last) self.firstName <- first self.lastName <- last end function } chris <- new Person("Chris", "McDonald") chris.fullName() # = "Chris McDonald"Monkey-Patching
All classes in Insight Maker are fully dynamic. This means that you can add properties to the class definition and all instances (current and future) of that class will immediately have access to those properties. This is sometimes known as "Monkey Patching". This capability can be used to extend to Insight Maker's internal classes. For instance, all strings in Insight Maker inherit from the StringBase object and all vectors inherit from the VectorBase object. We can modify these objects to extend Insight Maker functionality.
As an example, let's extend Insight Maker strings with a Reverse() function. Such a function is not built into Insight Maker, but it might be nice to have. Let's give it a try:
StringBase.reverse <- function() self.range(self.length():1) end function "Test".reverse() # = "tseT"Parent
The object an instance inherits from is known as its "Parent". A variable by this name is available in the object's functions in order to obtain a reference to the object's parent. This is especially useful for stringing constructors together. For instance, we may want to create a Student subclass of Person which calls its parent's constructor:
Student <- new Person("","") Student.grade <- 10 Student.school <- "Midfield High" Student.constructor <- function(first, last, grade, school) self.grade <- grade self.school <- school parent.constructor(first, last) end function
No hay comentarios:
Publicar un comentario