An integer is a whole number, i.e. one that has no fractional part. It may be represented in decimal, hexadecimal or binary:
100 (decimal), 0x64 (hexadecimal) and 0b1100100 all have the same value.
name of Type
examples of defining a literal value
default value
Int
let maxNumber be 10
0
Int
variable flags set to 0b1100100
0
Dot methods on an Int
function dot method
on Type
argument Types
return Type
returns
asBinary
Int
(none)
String
string of binary digits representing the argument's integer value
asString
Int
(none)
String
a string of decimal digits representing the argument's integer value
Notes
A literal integer or a named value of Type Int may
always be passed into a function or procedure that is expecting a Float.
The Int type is intended to represent whole numbers
in the range:
Maximum: 253 – 1 (which is 9007199254740991)
Minimum value: – (253 – 1)
If you go greater than the limit given above, the number will be accurate to approximately 17 decimal digits.
Larger numbers will be rounded with zeros at the end, unless they are larger than around 270, when they will automatically be shown
in exponential (scientific) format, as shown in the example.
Example of large numbers
▶ This code calculates 2 raised to the power of 50 through 72, and its output is shown below:
+for n from 50 to 72 step 1variable s set to lambdanasInt => n.asString().length()variable np set to 2^ncall printTab(-s(n), "{n}")call printTab(30 - s(np), "{np} ")end for
Floating point
A floating point number is a decimal number that may have both integer and fractional parts.
It may be written using exponential (scientific) notation, e.g.
120.0 = 1.20e2, and
0.012 = 1.20e-2.
name of Type
examples of defining a literal value
default value
Float
let phi be 1.618033988749895
0.0
Float
variable reading set to 1.1e-10
0.0
Dot methods on a Float
function dot method
on Type
argument Types
return Type
returns
asString
Float
(none)
String
a string representing the argument's floating point value
ceiling
Float
(none)
Int
the first integer larger than or equal to the argument's floating point value
floor
Float
(none)
Int
the first integer smaller than or equal to the argument's floating point value
round
Float
Int
Float
the value rounded to the number of decimal places specified in the argument
Notes
The limits on floating point numbers are:
Maximum value: just over 1.0 × 10308
Minimum value: approximately 5.0 × 10-324
A variable that has been defined as being of Type Float may not be passed as an argument into a method that requires an Int,
nor as an index into a List or an Array even if the variable contains no fractional part.
It may, however, be converted into an Int before passing, using the function floor or ceiling.
If you wish to define a variable to be of Type Float, but initialise it with an integer value, then add .0 on the end of the whole number, as in: variable hour set to 3.0
Any variable or expression that evaluates to an Int may always be used where a Float is expected.
Boolean
A Boolean value is either true or false.
The keywords true and false must be written in lower case.
name of Type
examples of defining a literal value
default value
Boolean
variable done set to true
false
Dot method on a Boolean
function dot method
on Type
argument Types
return Type
returns
asString
Boolean
(none)
String
string "true" if true, "false" if false
String
A String represents text, i.e. a sequence of zero or more characters.
name of Type
examples of defining a literal value
details
default value
String
let title be "Ulysses"
delimited by double quotes characters { }
define an interpolated field, so may not be included as literal characters
"" or empty String
String
let title be 'Iliad'
delimited by single quotes can contain { or } as literal characters,
but cannot include interpolated fields
'' or empty String
String
variable quote set to '"Hello", she said'
" may be used in a string delimited by ' ' may be used in a string delimited by "
Character sets
When typing from the keyboard into a literal string all the basic ASCII characters (0x20 to 0x7e) and' £' can be input.
You cannot use Alt-numeric-keypad to enter accented letters or other special characters in the range 0x80 to 0xff.
You can, however, copy any text from outide Elan and paste it into a string, whether ASCII, extended ASCII or UTF-8 encoded.
To insert a character from the full range, you use its Unicode (codepoint) value, in decimal or hexadecimal,
by means of function unicode:
print "Spanish syntax introduces a question with a {unicode(191)}"
⟶ Spanish syntax introduces a question with a ¿
print "This is an up arrow: {unicode(0x2191)}"
⟶ This is an up arrow: ↑
These examples 'interpolate' the output of function unicode by using curly braces (see Interpolated fields).
Notes
Strings can be built from other strings by concatenation using the plus operator, for example:
print "Hello " + 'world'
⟶ Hello World
A newline may be inserted within a string with \n:
print "Hello\nworld"
When the field is exited, the newline will be shown in the code. The newline will also be applied when the string is printed/
⟶ Hello world
As in most languages, strings are immutable. When you apply any operation or function with the intent of modifying an existing string, the existing string is never modified. Instead, the operation or function will return a new string that is based on the original, but with the specified differences.
There are no 'substring' methods in Elan, because you can always use an index range to get a substring, e.g. s[3..7] gives a string containing the fourth character to the seventh character inclusive of s. Note that the upper bound of the range is exclusive.
See Indexed values.
You can loop through a string picking each of its characters sequentially using the each loop.
The dot methods that can be applied to strings are shown in the table below.
Interpolated fields in strings
Strings that are enclosed by " (double quotes) are automatically interpolated, that is you can insert the values of variables
or simple expressions within the string by enclosing them in curly braces:
If a and b are named values of, say, 3 and 17:
print "{a} times {b} equals {a*b}"
⟶ 3 times 17 equals 51
If velocity and fuel are named values of, say, 3.0 and 50.4:
set message to "LANDED SAFELY AT SPEED {(velocity*100).floor()} FUEL {fuel.floor()}"print message
⟶ LANDED SAFELY AT SPEED 300 FUEL 50
let r be 2.0print "area = {(pi*r*r).round(2)}"
⟶ area = 12.57
Strings that are enclosed by ' (single quotes) cannot have interpolated fields in them.
Dot methods on a String
function dot method
on Type
argument Types
return Type
returns
asString
String
(none)
String
the string itself
asUnicode
String
(none)
Int
the Unicode value of the first character of the string
(To convert a Unicode value into a string, use function unicode)
asRegExp
String
(none)
RegExp
a new string that is a a converted to a regular expression: no check is made of whether the result is a valid regular expression
contains
String
(none)
Boolean
true if the string contains the substring specified as the argument – false otherwise
indexOf
String
String
Int
index of the first instance of the argument (substring) within the string If it is not present, -1 is returned
isAfter
String
String
Boolean
true if alphabetic comparison finds the string comes strictly 'after' the argument string – false otherwise
isAfterOrSameAs
String
String
Boolean
true if alphabetic comparison finds the string comes 'after' or equals the argument string – false otherwise
isBefore
String
String
Boolean
true if alphabetic comparison finds the string comes strictly 'before' the argument string – false otherwise
isBeforeOrSameAs
String
String
Boolean
true if alphabetic comparison finds the string comes 'before' or equals the argument string – false otherwise
length
String
(none)
Int
the number of characters in the string
lowerCase
String
(none)
String
a new string with the original rendered in lower case
matchesRegExp
String
RegExp
Boolean
true if the string matches the regular expression – false otherwise
replace
String
String, String
String
a new string with all occurrences of the first argument string replaced by the second argument string
split
String
String
List<of String>
a List of the substrings found between occurrences of the argument string
if the argument is the empty string then the list is of all the individual characters
trim
String
(none)
String
a new string with leading and trailing spaces removed
upperCase
String
(none)
String
a new string with the original rendered in upper case
Tuple
A Tuple is a way of holding a small number of values of differing Types together as a single reference.
It may be considered a lightweight alternative to defining a specific class for some purposes.
Tuples are referred to as 2-tuples, 3-tuples, etc. according to the number of values they hold.
Common uses include:
Holding a pair of x,y coordinates (each of Type Float) as a single unit.
Allowing a function to pass back a result comprised of both a message in a String and a Boolean indicating whether the operation was successful.
Returning both an updated copy of an immutable data structure and the value of an element from it, e.g. from method pop on a Stack.
A Tuple differs from a List in that:
A Tuple may hold elements of different Types
A Tuple is immutable – you may read, but not modify, the values that it contains.
Defining a Tuple
A new Tuple is defined using this syntax:
let t1 be tuple(3, "apple", true)
Note that the elements may be literal or named values. An element within a Tuple may even be another Tuple.
Passing a Tuple to or from a function
You may pass a Tuple into a function, or return one from a function. The Type of a Tuple is specified
in round brackets, containing the comma-separated Types for each element in order, for example:
may be deconstructed into new variables or named values where the number of variables or names must match the number of elements in the Tuple, for example:
let x1, y1 be pointvariable x2, y2 set to pointset x2, y2 to point
The 'discard' symbol _ (underscore) may also be used when deconstructing a Tuple when there is no need to capture some of its elements:
variable x3, _ set to pointvariable _, y3 set to point
Where it is not convenient to use Tuple deconstruction (or not possible, e.g. within a lambda) then individual elements
may be accessed using the special properties item0, item1, item2,... with dot syntax,
for example:
Note that the appropriate itemNs will be listed in the Editor's drop-down menu options for the referenced Tuple.
Notes
As in most languages, an Elan Tuple is immutable. Once defined Tuples are effectively read-only. You cannot alter any of the elements in a Tuple nor (unlike a List) can you create a new Tuple from an existing one with specified differences.
You cannot deconstruct a Tuple into a mixture of new and existing variables.
Tuples may be nested: you can define a Tuple within a Tuple.
Standard (mutable) data structures
Elan's four standard data structures have the Types Array, Array2D, List and Dictionary.
They are all mutable, meaning that their contents (referred to as items or elements) may be changed directly by calling the various procedure dot methods defined for each structure Type.
However, since procedure methods may be called only from within the main routine or a procedure,
it is also possible to make changes via function dot methods, which return a copy of the data structure
with specified changes: that is why all such methods have names starting 'with'.
All four contain values (and keys in the case of a Dictionary) of only a single Type, that Type being specified either explicitly (as in
<ofInt>), or implicitly if the structure is created using a literal definition.
This table summarises the creation and reference to these structures. Details of their methods follow.
Standard (mutable) data structures
Array
Array2D
List
Dictionary
Type form
Array<of Type> Type of item is Int, Float, String or Boolean
Array2D<of Type> Type of item is Int, Float, String or Boolean
List<of Type> Type of item can be any, including Array – or List for 'jagged' data
Dictionary<of keyType, valueType> keyType is Int, Float, String or Boolean, or a record, and valueType may be any Type
size
Static
Static
Dynamic
Dynamic
literal definition
only as converted from a literal List e.g.:["a","b","c"].asArray()
not available
["a","b","c"]
["a":3, "b":5]
create empty
emptyArray<of Type>
emptyArray2D<of Type>
emptyList<of Type> or new List<of Type>()
emptyDictionary<of keyType>, valueType> or new Dictionary<of keyType, valueType>()
create initialised
new Array<of Type>(size, value) size is the number of items, and value is the initial value of every item
new Array2D<of Type>(cols, rows, value) Type of item is Int, Float, String or Boolean, cols is the number of columns, rows is the number of rows, and value is the initial value of every item (all are named values or literals)
not available
not available
read by index
a[3]
a2[3, 4]
li[2]
d["b"]
read by index range
a[2..5] lower bound is inclusive from 0 upper bound is exclusive
not available
li[2..5] lower bound is inclusive from 0 upper bound is exclusive
An array is a simple data structure containing n elements of a single Type, indexed from 0 to n-1.
The array elements' Type must be one of Int, Float, String, or Boolean.
As in most languages, individual elements may be referenced by their index.
To modify an element you may either
call the put procedure method, or use the
withPut function dot method.
An array's size is specified when it is created, along with the single value to which each element is initialised.
It is not possible to append further elements to an array, but you can convert an array to a list using method asList
and then extend or reduce the list, and possibly make a new array using method asArray on it.
Procedure methods on an Array
procedure method
on Type
argument Types
action
put
Array
Int, item of Array element's Type
puts the item at the (integer) index position in the array
Function dot methods on an Array
function method
on Type
argument Types
return Type
returns
asList
Array
(none)
List
the contents of the Array as a List
asString
Array
(none)
String
a String that is a comma+space-separated list of the Array's elements, enclosed in square brackets
contains
Array
item of Array element's Type
Boolean
true if the Array contains the value – false otherwise
indexOf
Array
item of Array element's Type
Int
the index position of the first occurrence of the argument value in the array, or -1 if no match is found
length
Array
(none)
Int
the number of elements in the array
withPut
Array
Int, item of Array element's Type
Array
a new Array with the item at the (integer) index position
Array2D
The Array2D Type defines a 2-dimensional array of fixed size. The two
dimensions, which are specified when the array is created, may be of the same size (a square array)
or different (a rectangular array). If you want to create a 'jagged' array, you should use a List of Lists.
The convention is to refer to the position of an element in such an array by two index values
(x,y), where x is the column number and y is the row number of a rectangular grid.
The Type for the elements is also specified when the Array is defined and must be
one of: Int, Float,
String, or Boolean.
Procedure methods on an Array2D
procedure method
on Type
argument Types
action
put
Array2D
Int, Int, item of Array2D element's Type
puts the item at the (two integers) index position in the array
Function dot methods on an Array2D
function dot method
on Type
argument Types
return Type
returns
asString
Array2D
(none)
String
a String that is a comma+space-separated list of the Array2D's columns each of which contains a comma+space-separated list of row values enclosed in square brackets, the whole enclosed in square brackets
columns
Array2D
(none)
Int
the number of columns in the array (the maximum x value)
contains
Array2D
item of Array2D element's Type
Boolean
true if the Array2D contains the item – false otherwise
indexOf
Array2D
item of Array2D element's Type
Int, Int
a Tuple containing the index pair of the first occurrence of the argument's value in the array
by searching the columns in order, or (-1,-1) if no match is found
rows
Array2D
(none)
Int
the number of rows in the array (the maximum y value)
withPut
Array2D
Int, Int, item of Array2D element's Type
Array2D
a new Array2D with the item at the (two integers) index position in the array
Note
Since an Array2D has a known fixed size, methods rows and columns are of use only when passing such an array into a procedure or function that needs to discover its size.
You can read individual elements with a double index, for example:
+for colvariableName from 0expression to 7expression step 1expression+for rowvariableName from 0expression to 7expression step 1expressionprint board[col, row]expressionend forend for
List
A List is similar to an Array in that its n elements are all of one Type
and are indexed from 0 to n-1, but is more flexible because:
While the elements must all be of one Type, that Type is not limited to the simple value Types:
the element Type may also be a user-defined Type such as a class, record, another List or an Array.
A List is created either empty or initialised from a literal definition.
A List's size can be dynamically changed.
List Deconstruction
A List or ListImmutable may be deconstructed into new variables or named values in a similar way to deconstructing a Tuple, but in this case:
Always use two variable names.
Use a colon : to separate the variable names.
The first item in the list (the head) is assigned to the first variable.
The rest of the list (the tail) is assigned to the second variable. If the original list only contains one element, this will be an empty list.
Like deconstructing a Tuple, you can use an underscore _ as the variable name to discard either the head or the tail.
variable x:xs set to myList set h:t to myList set h:myList to myList
Discarding either the head or tail:
variable _:tail set to myList variable head:_ set to myList
Procedure methods on a List
procedure method
on Type
argument Types
action
append
List
item of List element's Type
the item is added to the end of the List
appendList
List
List
the argument List is added to the end of the List
insert
List
Int, item of List element's Type
the item is inserted at the index given.
If the index is negative it is counted from the end
If the index is greater than the list's length, the item is inserted at the end
prepend
List
item of List element's Type
the item is is added to the start of the List
prependList
List
List
the argument List is added to the start of the List
put
List
Int, item of List element's Type
replace the item at the given index with the new item
removeAll
List
item of List element's Type
delete all items equal to the given item
removeAt
List
Int
delete the item at the argument index
removeFirst
List
item of List element's Type
delete the first item equal to the given item
Function dot methods on a List
function dot method
on Type
argument Types
return Type
returns
asArray
List
(none)
Array
a new Array containing the same elements
asListImmutable
List whose elements are of an immutable Type
(none)
ListImmutable
a new ListImmutable containing the same elements
asSet
List
(none)
Set
a new Set containing all the unique elements in the List
asString
List
(none)
String
a String that is a comma+space-separated list of the List's elements, enclosed in square brackets
contains
List
item of List element's Type
Boolean
true if the List contains the specified element
– false otherwise
filter
List
lambda
List
a new List obtained from applying a lambda to the input List
see filter in HoFs
head
List
(none)
List element's Type
the first element of the List
indexOf
List
item of List element's Type
Int
the index of the first occurrence of the argument value in the List, or -1 if no match is found
join
List<of String>
String
String
a single String that joins all the elements in the list, with the specified String (which can be empty) inserted between the elements
length
List
(none)
Int
the number of elements in the List
map
List
lambda
List
a new List obtained from applying a lambda to the input List
see map in HoFs
maxFloat
(not a dot method)
List<of Float>
Float
the maximum value found in the List
minFloat
(not a dot method)
List<of Float>
Float
the minimum value found in the List
maxInt
(not a dot method)
List<of Int>
Int
the maximum value found in the List
minInt
(not a dot method)
List<of Int>
Int
the minimum value found in the List
maxBy
List
lambda
List element's Type
the list element corresponding to the maximum of the values returned by a lambda
see maxBy in HoFs
minBy
List
lambda
List element's Type
the list element corresponding to the minimum of the values returned by a lambda
see minBy in HoFs
reduce
List
item of List element's Type, lambda
return type of lambda
the final value obtained by applying a lambda cumulatively to each element in the input List
taking account of the first argument as an initial (and default return) value
see reduce in HoFs
sortBy
List
lambda
List
a new list with the elements sorted according to the value returned by a lambda
see sortBy in HoFs
withAppend
List
item of List element's Type
List
a new List lengthened with the item added after the end
withAppendList
List
List of same Type
List
a new List lengthened by appending the given List
withInsert
List
Int, item of List element's Type
List
a new List with the item inserted after the element at the given index position
withPrepend
List
item of List element's Type
List
a new List lengthened with the item added before the beginning
withPrependList
List
List of same Type
List
a new List lengthened by prepending the given List
withPut
List
Int, item of List element's Type
List
a new List with the item replacing that at the given index
withRemoveAll
List
item of List element's Type
List
a new List with items equal to the given item removed
withRemoveAt
List
Int
List
a new List with the item at the given index removed
withRemoveFirst
List
item of List element's Type
List
a new List with the first item equal to the given item removed
Dictionary
A Dictionary works like an Array, but instead of having a numeric (integer) index, each entry has a key. Each key is associated with a value,
so the Dictionary is a set of key:value pairs.
The key's Type must be one of Int, Float, String or Boolean,
or it may be a user-defined record. The values may be of any Type, and the Types of both key and value are fixed when the Dictionary is created.
Procedure methods on a Dictionary
procedure method
on Type
argument Types
action
put
Dictionary
key's Type, value's Type
replace the value at an existing key, or add a key:value pair
removeAt
Dictionary
key's Type
delete the key:pair at the given key (if it exists)
Function dot methods on a Dictionary
function dot method
on Type
argument Types
return Type
returns
asDictionaryImmutable
Dictionary
(none)
DictionaryImmutable
a new DictionaryImmutable with the same contents as the Dictionary
asString
Dictionary
(none)
String
a string that is a comma+space-separated list of the Dictionary's key:value pairs, enclosed in square brackets
hasKey
Dictionary
item of Dictionary key's Type
Boolean
true if the Dictionary contains the given key – false otherwise
keys
Dictionary
(none)
List
a List containing the keys
values
Dictionary
(none)
List
a List containing the values
withPut
Dictionary
item of Dictionary key's Type, item of Dictionary value's Type
Dictionary
a new Dictionary with the value replacing what was at the specified key
withRemoveAt
Dictionary
item of Dictionary key's Type
Dictionary
a new Dictionary with the key:value pair removed from the specified key
Immutable data structures
In contrast to the standard (mutable) data structures, the contents (referred to as items or elements)
of the five immutable data structures cannot be modified directly, and hence the structure Types have no procedure dot methods defined. Instead,
changes are made by using function dot methods that copy the existing data structure with specified
changes.
Immutable data structures are intended specifically to facilitate the Functional Programming paradigm,
but some are also useful within other programming paradigms.
This table summarises the creation and reference to these immutable structures. Details of their methods follow.
Immutable data structures
ListImmutable
DictionaryImmutable
Set
Stack
Queue
Type form
ListImmutable<of Type> Type of item must be immutable
DictionaryImmutable<of keyType, valueType> keyType is Int, Float, String or Boolean or a record ,and valueType may be any immutable Type
Set<of Type> Type of item must be immutable
Stack<of Type> Type of item must be immutable
Queue<of Type> Type of item must be immutable
Size
Dynamic
Dynamic
Dynamic
Dynamic
Dynamic
literal definition
{"a","b","c"}
{"a":3, "b":5}
Only as converted from a literal List e.g.: ["a","b","c"].asSet()
Not available
Not available
create empty
emptyListImmutable<of Type> or new ListImmutable<of Type>()
emptyDictionaryImmutable<of keyType, valueType>() or new DictionaryImmutable<of keyType, valueType>()
emptySet<of Type> or new Set<of Type>()
emptyStack<of Type> or new Stack<of Type>()
emptyQueue<of Type> or new Queue<of Type>()
create initialised
not available
not available
not available
not available
not available
read by key
lim[2]
dim["b"]
not available
not available
not available
read by range
lim[2..5] lower bound is inclusive from 0 upper bound is exclusive
A ListImmutable is like a List but is immutable (like a String).
You can still insert, delete or change elements in a ListImmutable, but the methods for these operations do not modify the input ListImmutable: they return a new ListImmutable based on the input ListImmutable with the specified differences.
Type name
The Type is specified in the following ways:
ListImmutable<of String> for a ListImmutable of Type String
ListImmutable<of Int> for a ListImmutable of Type Int
ListImmutable<of ListImmutable<of Int>> for a ListImmutable of such lists each of Type Int
Creating a ListImmutable
A ListImmutable may be defined in 'literal' form, delimited by curly brace with all the required elements separated by commas. The elements may be literal values but must all be of the same Type, for example:
variable fruit set to {"apple", "orange", "pear"}
ListImmutable Deconstruction
A ListImmutable can be deconstructed just like a List, using a colon : to separate two variable names.
set h:t to myListImmutable
Function dot methods on a ListImmutable
function dot method
on Type
argument Types
return Type
returns
asList
ListImmutable
(none)
List
a new List with the same contents as the ListImmutable
asSet
ListImmutable
(none)
Set
a new Set containing all the unique elements in the List
asString
ListImmutable
(none)
String
a string that is a comma+space-separated list of the List's elements, enclosed in braces
contains
ListImmutable
item of List element's Type
Boolean
true if the List contains the item – false otherwise
head
ListImmutable
(none)
item of List element's Type
the first item in the List
indexOf
ListImmutable
item of List element's Type
Int
index of the first occurrence of the argument value in the List, or -1 if no match is found
length
ListImmutable
(none)
Int
the number of elements in the List
withInsert
ListImmutable
Int, item of List element's Type
ListImmutable
a new ListImmutable with the item inserted at the specified position
withPut
ListImmutable
Int, item of List element's Type
ListImmutable
a new ListImmutable with the item replacing what was at specified position
withRemoveAt
ListImmutable
Int
ListImmutable
a new ListImmutable with the item at the specified position removed
withRemoveFirst
ListImmutable
item of List element's Type
ListImmutable
a new ListImmutable with the first occurrence of the item removed
withRemoveAll
ListImmutable
item of List element's Type
ListImmutable
a new ListImmutable with all occurrences of the item removed
Try these examples:
# reusing variable fruit for each new ListImmutablecommentvariable fruitname set to emptyListImmutable<ofString>expressionprint fruitexpressionset fruitvariableName to fruit.withAppend("apple")expressionset fruitvariableName to fruit.withAppend("pear")expressionprint fruitexpressionprint fruit[1]expressionprint fruit.length()expressionprint fruit[fruit.length() - 2]expressionprint fruit.contains("banana")expression
DictionaryImmutable
A DictionaryImmutable is like a Dictionary but immutable
(like a String). You can still insert, delete or change elements in a DictionaryImmutable,
but the methods of these operations do not modify the input DictionaryImmutable: they return a new
DictionaryImmutable with the specified differences.
A DictionaryImmutable may be defined in a constant.
Type name
In the following example, the keys are of Type String, and the values associated with the keys are of Type Int:
DictionaryImmutable<of String, Int>
Defining a literal DictionaryImmutable
A literal DictionaryImmutable is defined as a comma-separated list of key:value pairs surrounded by curly braces:
variable scrabbleValues set to {"a":1, "b":3, "c":3, "d":2}
Using an Immutable Dictionary
Try this example:
variable immDname set to newDictionaryImmutable<ofString, Int>()expressionprint immDexpressionset immDvariableName to immD.withPut("a", 3)expressionprint immD["a"]expressionset immDvariableName to immD.withRemoveAt("a")expressionprint immDexpression
Function dot methods on a DictionaryImmutable
function dot method
on Type
argument Types
return Type
returns
asString
DictionaryImmutable
(none)
String
a string that is a comma+space-separated list of the DictionaryImmutable's key:value pairs, enclosed in braces
hasKey
DictionaryImmutable
item of DictionaryImmutable key's Type
Boolean
true if the DictionaryImmutable contains the given key – false otherwise
withPut
DictionaryImmutable
item of DictionaryImmutable key's Type, item of DictionaryImmutable value's Type
DictionaryImmutable
a new DictionaryImmutable with the value replacing what was at the specified key
withRemoveAt
DictionaryImmutable
item of DictionaryImmutable key's Type
DictionaryImmutable
a new DictionaryImmutable with the key:value pair removed from the specified key
Set
A set is a standard data structure that works somewhat like a ListImmutable with the important difference that in a set a given element may appear only once. If an item being added to a Set is identical to an existing item in the Set then the Set remains the same length as before.
This enables a set to work like a mathematical set so that it is possible to perform standard set operations such as union or intersection. For the same reason, a Set is an immutable data structure: there are no methods that modify the set on which they are called, but several of them (including add, remove) return a new set that is based on the original set or sets, with specified differences.
Example of use:
variable stname set to newSet<ofInt>()expressionset stvariableName to st.addFromList([3, 5, 7])expressionprint st.length()expressionset stvariableName to st.add(7)expressionprint st.length()expressionset stvariableName to st.remove(3)expressionprint st.length()expressionset stvariableName to st.remove(3)expressionprint st.length()expressionprint stexpression
Notes
When creating a set, the Type of the elements must be specified in the form
Set<of String>. This applies both when creating a new, empty set and when defining the Type of a parameter to be a Set.
You can add elements: individually with add, or multiple elements with addFromList.
You can create a new set from an existing list or ListImmutable by calling asSet on it.
Function dot methods on a Set
function dot method
on Type
argument Types
return Type
returns
add
Set
item of Set element's Type
Set
a new Set extended with the item, provided it differs from all the Set's current elements
addFromList
Set
List
Set
a new Set extended with those items from the List that are not already in the Set
asList
Set
(none)
List
a List containing the Set's elements
asString
Set
(none)
String
a string that is a comma+space-separated list of the Set's elements, enclosed in braces
contains
Set
item of Set element's Type
Boolean
true if the Set contains the item – false otherwise
difference
Set
Set
Set
a Set containing those elements of the Set that do not occur in the argument Set
intersection
Set
Set
Set
a Set containing those elements common to both the Set and the argument Set
isDisjointFrom
Set
Set
Boolean
true if the Set has no items in common with the argument Set – false otherwise
isSubsetOf
Set
Set
Boolean
true if all the Set's items are also in the argument Set – false otherwise
isSupersetOf
Set
Set
Boolean
true if the Set contains all the items that are in the argument Set – false otherwise
length
Set
(none)
Int
the number of elements in the Set
remove
Set
item of Set element's Type
Set
a new Set with the argument item removed (if present)
union
Set
Set
Set
a Set containing all the unique elements of the Set and the argument Set (i.e. no duplicates)
Stack and Queue
Stack and Queue are similar data structures except that Stack is ‘LIFO’ (last in, first out), while Queue is FIFO (first in, first out). The names of the methods for adding/removing are different, but there are also common methods.
Both a Stack and a Queue are defined with the Type of the items that they can contain, similarly to how List and ListImmutable have a specified item Type, though with different syntax. The Type is specified in the form shown below e.g. Stack<of String>, Queue<of Int>, Stack<of (Float, Float)>, Queue<of Square>.
Both Stack and Queue are dynamically extensible, like List and ListImmutable. There is no need (or means) to specify a size limit as they will continue to expand until, eventually, the computer’s memory limit is reached.
This same syntax is used to specify the Type if you want to pass a Stack or Queue into a function, or specify it as the return Type.
Stack and Queue have two methods in common: length and peek.
peek returns the next item to be removed, without actually removing it.
The methods for adding and removing an item are different for Stack and Queue, as shown here:
Function dot methods on a Stack
function dot method
on Type
argument Types
return Types
returns
push
Stack
item of Stack element's Type
Stack
the Stack with the element added to the top of the Stack
pop
Stack
(none)
Stack element Type, Stack
the topmost element of the Stack, and the Stack with the element removed
peek
Stack
(none)
Stack element Type
the topmost element of the Stack (without altering the Stack)
length
Stack
(none)
Int
the number of elements in the Stack
asString
Stack
(none)
String
a string that is a comma+space-separated list of the Stack's elements, enclosed in square brackets,
last element pushed listed first
Function dot methods on a Queue
function dot method
on Type
argument Types
return Types
returns
enqueue
Queue
item of Queue element's Type
Queue
the Queue with the element added to the end of the Queue
dequeue
Queue
(none)
Queue element Type, Queue
the next element of the Queue, and the Queue with the element removed
peek
Queue
(none)
Queue element Type
the next element of the Queue (without altering the Queue)
length
Queue
(none)
Int
the number of elements in the Queue
asString
Queue
(none)
String
a string that is a comma+space-separated list of the Queue's elements, enclosed in square brackets,
first element enqueued listed first
Examples using Stack and Queue
▶ To push and pop strings:
variable stname set to newStack<ofString>()expressionprint st.length()expressionset stvariableName to st.push("apple")expressionset stvariableName to st.push("Pear")expressionprint st.length()expressionprint st.peek()expressionvariable fruitname set to ""expressionset fruit, stvariableName to st.pop()expressionprint fruitexpressionset fruit, stvariableName to st.pop()expressionprint fruitexpressionprint st.length()expression
▶ To enqueue and dequeue strings:
variable quname set to newQueue<ofString>()expressionprint qu.length()expressionset quvariableName to qu.enqueue("apple")expressionset quvariableName to qu.enqueue("Pear")expressionprint qu.length()expressionprint qu.peek()expressionvariable fruitname set to ""expressionset fruit, quvariableName to qu.dequeue()expressionprint fruitexpressionset fruit, quvariableName to qu.dequeue()expressionprint fruitexpressionprint qu.length()expression
Common dot methods
Dot methods that work on more than one fundamental Type
Although all applicable methods are described in earlier sections under each Type, this table lists those which are applicable to several Types.
Click on a Type to go to the Type's dot methods, or on the ✔ tick to go to the specific method on that Type.
The method asString is so widely applicable because it enables you to print
most variables and data structures to the IDE's Display pane for debugging purposes.
The length of a Dictionary or DictionaryImmutable can be found by applying method length to the list returned by method keys on the dictionary:
print dict.keys().length() ⟶ 15
Checking whether an item is contained in a Dictionary or DictionaryImmutable can be
done by applying method contains to the list returned by methods keys and values on the dictionary:
print dict.keys().contains(42) ⟶ true or false
print dict.values().contains("widget") ⟶ true or false
Graphics
Block graphics
Block graphics provides a simple way to create low resolution graphics, ideal for simple but engaging games for example.
The graphics are displayed on a grid that is 40 blocks wide by 30 blocks high.
Each block is be rendered as a solid colour.
An example of block graphics to produce a rapidly changing pattern of coloured blocks:
variable blocks set to newArray2D<ofInt>(40, 30, white)+while truelet x be randomInt(0, 39)let y be randomInt(0, 29)let colour be randomInt(0, white - 1)call blocks.put(x, y, colour)call displayBlocks(blocks)end while
Notes
The Array2D must be of TypeInt and of size 40 x 30.
You may create multiple Array2Ds holding different patterns of blocks, and switch between them
just by passing the required one as the argument to the displayBlocks method.
A colour is specified as an Int, as described under Colours.
Turtle graphics
Turtle graphics are implemented with output to the IDE's Display pane, i.e. the 'paper' on which the turtle draws.
The area is 200 turtle units wide by 150 turtle units high, and both integer and floating point values of turtle units can be used.
If a turtle is placed or moved outside the 200 × 150 area boundary, it will not cause an error,
but the turtle and any lines drawn outside the boundary will not be visible.
The origin for turtle units (0,0) is at the centre of the area: positive x rightwards, positive y upwards.
So the top left corner of the display is at (-100,75).
A new turtle's default starting position is at (0,0) facing upwards (heading is 0), from where a move will be in the y direction.
You can move and turn a turtle, causing lines to be drawn, whether or not the turtle is showing.
Procedure methods on a Turtle
procedure method
on Type
argument Types
action
clearAndReset
Turtle
(none)
clears the turtle display and moves the turtle to its starting position and heading
hide
Turtle
(none)
makes the turtle invisible in the display
move
Turtle
Int or Float
moves the turtle the specified number of turtle units in the direction of its heading or, if negative, in the opposite direction
moveTo
Turtle
Int or Float, Int or Float
moves the turtle to the specified x,y position, drawing its path if its pen is down
penColour
Turtle
Int
changes the colour of its path in the display to the literal or named value specified
penDown
Turtle
(none)
makes the turtle's subsequent moves leave a trace of its path in the display
penUp
Turtle
(none)
makes the turtle's subsequent moves leave no trace of its path in the display
penWidth
Turtle
Int or Float
changes the width of the line tracing the turtle's path in the display default (and minimum) is 1
placeAt
Turtle
Int or Float, Int or Float
places or repositions the turtle at the x,y position specified without drawing a path
show
Turtle
(none)
makes the turtle visible in the display as a green blob marked with a black radius that indicates its heading
turn
Turtle
Int or Float
turns the turtle through the specified number of degrees clockwise or, if negative, anticlockwise
turnToHeading
Turtle
Int or Float
turns the turtle to face in the direction specified in degrees where 0 is upward and increasing values go clockwise from there
Properties of a Turtle
The current location and heading of the turtle may be read using the properties
x, y, and heading.
Property
name
Type
description
default
heading
Float
the direction in which the turtle is pointing in degrees clockwise from North
0.0 degrees, i.e. upward in the Display
x, y
Float, Float
the x,y coordinates of the turtle's current position
0.0, 0.0
Examples using turtle graphics
▶ To draw a square:
let t be newTurtle()call t.placeAt(-75, 50)call t.show()+for i from 1 to 4 step 1call t.turn(90)call t.move(80)call pause(500)end for
▶ To draw a fractal snowflake, using a procedure and recursion:
+mainlet t be newTurtle()call t.placeAt(-50, 30)call t.turn(90)+for i from 1 to 3 step 1call drawSide(side, t)call t.turn(120)end forend main+procedure drawSide(lengthasFloat, tasTurtle)+if (length > 1) thenlet third be length/3call drawSide(third, t)call t.turn(-60)call drawSide(third, t)call t.turn(120)call drawSide(third, t)call t.turn(-60)call drawSide(third, t)else call t.move(length)end ifend procedure+constant side set to 100
Vector graphics
Vector graphics are implemented using the SVG (Scalable Vector Graphics) Html tag <svg>, and are output to the Display pane in the user interface.
The area is 100 units wide by 75 units high, and both integer and floating point values of the units can be used.
The origin of the units (0,0) is at the top left corner of the display: positive x rightwards, positive y downwards.
So for example the centre of the display is at (50, 37.5).
The names of the shapes broadly correspond to the names of SVG tags:
CircleVG for <circle../>
LineVG for <line../>
RectangleVG for <rect../>
ImageVG for <image../>
The properties of these shapes reflect the names of the attributes used in the SVG tags but some names differ. For example, SVG's stroke-width
is set with property strokeWidth, to make it a valid identifier.
Also stroke and fill are set by the properties strokeColour and fillColour.
Defining Vector graphic shapes
A set of SVG shapes is defined in either a List<ofVectorGraphic> or a List of any specific Type of VectorGraphic (e.g. List<ofCircleVG>) by using the Listappend method.
VectorGraphic is the abstract superclass of all ..VG shapes. You would use it if you wanted to define a List holding different types of shape.
Adding a shape returns a new instance of the list which must be assigned either to an existing variable, or to a new let.
The constructors for the VG Types require arguments defining the attributes of the relevant SVG tags, so defaults are supplied from the class properties,
as shown in the table below.
To supply your own values for the properties:
for a new instance, to set the value of property 'P..', use the with clause .
for an existing instance, to change the value of property 'P..', call the setP.. procedure method on the VectorGraphic or
(in a function) use the withP.. dot method on the VectorGraphic, in either case specifying new values in the arguments.
The fillColour and strokeColour properties may be specified as described under Colours.
Only fillColour can be specified as transparent.
Note that the height and width properties of an image are also dependent on the dimensions of the original graphic file.
Displaying Vector graphic shapes
As with Block graphics, the assembled list of shapes is not displayed until you make a call to procedure displayVectorGraphics with the list as its argument, thus allowing you to make multiple changes before display.
And as with using SVG in Html, the shapes are drawn in the order in which they are added to the VectorGraphic instance.
Later shapes are positioned over earlier ones.
Function dot methods on Vector graphic shapes
function dot method
on Type
argument Types
return Type
returns
asHtml
ImageVG
none
String
string containing the Html <img> tag filled with the image's URL and attribute values
asSVG
CircleVG
none
String
string containing the SVG <circle> tag filled with the circle's attribute values
asSVG
ImageVG
none
String
string containing the SVG <image> tag filled with the image's URL and attribute values
asSVG
LineVG
none
String
string containing the SVG <line> tag filled with the line's attribute values
asSVG
RectangleVG
none
String
string containing the SVG <rectangle> tag filled with the rectangle's attribute values
Properties of Vector graphic shapes
Property
Vector graphic shape
Property
Method
name
Type
CircleVG
ImageVG
LineVG
RectangleVG
description
default
procedure
function
alt
String
✔
alternate textual description (for accessibility)
empty string
setAlt
withAlt
centreX
Float
✔
horizontal position of circle centre
50
setCentreX
withCentreX
centreY
Float
✔
vertical position of circle centre
37.5
setCentreY
withCentreY
fillColour
Int
✔
✔
colour to fill the shape
yellow
setFillColour
withFillColour
height
Float
✔
✔
height to render the shape or the image
Image: 13.2 Rectangle: 20
setHeight
withHeight
radius
Float
✔
radius of circle
10
setRadius
withRadius
strokeColour
Int
✔
✔
✔
colour of the strokes in the shape
black
setStrokeColour
withStrokeColour
strokeWidth
Int
✔
✔
✔
width of the strokes in the shape
1
setStrokeWidth
withStrokeWidth
title
Float
✔
title text
empty string
setTitle
withTitle
width
Float
✔
✔
width to render the shape or the image
Image: 13.2 Rectangle: 40
setWidth
withWidth
x, y
Float, Float
✔ ✔
✔ ✔
image's x,y position from top left
Image: 0, 0 Rectangle: 10, 10
setX, setY
withX, withY
x1, y1
Float, Float
✔ ✔
line's starting x,y position from top left
10, 10
setX1, setY1
withX1, withY1
x2, y2
Float, Float
✔ ✔
line's ending x,y position from top left
70, 40
setX2, setY2
withX2, withY2
Examples using SVG graphics
▶ To draw a circle with a coloured circumference:
variable vgname set to newList<ofVectorGraphic>()expressionlet circname be newCircleVG() with centreX set to 20, centreY set to 20, radius set to 5, fillColour set to red, strokeColour set to green, strokeWidth set to 2expressioncall vg.appendprocedureName(circarguments)call displayVectorGraphicsprocedureName(vgarguments)
▶ To draw a circle that changes between red and green every second:
variable vgname set to newList<ofVectorGraphic>()expressionlet circname be newCircleVG() with centreX set to 50, centreY set to 37, radius set to 30, fillColour set to greenexpressioncall vg.appendprocedureName(circarguments)+while trueconditioncall displayVectorGraphicsprocedureName(vgarguments)call pauseprocedureName(700arguments)call circ.setFillColourprocedureName(redarguments)call displayVectorGraphicsprocedureName(vgarguments)call pauseprocedureName(700arguments)call circ.setFillColourprocedureName(greenarguments)end while
Combining graphic outputs
Program outputs, whether text or graphical, can be combined in the Display. In particular, Block graphics and text or Html printing can share the Display
along with either Vector graphics or Turtle graphics (but not both).
If you want to share the Display in this way, remember that both text and Html print outputs appear sequentially down the Display (which can be scrolled), whereas the graphic outputs are positioned in the Display using their own absolute coordinate systems.
The order in which the outputs are displayed (and therefore overwrite) is:
Block graphics
Vector or Turtle graphics
Printed text or Html
So some care is needed to manage the layout in the Display.
Note that, while vector graphics are being drawn, printed text that exceeds the height of the Display does not scroll until the graphic completes.
Input/output
Reading keys ‘on the fly’
In some applications – especially in games, for example – you want the program to react to a key pressed by the user, but without holding up the program to wait for value to be input.
Whether your application makes use graphics, or just uses the Display for text, reading keystrokes ‘on the fly’ is done via one of two methods:
let key1 be getKey()let key2, modifier be getKeyWithModifier()
Notes
When the getKey function is called, the system does not wait for a response. If a key has been pressed then that will be returned as a String e.g. "a".
Non-printable keys will be returned in the form: "Backspace","Enter","ArrowDown", etc.
If no key has been pressed (since the last time the method was called), it will return the empty string "".
Pressing just the Shift, Ctrl (⌘ Command under macOS) or Alt keys will not be detected by getKey. To read those keys use…
getKeyWithModifier which returns a 2-tuple containing the key pressed plus any ‘modifier’ key such a Shift, Ctrl, or Alt (or the empty string if no modifier key is pressed).
Both of these getKey methods are System methods because they have a dependency on the system and so may only be used within a procedure or in main.
Use the procedure method clearKeyBuffer() if you want to enforce that the user cannot get too far ahead of the program by hitting keys in very rapid succession.
waitForKey waits for a key to be pressed, and returns it. pressAnyKeyToContinue gives an optional prompt and is used when you don't need to know which key was pressed.
call pressAnyKeyToContinue(true)print "OK, press A or B"let mykey be waitForKey()print "That was {mykey}"
Reading text files
The TextFileReader class is used to read textual data from a file.
An instance is created by the standalone system method openFileForReading.
The available procedure methods are:
procedure method
argument Type
action
openFileForReading
(none)
opens a file system dialog to choose the filename to be read, which must have the filetype .txt
and returns a file handle
readLine
(none)
reads the next substring from the file that is terminated with a newline
readWholeFile
(none)
reads the whole file and closes the file
endOfFile
(none)
returns true after the last line has been read
– otherwise false
close
(none)
closes the file
These methods may be used to read a whole file in one go:
let filename be openFileForReading()expressionlet textname be file.readWholeFile()expressionprint textexpression
or to read a file line by line:
let filename be openFileForReading()expressionvariable linesname set to emptyList<ofString>expression+while notfile.endOfFile()conditionlet linename be file.readLine()expressioncall lines.appendprocedureName(linearguments)end whilecall file.closeprocedureName(arguments)
Notes
openFileForReading will present the user with a dialog to select the file.
readWholeFile returns a String containing every character in the file, without any trimming. It automatically closes the file after the read.
readLine reads as far as the next newline character (\n) and then automatically trims the line to remove any spaces and/or carriage-returns (which some file systems insert after the newline automatically) from the resulting line returned as a String. If this behaviour is not desired, you can use readWholeFile, which does no trimming, and then parse the resulting String into separate lines.
Calling file.close after reading line by line is strongly recommended to avoid any risk of leaving the file locked. It is not necessary to call it after using readWholeFile because that method automatically closes the file.
Calling any method on a file that is already closed will result in a runtime error.
Writing text files
The TextFileWriter class is used to write textual data to a file.
An instance is created by the standalone system method createFileForWriting.
The available procedure methods are:
procedure method
argument Type
action
creatFileForWriting
String
sets up a buffer for the data to be output, and specifies a filename (or the empty string)
with or without the default filetype of .txt
and returns a file handle
writeLine
String
writes the string to the buffer
writeWholeFile
String
writes the string to the buffer and then outputs the buffer to the file system
saveAndClose
String
writes the buffer to the file system
These methods may be used to write a whole file in one go:
let fname be createFileForWriting("myFile.txt")expressioncall f.writeWholeFileprocedureName("this is\nmyText"arguments)
or to write a file line by line:
let filename be createFileForWriting("squares.txt")expression+for ivariableName from 1expression to 100expression step 1expressioncall file.writeLineprocedureName("{i}{i*i}"arguments)end forcall file.saveAndCloseprocedureName(arguments)
Notes
writeLine adds the string it is passed onto the end of any data previously written, with a newline character (\n) automatically appended.
When execution reaches saveAndClose you will be presented with a dialog to confirm (or edit) the given filename and location where it is to be saved. It is not therefore strictly necessary to specify a filename when creating the file, since it can be specified by the user in the dialog so, in that case, you might put the empty string "" into the parameter of createFileForWriting.
writeWholeFile puts the string it is given into the file and then automatically saves the file, so the user will be presented with the same dialog as if saveAndClose had been called.
Calling any method on a file that has already been closed (by calling either saveAndClose or by writeWholeFile) will result in a runtime error.
If the user were to hit Cancel on the save dialog, then the program will exit with an error. If you want to guard against this possibility (if, for example, it might mean the loss of important data) then you should perform the save and close within a try..catch like this:
+try call file.saveAndCloseprocedureName(arguments)+catch exception in evariableNameprint "File save cancelled"expressionend try
or you could make the code offer the user options: to save again, or to continue without saving.
Rendering Html in the Display
If you embed Html code in a string and then attempt to print it, you will see the string displayed literally: the Html tags
will not be recognised. This is for security reasons. However, it is possible to display formatted Html in the Display.
The following code:
This Html forms another 'layer' in the IDE's Display pane. Any plain text that you print
(using print or any of the print.. procedures)
will be overlaid on top of this.
You can clear just the Html layer in the display using the procedure clearHtml.
For specifying style or other attributes within Html tags, the attribute values should be enclosed in single quotation marks ' as shown above.
Html will recognise single or double quotation marks, but entering double quotation marks would terminate the Elan string.
Alternatively, you could use the constant quotes within braces as an interpolated field.
Using an embedded CSS stylesheet
You can also specify a <style> tag at the start of your Html string, to apply to the whole Html being displayed. In this case you would put
the style definition into a named value as a literal but enclosed in single quotes, for example:
let style be '<style> h1 { color: Red; font-size: 24pt; } p { font-family: Serif; } </style>'call displayHtml("{style}<h1>New heading</h1><p>some new text</p>")
will produce:
The <style> string is displayed for editing like this:
let style be
But by inserting \n newlines thus:
let style be
the string can be shown folded and indented when not being edited:
let style be '<style> h1 { color: Red; font-size: 24pt; } p { font-family: Serif; } </style>'
Displaying images from URLs
An image that can be retrieved with an http or https URL may be sent to the Display, though note that other URI schemes, such as in file://host/path to access an image from your local file system, are not supported. Here are three techniques:
put the Html <img> tag into a string and then call the displayHtml method on it, thus:
let pic be "<img src='https://upload.wikimedia.org/wikipedia/commons/0/08/Corl0207_%2828225976491%29.jpg' width='400' height='200' title='shark' alt='shark'>"call displayHtml(pic)
Notes
The usual <img> tag attributes are respected, though alt does not give the expected tooltip when hovering over the image,
and title is only a descriptive comment.
The attributes can be shown listed vertically by inserting \n to signify a newline, as is done here before width and before title.
put the URL into a Vector Graphics ImageVG definition: and then call the displayVectorGraphics method on it, thus:
let pic be newImageVG('https://upload.wikimedia.org/wikipedia/commons/0/08/Corl0207_(28225976491).jpg') with width set to 400, height set to 200, alt set to "Shark"call displayVectorGraphics([pic])
Notes
Vector graphic image properties include height, width and alt.
Other vector graphic image properties such as x,y position can be added into the with clause.
put the URL into an image definition:
let pic be
On moving the editing focus away from the image field, the instruction shows a thumbnail of the image in the code.
Then convert it with method asHtml so that you can display it with the displayHtml method thus:
variable vg set to newList<ofVectorGraphic>()let pic be with width set to 304, height set to 139call vg.append(pic)call displayHtml(pic.asHtml())
Notes
Vector graphic image properties include height, width and alt that match the similar Html attributes and and have the same effect.
Other vector graphic image properties such as x,y position can be added into the with clause.
In this example, pic will be of type ImageVG which means that instead of displaying
it as Html it may be displayed as vector graphics (like in the earlier example).
Sound
The tone procedure allows the generation of a simple tone. It requires three arguments to be provided:
the duration of the generated tone specified in milliseconds (Int)
the frequency of the generated tone in Hertz (Int)
the volume of the tone (Float)
Note that the volume parameter is only relative in that the actual volume will be modified by the various sound settings on the output device, and may even be muted.
Example using tone
▶ An example that plays a C major scale first ascending then descending:
let scale be [262, 294, 330, 349, 392, 440, 494, 523]let scaleL be scale.length()let quaver be 250let volume be 1.5+for i from 0 to scaleL - 1 step 1call tone(quaver, scale[i], volume)end forcall pause(1000)+for i from 0 to scaleL - 1 step 1call tone(quaver, scale[scaleL - 1 - i], volume)end for
Other Types
Random
Generating random numbers within a function
It is not possible to use the system methods random() or randomInt() within a function because they create unseen side effects. You may use those system methods outside the function and pass the resulting random number (as an Int or a Float) as an argument into a function.
It is, however, possible to create and use random numbers within a function, but it requires a different approach and is a little more complex. You use the special Type Random. (Note the upper case R required for a Type name).
You start by getting a seed random number in your main or in a procedure using method initialiseFromClock:
variable rnd set to newRandom()call rnd.initialiseFromClock()
Then, in a function, you apply the dot method next or nextInt to the Random passed in, which returns a 2-Tuple containing both a new random value and a new instance of Type Random.
If you need another random value, you use the returned Random on which to apply the dot method again.
Avoid applying next or nextInt more than once on the same Random because they will return the same values.
If the initialiseFromClock call is absent, the program will still generate a sequence of random values, but the sequence will be exactly the same each time you run the program. Initialising from the clock ensures that you get a different sequence each run. Using Random without so initialising, however, can be extremely useful for testing purposes since the results are repeatable.
The initialising procedure method is:
procedure method
argument Type
action
initialiseFromClock
(none)
uses the system clock to provide a seed from which to generate fresh random values
The function dot methods on a Random are:
function dot method
on Type
argument Types
return Type
returns
next
Random
Float, Float
tuple(Float, Random)
random Float value in the range of the arguments, and new instance of Random for subsequent use
nextInt
Random
Int, Int
tuple(Int, Random)
random Int value in the range of the arguments, and new instance of Random for subsequent use
Example using Type Random
▶ To roll a dice in a function:
mainvariable rnd set to newRandom()call rnd.initialiseFromClock()variable dice set to 0for i from 1 to 10 step 1set dice, rnd to rollDice(rnd)print diceend forend mainfunction rollDice(rndasRandom) returns (Int, Random)return rnd.nextInt(1, 6)end function
Func
A function may be passed as an argument into another function (or a procedure), or returned as the result of calling another function.
This pattern is known as Higher-order Function (HoF), and is a key idea in the Functional Programming.
To define a function that takes in another function as a parameter, or returns a function, you need to specify the Type of the function,
just as you would specify the Type of every parameter and the return Type for the function.
Type name
The Type of any function starts with the word Func followed by angle brackets defining the Type of each parameter,
and the return Type for that function, following this syntax:
Func<of String, String, Int => Boolean>
This example defines the Type for a function that defines three parameters of Type String, String, and Int respectively, and returns a Boolean value. This Type would match that of a function definition that started:
These constants are of use when the characters { } and " are required in a string delimited by double quotes.
Within a string delimited by single quotes, those literal characters can also be used.
A colour is specified as an Int value using one of these methods:
the limited colours defined as library constants as in the above table.
an integer in the decimal range 0 (black) to 224-1 (white).
a six digit hexadecimal value in the range 0x000000 – 0xffffff
using the same 'RGB' format as used in Html style, for example 0xff0000 for red.
Standalone functions
Standalone library functions always return a value and are therefore used in contexts that expect a value, such as in the right-hand side of a variable declaration or a set assignment, either on their own or within a more complex expression. All standalone library functions require at least one argument to be passed in brackets, corresponding to the parameters defined for that function.
unicode
Function unicode converts a Unicode (codepoint) value expressed as an integer value in decimal or hexadecimal notation, or as any expression evaluating to an Int, into a string of a single character. For example:
+function heartsname(parameter definitions) returns StringTypereturn unicode(0x2665)expressionend function
parseAsInt and parseAsFloat
Function parseAsInt attempts to parse the input String as an Int, and returns a 2-tuple, the first value of which is Boolean, with true indicating whether or not the parse has succeeded, and the second value being the resulting Int.
parseAsFloat does the equivalent for floating point. Operation is illustrated by these tests:
+test optional descriptionassert parseAsInt("31")computed value is tuple(true, 31)expected valuepassassert parseAsInt("0")computed value is tuple(true, 0)expected valuepassassert parseAsInt("thirty one")computed value is tuple(false, 0)expected valuepassassert parseAsInt("3.1")computed value is tuple(false, 0)expected valuepassassert parseAsFloat("31")computed value is tuple(true, 31)expected valuepassassert parseAsFloat("0")computed value is tuple(true, 0)expected valuepassassert parseAsFloat("3.1")computed value is tuple(true, 3.1)expected valuepassend test
Notes
Any string that parses as an Int will also parse as a Float.
If the parse fails, the second value will become zero, so you should always check the first value to see if the second value is a correct parse or just the default.
You can 'deconstruct' the Tuple into two variables:
variable success, parsedValuename set to parseAsInt(myString)
One use of these parsing methods is for validating user input, but note that an easier way to do this is to use the various input methods.
floor, ceiling, round, isNaN, and isInfinite
All of these functions are called as 'dot methods' on a numeric value of Type Float or Int.
NaN is short for 'Not A (Real) Number' Their use is illustrated with the following tests:
+test let n be piassert n.floor() is 3passassert n.ceiling() is 4passassert n.round(3) is 3.142passassert sqrt(-1).isNaN() is truepasslet x be 1/0assert x.isInfinite() is truepassend test
Maths functions
All the maths functions take a Float argument and return a Float value.
function
argument Type
input unit
returns
output unit
abs
Float
absolute value of the input
acos
Float
arccosine of the input
radians
asin
Float
arcsine of the input
radians
atan
Float
arctangent of the input
radians
acosDeg
Float
arccosine of the input
degrees
asinDeg
Float
arcsine of the input
degrees
atanDeg
Float
arctangent of the input
degrees
cos
Float
radians
cosine of the input
cosDeg
Float
degrees
cosine of the input
exp
Float
𝑒𝑥 where 𝑥 is the argument and 𝑒 is Euler's number 2.718281828459045.. the base of natural logarithms
logE
Float
natural logarithm of the input
log10
Float
base-10 logarithm of the input
log2
Float
base-2 logarithm of the input
sin
Float
radians
sine of the input
sinDeg
Float
degrees
sine of the input
sqrt
Float
positive square root of the input
tan
Float
radians
tangent of the input
tanDeg
Float
degrees
tangent of the input
degToRad
Float
degrees
converts input from degrees to radians
radians
radToDeg
Float
radians
converts input from radians to degrees
degrees
Examples of some maths functions and constants being tested:
+test optional descriptionassert picomputed value is 3.141592653589793expected valuepassassert abs(-3.7)computed value is 3.7expected valuepassassert asin(0.5).round(3)computed value is 0.524expected valuepassassert acos(0.5).round(3)computed value is 1.047expected valuepassassert atan(1).round(2)computed value is 0.79expected valuepassassert sin(pi/6).round(2)computed value is 0.5expected valuepassassert cos(pi/4).round(3)computed value is 0.707expected valuepassassert tan(pi/4).round(2)computed value is 1expected valuepassassert exp(2).round(3)computed value is 7.389expected valuepassassert logE(7.389).round(2)computed value is 2expected valuepassassert log10(1000)computed value is 3expected valuepassassert log2(65536)computed value is 16expected valuepassassert log2(0x10000)computed value is 16expected valuepassassert sqrt(2).round(3)computed value is 1.414expected valuepassend test
Regular expressions
Elan's regular expressions are modelled on those of JavaScript, including the syntax for literal regular expressions. See, for example this Guide to Regular Expressions.
More functions for using regular expressions will be added in a future release of Elan. For now…
The method matchesRegExp is applied to a String using dot syntax and requires a RegExp parameter specified as a literal or as variable. It returns a Boolean. For example:
+test optional descriptionlet s1name be "hello"expressionlet s2name be "World"expressionlet rname be /^[a-z]*$/expressionassert s1.matchesRegExp(r)computed value is trueexpected valuepassassert s2.matchesRegExp(r)computed value is falseexpected valuepassend test
You can convert a valid string without /../ delimiters to a RegExp using function asRegExp:
+test optional descriptionlet s1name be "hello"expressionlet s2name be "World"expressionlet rname be "^[a-z]*$".asRegExp()expressionassert s1.matchesRegExp(r)computed value is trueexpected valuepassassert s2.matchesRegExp(r)computed value is falseexpected valuepassend test
Although it is recommended that literal regular expressions are written with /../ delimiters, the ability to convert a string allows you to input a regular expression into a running program.
Bitwise functions
These functions take in an integer value, and manipulate the bit representation of that value.
function
argument Types
return Type
returns
bitAnd
Int, Int
Int
integer whose binary representation is the bitwise AND of the inputs
bitOr
Int, Int
Int
integer whose binary representation is the bitwise OR of the inputs
bitNot
Int
Int
integer whose binary representation is the bitwise complement of the input
bitXor
Int, Int
Int
integer whose binary representation is the bitwise XOR of the inputs
bitShiftL
Int, Int
Int
integer bit shifted to the left by the value of the second argument, i.e. multiplied by 2 to the power of the second argument
bitShiftR
Int, Int
Int
integer bit shifted to the right by the value of the second argument, i.e. divided by 2 to the power of the second argument
Examples of the bitwise functions being tested
▶ Examples of the bitwise functions being tested
+test bitwiseoptional descriptionvariable aname set to 13expressionassert acomputed value is 0xdexpected valuepassassert acomputed value is 0b1101expected valuepassassert a.asBinary()computed value is "1101"expected valuepassvariable bname set to 30expressionassert bcomputed value is 0b11110expected valuepassassert bitAnd(a, b)computed value is 0b1100expected valuepassvariable aobname set to bitOr(a, b)expressionassert aobcomputed value is 0b11111expected valuepassvariable axbname set to bitXor(a, b)expressionassert axbcomputed value is 0b10011expected valuepassvariable notaname set to bitNot(a)expressionassert notacomputed value is -14expected valuepassvariable aLname set to bitShiftL(a, 2)expressionassert aLcomputed value is 0b110100expected valuepassassert bitShiftR(a, 2)computed value is 0b11expected valuepassend test
The result of bitNot(a) being -14 , when a is 13, might be a surprise. But this is because the bitwise functions assume that the arguments are represented as 32-bit signed binary integers. So 13 is represented as 00000000000000000000000000001101, and applying bitNot gives 11111111111111111111111111110010 which is the value -14 in signed two’s complement format, the leftmost bit being the sign (0 positive, 1 negative).
Single bit operations
▶ A reminder of single bit operations:
p,q
0,0
0,1
1,0
1,1
bitAnd(p,q)
0
0
0
1
bitOr(p,q)
0
1
1
1
bitNot(p)
1
1
0
0
bitXor(p,q)
0
1
1
0
As noted in the example tests above, these methods act on 32-bit signed integers rather than on 1-bit values,
so the effect of bitNot is not just the simple bit reversal shown here.
Sequence and SequenceWithStep
The sequence function is used to create a List containing a sequence
of integer values as defined by the two integer parameters: start and end
(both being inclusive values).
Here is an example which uses Higher-order Functions to print the prime numbers between 2 and 30:
print sequence(2, 30).filter(lambdanasInt => sequence(2, sqrt(n).floor()).reduce(true, lambdacasBoolean, masInt => c and ((n mod m) isnt 0)))
The sequenceWithStep function is similar to sequence but takes a third integer parameter
specifying the step size, which may be positive or negative.
pauses the execution of a program for the specified number of milliseconds, e.g. for a game pause(1000) delays execution for one second
clearPrintedText
(none)
(none)
clears the IDE's Display pane
clearBlocks
(none)
(none)
clears the block graphics layer of the IDE's Display pane
clearVectorGraphics
(none)
(none)
clears the vector graphics layer of the IDE's Display pane
clearHtml
(none)
(none)
clears the Html layer of the IDE's Display pane
clearAllDisplays
(none)
(none)
clears Display pane as well as the blocks, vector graphics and Html layers
clearKeyBuffer
(none)
(none)
clears the IDE's keyboard input
pressAnyKeyToContinue
Boolean
(none)
pauses execution of the program until a keyboard key is pressed
if the argument is true 'Press any key to continue' is first displayed,
if false nothing is displayed
printLine
String
(none)
prints the string to the Display followed by a newline.
This method offers the means to do all printing via methods
rather than coding a mixture of call and print statements
printNoLine
String
(none)
prints the string to the Display without appending a newline so
a following call will output on the same line.
You can put your own \n newlines in the argument string
printTab
Int, String
(none)
prints the string to the Display starting at the tab position given (from 0)
Method printTab helps in the layout of information printed to the Display, in particular, when printing columns of data.
For example:
call printTabprocedureName(0, "Number"position, text)call printTabprocedureName(10, "Square"position, text)call printTabprocedureName(20, "Cube\n"position, text)+for ivariableName from 1expression to 10expression step 1expressioncall printTabprocedureName(0, i.asString()position, text)call printTabprocedureName(10, "{i^2}"position, text)call printTabprocedureName(20, "{i^3}\n"position, text)end for
Right-align numeric output (powers of 9) using a lambda:
let tab be 10+for i from 1 to (tab - 1) step 1let j be 9^ilet f be lambdajasInt => j.asString().length()call printTab(tab - f(j), "{j}\n")end for
System methods
System methods appear to work like functions, because:
they may require one or more arguments to be provided.
they always return a value.
they are used in expressions.
They are not, however, pure functions because:
They may have a dependency on data that is not provided as an argument.
They may generate side effects, such as changing the screen display or writing to a file.
Because of these properties, system methods may be used only within the main routine or within a procedure.
They may not be used inside a function that you have defined, because to do so would mean that your function would not be pure.
System methods are all defined within the standard library: you cannot write a system method yourself.
System methods are commonly associated with Input/output, but note that:
Input/output may be performed in procedures as well as in main.
Some system methods are not concerned with input/output, e.g. some generate random numbers.
The reason those are system methods is that they have a dependency on data that is not passed into them as arguments.
system method
argument Types
return Types
action
clock
(none)
Int
returns the current value (in milliseconds) of a system clock. Useful for measuring elapsed time by comparing the values returned by two calls
returns the last character pressed on the keyboard during program execution
getKeyWithModifier
(none)
2-tuple: (String, String)
returns both the character of the keyboard key pressed and the modifier key's name if also pressed
inputString
String
String
prints the string as a prompt, and returns the typed input when Enter is pressed
inputStringWithLimits
String, Int, Int
String
prints the string as a prompt and returns the typed input when Enter is pressed
provided the length of the response is within the minimum and maximum limits specified.
If it is not, the prompt is repeated with the relevant limit displayed
inputStringFromOptions
String, List<of String>
String
prints the string as a prompt and returns the typed input when Enter is pressed
provided it is one of the options in the list.
If it is not, the prompt is repeated with the options displayed
inputInt
String
Int
prints the string as a prompt and returns the value of the typed input when Enter is pressed
provided that it is an integer. If it is not, the prompt is repeated with an error message
inputIntBetween
String, Int, Int
Int
prints the string as a prompt and returns the value of the typed input when Enter is pressed
provided that it is an integer with a value in the (inclusive) range. The first range value must be less than or equal to the second
inputFloat
String
Float
prints the string as a prompt and returns the value of the typed input when Enter is pressed
provided that it is a number. If it is not, the prompt is repeated with an error message
inputFloatBetween
String, Int, Int
Float
prints the string as a prompt and returns the value of the typed input when Enter is pressed
provided that it is a number with a value in the (inclusive) range. The first range value must be less than or equal to the second
pauses execution of the program until a key is pressed on the keyboard,
and returns either a character or the name of a non-character key
Note
Methods random and randomInt cannot be used in a function because
of their dependence on external factors, i.e. they are subject to side effects.
To obtain random numbers within a function, use Type Random.
Higher-order Functions (HoFs)
A higher-order function (HoF) is one that takes in a reference to another function as a parameter, or (less commonly) that returns a reference to another function as its result.
Standard HoFs
The standard library contains several HoFs that are widely recognised and used within functional programming, namely
filter,
map and
reduce, and also provides
maxBy,
minBy and
sortBy.
Passing a function as a reference
To use a higher-order function (HoF), you have to pass in a reference to a function.
This can be either a lambda or the keyword ref followed by the name of a function.
It is also possible to use references to functions not in the context of HoFs.
On most occasions when you write the name of an existing function elsewhere in code your intent is to evaluate the function and, to do so, you write the name of the function followed by brackets containing such arguments as are required by the function. For this reason if you forget to add the brackets, you will get an error, for example:
To evaluate function 'myFunction' add brackets. Or to create a reference to 'myFunction', precede it by 'ref'.
The second sentence in this error message is for when your intention is not to evaluate the function, but to create a reference to the function. This is a valid thing to do in functional programming but is not generally done in procedural programming. As the error message says, to create a reference to a function you need to precede it by ref and the name of the function should then not be followed by brackets (or any arguments). For example:
+class PupilNameproperty mathsPercent as Intend class+mainlet allPupils be newList<ofPupil>()variable passes set to allPupils.filter(refpassedMathsTest)end main+function passedMathsTest(pasPupil) returns Booleanreturn p.mathsPercent > 35end function
Defining your own HoFs
Library functions that process Lists
These dot methods are called on any List,ListImmutable or String. As Higher-order Functions they take either a lambda or a function reference as one of their arguments: see above.
Although several of these methods return a ListImmutable, they may be converted to a List using method asList.
These are not yet fully documented but, for readers familiar with HoFs from another programming language, some examples are shown below.
filter
Example using filter
▶ Example from demo program pathfinder.elan in which filter applies the lambda to the nodes (of class Node) to find one that contains p (of record Point):
+function getNodeForname(pasPointparameter definitions) returns NodeTypelet matchesname be property.nodes.filter(lambdanasNode => n.point is p)expressionreturn if matches.length() is 1 then matches.head() else emptyNodeexpressionend function
map
The function map is used to apply a function to every element of a List, returning a new list.
The function to be applied is usually specified as a lambda having a single argument of the Type of the list's elements, as in these examples:
Examples using map
▶ To cube each integer value in a list:
let li be [1, 2, 3, 4, 5]print li.map(lambdanasInt => n^3)
⟶ [1, 8, 27, 64, 125]
▶ To change each string in a list to upper case:
let names be ["Tom", "Dick", "Harriet"]print names.map(lambdasasString => s.upperCase())
⟶ ["TOM", "DICK", "HARRIET"]
▶ To reverse each string in the list of names using the function reverse
(from the example of the each loop):
▶ And this example from demo program maze-generator.elan:
let n be p.neighbouringPoints().map(lambdapasPoint => getValue(p, g))
reduce
The function reduce is used to apply a function (F) to every element of Type (E)
in a List or ListImmutable(L),
and return a single value which may be of any Type (T).
As shown in this table, reduce takes two arguments, the second being a function that also takes two arguments.
reduce arguments
Type
description
1
(T) Type of the function's output
initial value used by the function, and the default return value if the function doesn't change it
2
(F) Function
either a lambda or a function reference
lambda or function arguments
Type
description
1
(T) Type of the function's output
cumulative value created by the function
2
(E) Type of input list's items
refers successively to each item in the list
Examples using reduce
▶ To reduce the integer values in a list to their sum:
let li be [2, 3, 4, 7, 11]print li.reduce(0, lambdamasInt, nasInt => m + n)
⟶ 15
▶ To reduce the floating point values in a list to their sum:
let lj be [0.1, 2.5, 2, 0.3, 5.7, 0.2]print lj.reduce(0.0, lambdafsumasFloat, itemasFloat => fsum + item).round(1)
⟶ 10.8
Notes
You usually need to round floating point values when outputting them.
The inclusion of an integer in a List of floats is valid provided it is not the first value.
▶ And this example on a DictionaryImmutable from demo program wordle-solver.elan:
let d2 be possAnswers.reduce(d, lambdaddasDictionaryImmutable<ofString, Int>, answerasString => incrementCount(dd, answer, attempt))
maxBy and minBy
maxBy and minBy are dot functions of List and ListImmutable.
They take a function as an argument, and return the list element corresponding to the maximum or minimum of the values returned by the function.
▶ To find in a list the integer that has the highest last digit:
let a be {33, 4, 0, 92, 89, 55, 102}print a.maxBy(lambdaxasInt => x mod 10)
⟶ 89
▶ To find in a list the the shortest string:
let fruit be {"apple", "orange", "pear"}print fruit.minBy(lambdaxasString => x.length())
⟶ pear
sortBy
sortBy takes a lambda that takes two arguments (of the same Type as that of the ListImmutable being sorted) and compares them, returning an integer with one of the values -1, 0, or +1, to indicate whether the first argument should be placed respectively before, adjacent to or after the second argument in the sorted result, where ‘adjacent to’ means it does not matter whether before or after):
Examples using sortBy
▶ To sort integers in a List:
let numbersname be [27, 2, 3, 5, 7, 31, 37, 11, 13, 17, 19, 23]expressionlet sortedname be numbers.sortBy(lambdaxasInt, yasInt => if x > y then 1 else if x < y then -1 else 0)expressionprint sortedexpression
⟶ [2, 3, 5, 7, 11, 13, 17, 19, 23, 27, 31, 37]
▶ And similarly for string comparisons using methods isBefore and isAfter:
let namesname be ["Simon", "Pauline", "Jason", "Zelda", "Edith", "Lance", "Alice", "Paul"]expressionlet sortedname be names.sortBy(lambdaxasString, yasString => if x.isAfter(y) then 1 else if x.isBefore(y) then -1 else 0)expressionprint sortedexpression
⟶ [Alice, Edith, Jason, Lance, Paul, Pauline, Simon, Zelda]