↑ Up |
The first thing one can basically do is to use Moss as a calculator. Just type in some mathematical expression and then press the return-key.
> 1+2 3
There is no invisible times, the multiplication
operator (*)
has always to be stated explicitly, wich is
a good thing. This allows us to have multiple letter identifiers and
an invisible function application operator.
These concepts will be explained later.
The division operator is the slash (/)
.
Only round brackets can be used to undermine the order of
operations. Square brackets and curly brackets have another purpose.
The expression "10 to the power of two" is written
"10^2"
.
> (2+4)*(4-2) 12 > 10^2 100
For integer division, use the the operator "//
"
instead of "/
". The remainder of the division is
calculated by the modulo operator "%"
.
> 14/5 2.8 > 14//5 2 > 14%5 4
It is possible to store the value of an expression in a variable.
The statement "a=12"
means that the value 12 is assigned
to the variable a
. If a
afterwards occurs
itself in an expression, it has this value. So the variable contains
this value until some other value is assigned to it.
> a=12 > 2*a 24 > a=10*a > 4*a 480
A variable is represented via an identifier, in this case
"a"
. An identifer can consist of more than one letter.
Some or all of the letters can be capital letters, but
Omega
is different from omega
. That means,
in Moss identifiers are case sensitive.
We can test, whether two numbers are equal or not. The expression
"a is equal to b" is written "a==b
".
> a=360 > a==360, a==240, a!=360, a!=240 [true, false, false, true]
And we can compare two numbers with respect to their order.
> 1<2, 1>2, 1<=2, 1>=2 [true, false, true, false]
To get the minimum and maximum of two numbers,
use the functions min(x,y)
and max(x,y)
.
Calculations with complex numbers are possible.
> (4+2i)*(2+1i) 6+8i
Moss can calculate really big numbers.
> 2^10 1024 > 2^1000 1071508607186267320948425049060001810561404811705533607443750388370351 0511249361224931983788156958581275946729175531468251871452856923140435 9845775746985748039345677748242309854210746050623711418779541821530464 7498358194126739876755916554394607706291457119647768654216766042983165 2624386837205668069376
Typename | Example data | Meaning |
---|---|---|
Bool
| false, true
| logical values |
Int
| 0, 1, 2, -1
| integer numbers |
Float
| 0.0, 4.2, 1/3
| floating point numbers |
Complex
| 1i, 2+4i, -3.2i
| complex numbers |
String
| "", "ab"
| strings of characters |
List
| [], [1,2]
| lists of elements |
Map
| {}, {a=1, b=2}
| maps of key-value pairs |
Range
| 1..10, (1..)
| ranges of elements |
Function
| |x| 2*x
| functions |
Think of the expressions 4+2*1, 4+2*2, 4+2*3, 4+2*4
.
We want to abstract the pattern 4+2*x
from these expressions. To achive this, we create a function
and store it in the variable f
.
> f = |x| 4+2*x
This is a function wich takes the value of the variable
x
as its input and returns the value of 4+2*x
as output. The x
is a variable,
but it is bound to the function because it appears between
the vertical bars. Because of this the variable x
is called formal argument of the function.
Now, if a
is some
number, we can write f(a)
to calculate the value
of 4+2*a
. The number a
is called actual
argument of the function. This makes us able to compute the
values without typing in the same pattern again and again.
Think of much longer patterns.
> f(1),f(2),f(3),f(4) [6, 8, 10, 12]
It is possible to abstract this further.
> [1,2,3,4].map(f) [6, 8, 10, 12] > list(1..4).map(f) [6, 8, 10, 12] > f[1..4] [6, 8, 10, 12]
A function may have more than one argument. We will state a function that has two arguments. There is more than one way to achieve this. The first one uses two formal arguments. Formal arguments appear between the vertical bars and are separated there by commata.
> g = |x,y| x*y > g(3,4) 12
The next way is a technique known as currying.
> g = |x| |y| x*y > g(3)(4) 12
The next way is to have a list as an argument instead.
> g = |t| t[0]*t[1] > g([3,4]) 12 > g = |[x,y]| x*y > g([3,4]) 12
The next way is to have a map of two named arguments.
> g = |m| m["x"]*m["y"] > g({x=3,y=4}) 12 > g(x=3,y=4) 12 > g = |{x,y}| x*y > g(x=3,y=4) 12
If a function not only consists of a single expression, but of a sequence of statements, a longer syntax has to be used.
# (1) Short syntax f = |x| 2*x # (2) Longer syntax f = fn|x| 2*x end # (3) Full syntax f = fn|x| return 2*x end # (4) Function with private name "f" f = fn f|x| return 2*x end # (5) Equivalent to (4) function f(x) return 2*x end
A function can contain statements and local variables. Here is an example, an approximation of the natural exponential function:
function exp(x) u = x/64 y = 1 + u + u^2/2 + u^3/6 + u^4/24 return y^64 end
In Moss, all kinds of objects can be stored in variables.
A string of characters is also an object, and thus can
be stored in a variable. The function print
writes
an object to the output.
> s = "London"
> s
"London"
> print(s)
London
The function str
transforms an integer into a string.
The function int
does the reverse.
> str(360) "360" > int("360") 360
Called on a string, str
does not change anything.
To int
applies the same.
> str("Flower") "Flower" > int(12) 12
Two strings can be added, this will glue both strings together. Therefore addition of strings is associative.
> s = "Night" > s+"fall" "Nightfall"
The empty string ""
is to strings like the number zero
is to numbers.
> "Elm tree"+"" "Elm tree"
We can test, whether two strings are equal or not.
> s = "Mouse" > s=="Mouse", s=="Mice", s!="Mouse", s!="Mice" [true, false, false, true]
The function len
takes a string and returns
the number of characters.
> len("Bee") 3 > len("") 0
The result of the expression s[i]
is the character at
index i
. Furthermore the result of the expression
s[i..j]
is the substring
s[i]+s[i+1]+...+s[j-1]+s[j]
.
> s = "Elm tree" > s[0], s[2], s[-4], s[-1] ["E", "m", "t", "e"] > s[0..2] "Elm" > s[..2] "Elm" > s[4..] "tree" > s[-2..] "ee" > s[4..-3] "tr"
Note that s[i..-1]
is a special case:
an empty slice for every index i
. You can bypass this
behavior by writing s[i..]
or
s[i..len(s)-1]
.
There are some escape sequences that provide the notation of arbitrary Unicode characters inside of a string literal.
Sequence | Meaning |
---|---|
\n | new line |
\s | space |
\t | tabulator |
\b | backslash |
\d | double quote |
\q | single quote |
\x{61} | Unicode character 0x61
|
An example:
print("\b\b\b\b\na\s\sb\n\d\q\q\d\n\x{61}\x{62}\x{63}") # Output: # \\\\ # a b # "''" # abc
A triple quoted string literal can span over more than one line.
s = """ Multi line string """
If space is placed directly behind a backslash, this space is overlooked until some non spacing character is found.
print("a\ b") # output: # ab s = """\ Multi\s\ line\s\ string\ """ print(s) # output: # Multi line string
In Moss, lists are dynamic arrays (not doubly linked lists). This is a fundamental, albeit easy to understand, data structure.
# Construction of a list with elements 4,9,2,6. > [4,9,2,6] [4, 9, 2, 6] # A list can be empty. > [] [] # We can assign a list to a variable. > a = [1,2,3,4] > a [1, 2, 3, 4] # Every element of a list can be of a different type. # A list can contain lists as elements. > ["abc",12,[1,2]] ["abc", 12, [1, 2]]
The expression list(a..b)
creates a list of
integers from a
to b
.
> list(1..10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
We can transform a string into a list of characters. The inverse transform is also possible.
> list("abc") ["a", "b", "c"] > ["a","b","c"].join() "abc"
A list can be used as a dynamic stack.
> a=[] > a.push(2) > a.push(3) > a [2, 3] > a.pop() 3 > a [2]
Lists can be added. The result is a concatenation.
> [1,2]+[3,4] [1, 2, 3, 4] > [1,2]+[] [1, 2]
Lists are indexed like strings. The function len(a)
takes a list and returns the number of its elements.
> a = [4,5,6,7,8] > a[0], a[1], a[-1], a[-2] [4, 5, 8, 7] > a[2..3] [6, 7] > len(a) 5
To reverse a list, write a.rev()
. This operation
reverses the list itself, but also returns the result.
> list(1..4).rev() [4, 3, 2, 1] > a = list(1..4) > a.rev() [4, 3, 2, 1] > a [4, 3, 2, 1] > a = list(1..4) > copy(a).rev() [4, 3, 2, 1] > a [1, 2, 3, 4]
To reverse only a part of the list, use a slice assignment:
> a = list(1..10) > a[0..3] = a[0..3].rev() > a [4, 3, 2, 1, 5, 6, 7, 8, 9, 10]
Two elements of a list are swappend as follows:
> a = list(1..10) > a.swap(0,3) > a [4, 2, 3, 1, 5, 6, 7, 8, 9, 10]
Or this way:
> a = list(1..10) > a[0],a[3] = a[3],a[0] > a [4, 2, 3, 1, 5, 6, 7, 8, 9, 10]
It is possible to swap slices:
> a = list(1..10) > a[0..1],a[2..3] = a[2..3],a[0..1] > a [3, 4, 1, 2, 5, 6, 7, 8, 9, 10] > a = list(1..10) > a[0..2],a[3..4] = a[2..4],a[0..1] > a [3, 4, 5, 1, 2, 6, 7, 8, 9, 10] > a = list(1..10) > a[0],a[1..3] = a[3],a[0..2] > a [4, 1, 2, 3, 5, 6, 7, 8, 9, 10]
We can rotate the last three elements of a list:
> a = list(1..10) > a[-1],a[-3],a[-2] = a[-3..] > a [1, 2, 3, 4, 5, 6, 7, 9, 10, 8]
A map (also called dictionary) can be seen as a generalisation of a list. Like in a list an element of a map belongs to an index, called key. But such a key is not restricted to integers.
> m = {} # empty map > m = {0: "a", 1: "b", "alpha": "c"} > m[0] "a" > m["alpha"] "c"
The operation "k in m"
is used to test whether the key
k
is contained in the map m
or not.
> 0 in m, "alpha" in m, "beta" in m [true, true, false] > 0 not in m, "alpha" not in m, "beta" not in m [false, false, true]
The function call list(m)
returns the list
of keys contained in m
.
The keys can be in any order, because a map
is not (explicitly) ordered.
> list(m) [0, 1, "alpha"]
If you are interested in the values or key-value pairs, simply express this by methods.
> list(m.values()) ["a", "b", "c"] > list(m.items()) [[0, "a"], [1, "b"], ["alpha", "c"]]
If the key is an identifier, there is a shorthand notation. Instead of
{"x": 4, "y": 3}
one may write:
{x=4,y=3}
But note that this is not the same as {x:4,y:3}.
Before a colon, x,y
are itself variables that should
contain keys.
> x="u"; y="v" > {x:4,y:3} == {u=4,v=3} true
If the value of some key is omitted, null
is
taken as its value. This value, null
, is a special
object that represents absence of something.
A map with all values omitted can be used as a set.
All set operations are supported (e.g. union, intersection,
subset relation, universal quantifier).
> {1,2} == {1:null, 2:null} true > A = {1,2,3} > B = {2,3,4} > A|B, A&B, A-B, A$B [{1, 2, 3, 4}, {2, 3}, {1}, {1, 4}] # union, intersection, difference, symmetric difference
One can use set(a)
to convert an iterable
object a
into a set. A set is itself iterable.
For any iterable object A
, things like
A.map(f)
(image of A
under f
)
A.all(p)
(universal quantifier)
A.any(p)
(existential quantifier)
> set(1..4) {1, 2, 3, 4} > set((1..4).map(|x| 2*x)) {2, 4, 6, 8} > {2,4,6,8}.all(|x| x%2==0) true
But note that the elements of a set can be in any order. If you wish to pretty print a set, it should be sorted.
> A = set(1..4) > A.sort() [1, 2, 3, 4]
Suppose we want to write a program that asks a character from the user and prints the Unicode-value of this character. And this program should be repeated endlessly. In a loop, if all statements are executed, we jump to the beginning of the loop to execute them again.
while true c = input() print(ord(c)) end
This program is so short that it can be written as an one-liner.
while true do print(ord(input())) end
Maybe you already know about the nesting problem. That is the question to which opening keyword or bracket a closing "end" or bracket belongs if there are many of them. Have a look at the programming language Lisp and you will understand.
A control flow statement can also be written in a longer form, stating explicitly what shall end. This is useful if the body of the loop is longer than one page.
while true c = input() print(ord(c)) end of while
The user might input more than one character. We take this into account in the following way.
while true a = list(input()) print(a.map(ord)) end
A standard task is to print the numbers from one to ten.
i = 1 while i<=10 print(i) i = i+1 end
For arithmetic operators, there is a shorthand syntax:
i+=x
is the same as i=i+x
,
i-=x
is the same as i=i-x
,
i*=x
is the same as i=i*x
,
The numbers in a given range are printed better by a for-loop than by a while-loop.
for i in 1..10 print(i) end
By step two.
for i in 1..10: 2 print(i) end
In reverse.
for i in 10..1: -1 print(i) end
A range of characters.
for i in 'a'..'d' print(i) end
A list instead of a range.
for i in [4,2,1,9] print(i) end
It is also possible to iterate over the characters of a string.
for i in "abc" print(i) end # Output: # a # b # c
Maps are iterable.
for key in {a=1,b=2} print(key) end for value in {a=1,b=2}.values() print(value) end for t in {a=1,b=2}.items() print(t) end # Output: # ["a", 1] # ["b", 2] # or # ["b", 2] # ["a", 1] for key,value in {a=1,b=2}.items() print(key,"|",value) end # Output: # a|1 # b|2 # or # b|2 # a|1
One can iterate over cartesian products.
for i,j in ["a","b"]*[0,1] print(i,j) end # vs. for i in ["a","b"] for j in [0,1] print(i,j) end end # Output: # a0 # a1 # b0 # b1
The following program produces the 9×9 multiplication table.
for i in 1..9 for j in 1..9 put(str(i*j).rjust(3)) end print() end # Output: # 1 2 3 4 5 6 7 8 9 # 2 4 6 8 10 12 14 16 18 # 3 6 9 12 15 18 21 24 27 # 4 8 12 16 20 24 28 32 36 # 5 10 15 20 25 30 35 40 45 # 6 12 18 24 30 36 42 48 54 # 7 14 21 28 35 42 49 56 63 # 8 16 24 32 40 48 56 64 72 # 9 18 27 36 45 54 63 72 81
A for-loop can include the index for each element.
Let i
be the index, and x
the element.
for i,x in "abc".enum() print(i,"|",x) end # Output: # 0|a # 1|b # 2|c a = [1,2,3,4] for i,x in a.enum() for y in a[i+1..] print([x,y]) end end # Output: # [1, 2] # [1, 3] # [1, 4] # [2, 3] # [2, 4] # [3, 4]
In a program we often have to make a branch. Depending on a condition the program has to perform either in one way or in another.
x = int(input("A number: ")) if x%2==0 print("This number is even.") else print("This number is odd.") end
Conditional expressions are similar to conditional statements.
Let us take x
to the power
of n
as an example. Powers with natural exponents
can be defined recursively:
x0 := 1, xn := x*xn-1 (n>0).
In this case the definition can be implemented without further ado:
pow = |x,n| 1 if n==0 else x*pow(x,n-1)
A more complicated example:
theta = int(input("A temperature: ")) print( "freezing" if theta< 1 else "cold" if theta< 6 else "fresh" if theta<17 else "warm" if theta<24 else "hot" ) # By means of an if-statement if theta<1 print("freezing") elif theta<6 print("cold") elif theta<17 print("fresh") elif theta<24 print("warm") else print("hot") end
As our programs are getting bigger, there is the need of using some piece of functionality multiple times.
function count(s,c) k = 0 for x in s if x==c then k = k+1 end end return k end while true s = input() print("Number of left brackets: ", count(s,"(")+count(s,"[")) print("Number of right brackets: ", count(s,")")+count(s,"]")) end
It is complicated to solve such a task without subprograms.
We can give a function a private name. Then the assignment statement has an alternative syntatic form.
# (1) A function without a private name, # called anonymous. f = fn|x| 2*x end # (2) The same function, but with private name "f". f = fn f|x| 2*x end # (3) Syntactic sugar for statement (2). # This is an assignment to the variable f, # but the assignment operation is invisible. function f(x) 2*x end
So we could have written count = fn|s,c|
or
count = fn count|s,c|
instead
of function count(s,c)
.
Additional functionality is provided by importing modules.
For example, there is a module called math
, that
provides the elementary functions.
# ordinary import use math f = |x| math.sin(math.pi*x) # qualified import use math: pi, sin, cos f = |x| sin(pi*x) # alias import use m = math f = |x| m.sin(m.pi*x) # qualified alias import use math: pi, s = sin, c = cos f = |x| s(pi*x) # qualification of everything # (should be avoided) use math:* f = |x| sin(pi*x) # load and qualify in separate steps math = load("math") use(math): pi, sin, cos # line breaks use math{ e, pi, exp, ln, sin, cos, tan } # show what is contained in math expose = |m| print(list(record(m)).sort().join(", ")) expose(math)
# Single line comment /* Multi line comment */ /* s = "Phantom text" print(s) #*/ /*01*/ stoi = fn|s| /*string to integer*/ /*02*/ s.reduce(0,|x,c| 10*x+ord(c)-48) /*03*/ end