Variables and Types
Variables and types
Like many languages, Morpho allows the programmer to define
variables to contain pieces of information of values. A variable is
created using the var keyword, which is followed by the variable name
var a
Variable names must begin with an alphabetical character or the
underscore character _, and may consist of any combination of
alphanumeric characters or underscores thereafter. Variable names are
case sensitive, so
var a
var A
each refer to distinct variables.
After creating a variable, you may immediately store information in it by providing an initializer, which can be any value
var i = 1
var str = "Hello"
Types
Morpho is a dynamically typed language: Every value has a definite type, and Morpho is always able to tell what type it is, but variables may generally contain values of any type and functions or methods can accept arguments of any type.
There are a number of basic types in Morpho:
nil
: is a special value that represents the absence of information and
is different from any other value. Unless an initializer is
provided, Morpho variables initially contain nil after
declaration.
Bool
: values contain either true or false.
Int
: values contain 32 bit signed integer numbers. An integer constant is
written intuitively, e.g. 1, 50, 1000 and may include a
negative sign -100.
Float
: values contain double precision floating point numbers. You can
write numeric constants either using a decimal 1.5 or in
scientific notation, e.g. 1e10, 1.6e-19 or 6.625e26.
In addition to these basic types, Morpho provides a rich collection of objects. Discuss immutability i.e. cannot be changed after creation, or not
Strings
Strings are sequences of unicode UTF8 encoded characters. You specify a literal string using double quotes
var h = "Hello"
Literal strings can contain a number of special characters, which are introduced by a backslash character as in the table below
| Code | Character |
|---|---|
\f |
Form feed |
\n |
Newline |
\r |
Carriage return |
\t |
Tab |
\u |
Unicode character (followed by 4 hex digits) |
\U |
Unicode character (followed by 8 hex digits) |
\x |
Unicode character (followed by 2 hex digits) |
\\ |
Backslash |
\" |
Quote |
To create a string incorporating the morpho butterfly emoji, for example, you would use
"\U0001F98B morpho"
Hex digits are not case sensitive.
You may also insert the results of arbitrary expressions (see Chapter Expressions) into a Morpho string using interpolation as in the following example
print "Hello ${name}! Happy ${age} birthday"
The values of the variables name and age are converted to strings if
necessary and joined with the surrounding string.
To convert something to a string, use the String constructor
print String(1.5)
Note that Strings in Morpho are immutable. Their contents cannot be changed, and operations on strings always return a new String:
print "ABC" + "DEF"
Lists
Lists are ordered sequences of values. They can be created either through the syntax
var a = [1,2,3]
or using the List constructor
var a = List(1,2,3)
which converts its arguments into a List.
Elements of a list can be accessed using the index notation
print a[0] // Prints the first element
Note that, like all Morpho collection objects, the index 0 represents
the first element. You can change the value of any element in the List
using analogous notation
a[0] = 10
You can access the last element using -1
print a[-1] // Prints the last element
and, more generally, negative numbers can be used to count from the end of the List.
Stacks
One application of a List is to implement a stack, which is a data structure to which values can be added to or removed from, following a Last In, First Out (LIFO) protocol.
We create the stack using an empty List
var stack = []
and can then implement key stack operations as follows:
-
Push elements onto the stack:
stack.append(1,2,3) -
Pop the last element off the stack:
print stack.pop() -
Peek at the last element of the stack without removing it:
print stack[-1] -
Check if the stack is empty:
if (stack.count()==0) { // Do something }
We can use these basic operations to implement more complex operations, such as duplicating the last element of the stack
a=stack.pop()
stack.append(a,a)
or swapping the top two elements
a=stack.pop()
b=stack.pop()
stack.append(a,b)
Arrays
Arrays are multidimensional stores, and can be created as part of a variable declaration. Both of these examples create a \(2\times2\) array:
var a[2,2]
or by a constructor
var a = Array(2,2)
Use index notation to set and get individual elements
a[0,0]=1
print a[0,0]
Each array element can contain any content that a regular variable can, so you may store Strings, Lists and even other Arrays in an Array:
a[0,0]="Hello"
a[1,0]=[1,2,3]
All Array elements are initialized to nil by default.
Dictionaries
Dictionaries, also called hashtables, hashmaps or associative arrays, are data structures that map a set of keys to a set of values. This simple example maps a few state codes to their capitals:
var cap = { "MA" : "Boston", "NY" : "Albany", "VT": "Montpellier" }
You could also use the Dictionary constructor
var cap = Dictionary("MA", "Boston", "NY", "Albany", "VT", "Montpellier")
where the arguments are, alternatingly, keys and values.
Access the contents of a Dictionary using index notation
print cap["MA"]
which also allows you to add additional key-value pairs
cap["ME"]="Augusta"
Any value can be used as a key in a Dictionary, but there is a subtlety: the behavior depends on whether the object is immutable. Basic types are immutable, and hence integers work as expected
var a = { 0: "Zero", 1: "One", 2: "Two"}
print a[0]
Lists, however, are
Ranges
Ranges describe a collection of values, denoted using the .. and
.... The Range
1..10
describes the collection of integers from 1 to 10 inclusive; the Range
1...10
(note the triple dots) represents the integers from 1 to 9; the upper bound is excluded. You can represent a range with an increment other than 1 like so
1..9:2
which comprises the odd numbers from 1 to 9. Ranges work with floating point numbers, e.g.
0..1:0.1
Ranges are frequently used to define the bounds of loops as will be described in Section X.
Complex numbers
Morpho supports complex numbers, which can be created using the keyword
im to denote the imaginary part of a complex number
var z = 1 + 1im
You can use any number format for either the real or imaginary parts
print 0.1im
print 1e-3im
Complex numbers work with arithmetic operators just like regular numbers
print z + 1/z
Get the real and imaginary parts of a complex number using the
convenience functions real and imag
print real(z)
print imag(z)
or by calling the corresponding methods
print z.real()
print z.imag()
Find the complex conjugate
print z.conj()
and obtain the magnitude and phase
print z.abs() // Or use the regular abs() function
print z.angle()
Note that the value \(\phi\) returned by the angle method always lies on
the interval \(-\pi<\phi\le\pi\); in some applications you will need to
track the correct Riemann surface separately and add multiples of \(2\pi\)
as appropriate.
Tuples
Tuples, like Lists, represent an ordered sequence of values but are immutable. They are created either using the syntax
var t = (1,2,3)
or using the Tuple constructor
var t = Tuple(1,2,3)
Elements can be accessed using index notation
print t[1] // Prints the second element
but an attempt to change them throws an error
t[0] = 5 // Throws 'ObjImmutable'
Because Tuples are immutable, they can be used as keys in a Dictionary:
var dict = { (0,0): true }
print dict[(0,0)] // expect: true