Overview
The SCAN statement ‘sc’ iterates over data structures in the MUFL programming language, providing options for binding keys and values, nested iteration, filtering, and conditional execution. Here is a simple example that iterates over a list of integer numbers and prints them:
sc [1, 2, 3] -- (->value) { _print value "\n". }
Prints:
1
2
3
Syntax
scan := [label;]sc <source> <layers> { body }
source := <any mufl expression>
layers := -- <layer> -- <layer> ...
layer := range-optional bindings-optional explicit-value-or-set-optional condition-optional
range := [ begin-value-optional .. end-value-optional ]
bindings := ( key-name-optional -> value-name-optional )
explicit-value := = value
explicit-set := = (nested-scan)
condition := ?? expression
Note, that
label
is marked with semicolon
Layers
The ‘sc’ statement is designed to iterate over data structures that are often nested. Consequently, each layer expected within the data structure requires an instruction in the scan statement. Layers of the statement are always preceded by special delimiter --
Components
label
- Optionally, names the loop for purposes of using the name inbreak
andcontinue
statements.source
- The data structure to iterate over, such as a list or dictionary.range
- Has the form[begin..end]
, for example[1..10]
. Range is an optional instruction to begin and end the iteration at a specified value.bindings
- The bindings clause names the variables that will be bound to the keys and values of the data structure during the iteration. For example, binding(a->b)
instructs the evaluator to bind the keys to namea
and values to nameb
condition
- The condition clause starts with??
and takes a predicate expression. The evaluator uses the condition clause to filter the values being iterated over – values that don’t pass the condition expression will be skippedexplicit-value-or-set
- This instruction specifies a sub-set of values aside of which all others will be skippedExamples
1. Basic Iteration
Iterating over a list and printing its elements. This scan statement as a single layer, and binds all the values of the list to name value
using the binding instruction (->value)
. Note that the key binding is omitted.
scan [1, 2, 3] has value do _print value "\n". end
Prints:
1
2
3
2. Iterating with Keys
Iterating over a dictionary and printing key-value pairs:
sc ("a"->1, "b"->2, "c"->3) -- (key->value) { _print key " -> " value "\n". }
Prints:
a -> 1
b -> 2
c -> 3
3. Nested Iteration
Iterating over a nested data structure. Here we introduce a second layer into the statement, because the data structure is a two-layer dictionary representing a two-dimentional matrix of numbers.
sc [[1,2],[3,4]]
-- (i->) // bind keys to name i
-- (j->val) // bind keys to j and values to val
{
_print "[" i "," j "]=" val "\n".
}
Prints:
[0,0]=1
[0,1]=2
[1,0]=3
[1,1]=4
4. Filtering with Conditions
Using a filter condition to print only the elements with odd values:
sc [1, 2, 3, 4, 5]
-- (->value)??value % 2 == 1
{
_print value "\n".
}
Prints:
1
3
5
5. Filtering with Nested Scan
Using a nested scan statement to filter elements based on another data structure:
sc [1,2,3,4,5]
-- (->value) =(sc [0,2,3]) // only iterate the provided list over a subset of indices 0,2,3
{
_print value "\n".
}
Prints:
1
3
4
6. Range-based Filtering
Using [from..until]
to define a range of keys:
sc [1,2,3,4,5,6,7,8]
-- [2..4] (->value)
{
_print value "\n".
}
Prints:
3
4
5
the end-value of the range is inclusive. Add
<
as follows to specify that the end of the range is exclusive[begin..<end]
7. Destructuring Binding
Values in the scan
statement can be destructured on the fly using standard patterns.
For more information, refer to Destructuring with the bind Statement.
Binding multiple variables to elements in the data structure:
sc [[1, "hello"], [2,"world"]] -- (i->[number, name])
{
_print i "->[" number "," name "]\n".
}
Prints:
0->[1,hello]
1->[2,world]
8. Destructuring Binding and Filtering with Conditions
When filtering values with the condition clause, the condition may refer to any variables accessible in the current scope, as well as any named values that precede it, including the names of destructured values.
Modifying the preceding example to make use of a where condition that accesses the key i
and the destructured value number
:
sc [[1, "hello"], [2,"world"]]
-- (i->[number, name])??number % 2 == 0
{
_print i "->[" number "," name "]\n".
}
Prints:
1->[2,world]
9. Using number sequences with scan
Instead of the traditional for-loop construct, use the sc
statement to iterate over a number sequence.
Sum all numbers between 1 and 10 inclusive:
sum = 0.
sc (1..10) -- (->val)
{
sum -> sum + val.
}
_print sum.
Prints:
55