Table of Contents
5.8 Functions
filter
, find
and filterMap
5.9 Functions forall
and exists
5.10 Functions count
and countRep
5.11 Functions sort
and
sortBy
List is the most important and most often used structured data type in functional programming languages. List is a sequential type, which means that a list may contain many elements in an ordered sequence. In Wafl, a list can consist of elements of any type, but all elements of any specific list must have the same type.
Because of the importance of the list processing concept, it is suggested that this chapter is carefully studied. The discussed functions are essential for programming in Wafl.
The general list type is written as List['1]
, where
'1
denotes a type variable (a pure polymorphic
type). Each specific list in a program must have a specialized
type, where this free type is fully determined.
A list without elements is called empty list. An empty list
is written as nil
or []
. These two syntaxes
are equivalent.
Nonempty list literals are written as elements enclosed in square brackets and separated by commas.
{#[],
nil,[1,2,3], // List[Int]
['a','b','c','d'], // List[String]
[[1,2],[3,4,5],[6,7,8]] // List[List[Int]]
#}
{# [], [], [1, 2, 3], ['a', 'b', 'c', 'd'], [[1, 2], [3, 4, 5], [6, 7, 8]] #}
In the previous example we have the following lists:
[]
;nil
;The processing of the list elements is often explained in terms of processing the list head and the list tail. The head represents the first element of the list and tail represents all but the first elements of the list. Both head and tail are meaningful only for non-empty list.
Basic list functions include elementary operations for checking the list size, extracting its head, tail or sub-list, constructing a list and comparing two lists.
Function / Type and Description
A = B
('1 * '1 -> Bool)
Equality comparison
operator.
A != B
('1 * '1 -> Bool)
Inequality comparison
operator.
empty
(Indexable['1]['2]['3] -> Bool)
Check if the
collection is empty.
length
(Indexable['1]['2]['3] -> Int)
Get the
collection size.
longerThan
(List['1] * Int -> Bool)
Check if list has more
elements than the given integer.
size
(Indexable['1]['2]['3] -> Int)
Get the
collection size.
hd
(Sequence['1]['2] -> '2)
Get the sequence head.
Not defined on empty sequence.
tl
(Sequence['1]['2] -> Sequence['1]['2])
Get the
sequence tail.
A : B
('1 * List['1] -> List['1])
List construction
operator.
It may be strange to see a type like
Indexable['1]['2]['3]
, but there is nothing special with
it. It is any of the indexable collections (where
'1
may be one of the indexable collections base, like list,
array, map and string) with index of type '2
and
elements of type '3
. So, this type may reduce to
List['3]
.
We could replace the types in the tables, but it is good to adapt to the notation.
Lists are comparable for equality. The operators ==
and
!=
evaluate the expected results:
{#[] = nil,
[] != nil,
[] = [1,2,3],
[] != [1,2,3],
[1,2,3] = [1,2,3],
[1,2,3] = [1,2,3,4,5],
[1,2,3] != [1,2,3],
[1,2,3] != [1,2,3,4,5]
#}
{# true, false, false, true, true, false, false, true #}
empty
To check if a list is empty, we can use function
empty(l)
or compare the list to the empty list literal.
{#empty( [] ),
empty( nil ),
empty( [1,2,3] ),
[] = nil
#}
{# true, true, false, true #}
size
,
length
and longerThan
Function length(l)
computes the length of the list.
Function size(l)
is a synonym.
It is planned to keep both in the future. size
is more
general, but length
is more natural for sequences.
{#length( [] ),
length( nil ),
length( [1,2,3] ),
size( [1,2,3] )
#}
{# 0, 0, 3, 3 #}
In some cases, it is not efficient to compute the length of a list.
For example, if a list represents a database query result, then the
length is not computable until the complete result is fetched from the
database. In such cases it is often useful to just check if the list
length is greater than a number. Function longerThan(lst,n)
checks if the lst
is longer than n
and return
true
if it is.
{#longerThan( [1,2,3], 0 ),
longerThan( [1,2,3], 1 ),
longerThan( [1,2,3], 2 ),
longerThan( [1,2,3], 3 ),
longerThan( [1,2,3], 4 ),
longerThan( [1,2,3], 5 )
#}
{# true, true, true, false, false, false #}
hd
, tl
and
A:B
Function hd(l)
computes the first element of the given
nonempty list. It is not defined on empty lists.
Function tl(l)
computes the tail of the given nonempty
list. The list tail is a list suffix consisting of all list
elements except the first one. When applied to an empty list, it returns
the same empty list.
Infix binary operator h:l
constructs a list with head
h
and tail t
. The operator type is
('1 * List['1] -> List['1])
. It is often called
cons-operator. (In many functional programming languages there
is a function cons
(constructs a list) equivalent
to this operator.)
{#hd( [1,2,3] ),
tl( [1,2,3] ),
1:[2,3],
1:2:3:nil,
[2,3]:nil
#}
{# 1, [2, 3], [1, 2, 3], [1, 2, 3], [[2, 3]] #}
List elements are usually processed using recursion:
We will try to apply this concept in few examples.
Let’s sum the elements of an integer list by applying the presented concept:
[1,2,3,4,5].sum()
where {
sum( lst ) =
if lst.empty() then 0
else lst.hd() + lst.tl().sum();
}
15
Let’s compute a list of squares of natural numbers from 1 to 10. We
may start from a list [1,2,...,10]
and use a recursive
function squares
:
squares
of the empty list is an empty list;squares
of the tail.[1,2,3,4,5,6,7,8,9,10].squares()
where {
squares( lst ) =
if lst.empty() then []
else lst.hd() * lst.hd() : lst.tl().squares();
}
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
The same result may be computed based on the same idea but without explicitly writing a list literal:
1,10)
squares(where {
squares( from, to ) =
if from > to
then []
else from * from : squares( from + 1, to );
}
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Like in the previous example, it is often useful to have a list of
integers in a specific range. In this example the function
mklst(n,m)
computes the list of integers from
n
to m
:
100,110)
mklst(where{
mklst(n,m) =
if n>m then []
else n : mklst( n+1,m);
}
[100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110]
In the examples of the previous section we may recognize some repeatability. Let’s focus on two topics:
It is usual to approach these two topics by two different functions.
First, we will write the function processElements
that
processes each of the list elements and return the results in a new list
in the same preserved order. Then, we will write function
combineElements
that combines the elements of the result
list as we need.
Function processElements
should have two arguments: the
list of the elements to process and the processing function. This
function is much like presented squares
, so let’s test it
by implementing the same thing, by applying it to the function
\x:x*x
:
[1,2,3,4,5].processElements( \x: x*x )
where {
processElements( lst, fun ) =
if lst.empty() then []
else lst.hd().fun() : lst.tl().processElements( fun );
}
[1, 4, 9, 16, 25]
Function combineElements
should have three arguments:
the list of the elements to combine, the function describing one
combining step and the starting value. Let’s test it on the summing -
the function is the operator+
and the starting value is
zero:
[1,2,3,4,5].combineElements( operator+, 0 )
where {
combineElements( lst, fun, value ) =
if lst.empty() then value
else lst.hd().fun( lst.tl().combineElements( fun, value ) );
}
15
Now we can use both functions for summing the squares of the numbers in a given list:
[1,2,3,4,5]
\x: x*x )
.processElements( +, 0 )
.combineElements( operatorwhere {
processElements( lst, fun ) =
if lst.empty() then []
else lst.hd().fun() : lst.tl().processElements( fun );
combineElements( lst, fun, value ) =
if lst.empty() then value
else lst.hd().fun( lst.tl().combineElements( fun, value ) );
}
55
As we can see, these two functions are very helpful. This is why the
equivalents of these two functions, as well as many other higher order
functions, are implemented in Wafl core library. The core library
function map
is equivalent for
processElements
, and foldr
is equivalent for
combineElements
.
Some basic list functions are presented in a previous section. Sere we present some more complex list functions, before we deal with higher order functions in the following sections.
Type SequenceStr['2]['1]
includes sequences and strings.
Sequences are indexable types with integer indexes.
sub
, subList
Function subList(l,p,n)
computes a sub-list of the given
list, beginning at position p
and with n
elements.
Function / Type and Description
sub
(SequenceStr['2]['1] * Int * Int -> SequenceStr['2]['1])
Extracts the subsequence from given 0-based position and given
length:
sub(seq,pos,len)
subList
(List['1] * Int * Int -> List['1])
Extracts the
sub-list from given 0-based position and given length:
subList(list,pos,len)
It has the same semantics as already presented sub
and
subStr
for strings. Moreover, sub
works for
lists as well.
{#sub( [0,1,2,3,4,5,6,7], 0, 3 ),
sub( [0,1,2,3,4,5,6,7], 2, 3 ),
sub( [0,1,2,3,4,5,6,7], -2, 5 ),
sub( [0,1,2,3,4,5,6,7], 5, 10 ),
sub( [0,1,2,3,4,5,6,7], 5, -2 )
#}
{# [0, 1, 2], [2, 3, 4], [], [5, 6, 7], [] #}
Function / Type and Description
A[ : B ]
(SequenceStr['2]['1] * Int -> SequenceStr['2]['1])
Extracts a prefix of the sequence with N elements:
seq[:N]
A[ B : ]
(SequenceStr['2]['1] * Int -> SequenceStr['2]['1])
Extracts a suffix of the sequence with N elements:
seq[N:]
A[ B : C ]
(SequenceStr['2]['1] * Int * Int -> SequenceStr['2]['1])
Extracts a sequence segment from Nth to (M-1)th element:
seq[N:M]
A[ B ]
(Indexable['1]['2]['3] * '2 -> '3)
Extracts an
element from the indexable collection:
col[idx]
Index operator lst[i]
and slice operators
lst[:n]
, lst[:n]
and lst[n:m]
have the same semantics as for strings, so we will not discuss it in
details here.
Note that indexing operator is not defined on empty lists.
{# [-4], lst[-3], lst[-2], lst[-1],
lst[0], lst[1], lst[2], lst[3],
lst[4], lst[6], lst[7], lst[8]
lst
#}where {
lst = [0,1,2,3];
}
{# 0, 1, 2, 3, 0, 1, 2, 3, 0, 2, 3, 0 #}
{# [:6],
lst[:-2],
lst[2:],
lst[-6:],
lst[2:6],
lst[2:-2],
lst[-6:6],
lst[-6:-2]
lst
#}where {
lst = [1,2,3,4,5,6,7,8];
}
{# [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6], [3, 4, 5, 6, 7, 8], [3, 4, 5, 6, 7, 8], [3, 4, 5, 6], [3, 4, 5, 6], [3, 4, 5, 6], [3, 4, 5, 6] #}
append
and A ++ B
Function / Type and Description
A ++ B
(List['1] * List['1] -> List['1])
Append the
second list elements to the first one.
append
(List['1] * List['1] -> List['1])
Append the
second list elements to the first one.
Function append(l,r)
computes a list containing all
elements of the two lists in the preserved order. Operator
‘++
’ is a synonym with infix operator notation:
{#[1,2,3] ++ [4,5,6],
append( [1,2,3], [4,5,6] )
#}
{# [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6] #}
A..B
,
intRange
, intRangeBy
Function / Type and Description
A .. B
(Int * Int -> List[Int])
Create a list of
integers in the range:
2..5 = [2,3,4,5]
intRange
(Int * Int -> List[Int])
Create a list of
integers in the range:
intRange(2,5) = [2,3,4]
intRangeBy
(Int * Int * Int -> List[Int])
Create a list of
integers in the given range with a given step:
intRange(2,9,2) = [2,4,6,8]
intRangeWithLast
(Int * Int -> List[Int])
Create a list of
integers in the range:
intRangeWithLast(2,5) = [2,3,4,5]
Integer range operator n..m
computes the list of
integers from n
to m
, including both limits.
If n
< m
, the elements will be in the
increasing order, ant if n
> m
, the
elements will be in the decreasing order:
{#0..10,
-5..5,
5..-5
#}
{# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5], [5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5] #}
No parenthesis nor brackets are required when using the operator
A..B
. Moreover, if brackets are used, the
[1..2]
will compute [[1,2]]
and not the
expected [1,2]
.
Function intRangeWithLast
is a synonym for the operator
A..B
.
Function intRange(n,m)
is similar, but will not include
the end of the range m
in the list.
Function intRangeBy(n,m,d)
creates a list of integers
from n
to m
but uses a step d
instead of 1. As with intRange
, its result does not include
the ending m
value.
{#5..9,
intRange(5,9),
intRangeBy(5,9,2),
intRangeWithLast(5,9)
#}
{# [5, 6, 7, 8, 9], [5, 6, 7, 8], [5, 7], [5, 6, 7, 8, 9] #}
map
and
zip
Function / Type and Description
map
(Sequence['2]['1] * ('1 -> '3) -> Sequence['2]['3])
Map the function to sequence elements:
map(seq,fun)
mapIdx
(Sequence['1]['2] * (Int * '2 -> '3) -> Sequence['1]['3])
Map the function to sequence elements:
mapIdx(seq,fun)
zip
(Sequence['4]['1] * Sequence['4]['2] -> Sequence['4][Tuple['1, '2]])
Zips two sequences to a sequence of pairs.
zipBy
(Sequence['4]['1] * Sequence['4]['2] * ('1 * '2 -> '3) -> Sequence['4]['3])
Zips two sequences by given function:
zipBy(seq1, seq2 ,fun)
zipByIdx
(Sequence['4]['1] * Sequence['4]['2] * (Int * '1 * '2 -> '3) -> Sequence['4]['3])
Zips two sequences and indexes by given function:
zipByIdx(seq1, seq2 ,fun)
map
The function map( lst, fun )
is one of the most
important higher order functions. It maps the list lst
to a
new list with the same number of elements. Each element x
of the lst
is mapped to fun(x)
element of the
new list, in the same order.
This function is equivalent to our function
processElements
from the previous examples.
In the following example map
is used to compute a list
of elements increased by one:
[1,2,3,4,5,6,7,8,9,10].map( \x: x+1 )
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
The same result:
[1,2,3,4,5,6,7,8,9,10].map( operator+(_,1) )
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
We will use map
in many of the following examples.
mapIdx
Sometimes it may be useful to know at what position in the list is
the processed element. We can achieve that by transforming (mapping) a
list of values to a list of pairs (index, value) and than mapping that
list of pairs where indexes are available. However, it is more efficient
to have a specific function mapIdx(lst,fun)
, where
fun(idx,x)
maps the value x
at position
idx
.
['a','b','c','d','e','f','g','h']
mapIdx( \i,x: x + i$ ) .
['a0', 'b1', 'c2', 'd3', 'e4', 'f5', 'g6', 'h7']
zip
Function zip(lst1,lst2)
returns a list of tuples, where
each tuple contains the corresponding elements of the two given lists
lst1
and lst2
.
[1,2,3], ['A','B','C']) zip(
[{# 1, 'A' #}, {# 2, 'B' #}, {# 3, 'C' #}]
zipBy
Function zipBy(lst1,lst2,fun)
returns a list where each
element is a result of the fun(x,y)
applied to the
corresponding elements of the two given lists lst1
and
lst2
.
['a','b','c'], ['A','B','C'], operator+ ) zipBy(
['aA', 'bB', 'cC']
zipByIdx
Function zipByIdx(lst1,lst2,fun)
returns a list where
each element is a result of the fun(i,x,y)
applied to the
corresponding elements of the two given lists lst1
and
lst2
and their index (position).
['a','b','c'], ['A','B','C'],
zipByIdx( \i,x,y: i$ + '-' + x + '-' + y
)
['0-a-A', '1-b-B', '2-c-C']
foldr
and
foldl
Function / Type and Description
foldr
(Sequence['3]['2] * ('2 * '1 -> '1) * '1 -> '1)
Right associative folding of sequence elements:
foldr([a,b,c],fn,zero) = fn(a,fn(b,fn(c,zero)))
foldl
(Sequence['3]['2] * ('1 * '2 -> '1) * '1 -> '1)
Left associative folding of sequence elements:
foldl([a,b,c],fn,zero) = fn(fn(fn(zero,a),b),c)
foldr
Like the map
, function
foldr( lst, fun, zero )
is one of the most important higher
order functions. It combines the lst
elements, using
zero
as a starting value, and applying
fun(x,res)
co combine the element x
with the
result res
of already completed combining.
This function is equivalent to our function
combineElements
from the previous examples. So let’s use it
in an equivalent example:
[1,2,3,4,5].foldr( operator+, 0 )
15
To better understand what foldr
evaluates, consider the
following example:
['1','2','3','4','5'].foldr( \x,res: '('+x+','+res+')', '' )
(1,(2,(3,(4,(5,)))))
As we can see, foldr
combines elements by combining the
list head with already combined list tail. Thus, it really begins the
combining from the end of the list, or from the right side,
which is why it has an r
at the end of the name.
foldl
If the combining function fun
as an associative one
(i.e. fun(fun(a,b),c)
= fun(a,fun(b,c))
), then
the order of combining is not important. But for non-associative
functions, like in the previous example the order is important. This is
why we have foldl
.
Function foldl
combines the list elements from the
beginning. It first combines zero
and the list head, and
then combines the result with the next list element and so on. Consider
the following example and compare its result to the previous example,
where foldr
was used with the same arguments:
['1','2','3','4','5'].foldl( \x,res: '('+x+','+res+')', '' )
(((((,1),2),3),4),5)
If the result of the combining operation has the same type as the
list elements, then the combining function has both arguments and the
result of the same type. As we already emphasized, if the combining
function is associative, then foldr
and foldl
evaluate the same result. Here we can confirm that for addition:
{#['1','2','3','4','5'].foldr( operator+, '' ),
['1','2','3','4','5'].foldl( operator+, '' )
#}
{# '12345', '12345' #}
On the other side, if the function is not associative, but has the same argument types, then the result will not be the same:
{#foldr( fun, '' ),
lst.foldl( fun, '' )
lst.
#}where {
lst = ['1','2','3','4','5'];
fun(x,y) = '(' + x + ',' + y +')';
}
{# '(1,(2,(3,(4,(5,)))))', '(((((,1),2),3),4),5)' #}
As we can see, foldl
combines elements by combining the
given previous result with the list head, assuming that the result will
be passed further to the tail, for the same processing. Thus, it really
begins the combining from the beginning of the list, or from the
left side, which is why it has an l
at the end of the
name.
There are many combining functions that cannot be used with both
foldr
and foldl
. If the result of the
combining operation has a different type than the list elements, then
the used combining function has to have different argument types, also.
Such functions cannot be used with both foldr
and
foldl
.
For example, let us write the append2
function that
appends two lists (the equivalent to the library function
append
and operator ++
). We can do that by
combining the elements of the first list using cons-operator, and using
the second list as a zero
. Because cons-operator is not
associative (moreover, it has the different argument types) it is only
possible to use foldr
:
[1,2,3].foldr( operator:, [4,5,6])
[1, 2, 3, 4, 5, 6]
In a similar way, we may append many lists:
[[1,2],[3,4],[5,6]].foldr( append, [])
[1, 2, 3, 4, 5, 6]
To use the cons-operator with foldl
we need to reverse
the arguments. But the result of the application will be a reversed
list:
[1,2,3,4,5,6].foldl( \x,y: y:x, [] )
[6, 5, 4, 3, 2, 1]
The following example illustrates how to use foldr
to
compute a list of distinct elements of a sorted list. The combining
lambda function assumes that the second argument is the already
processed tail:
1..100)
(map( operator%(_,10) )
.sort()
.
.distinct()where {
distinct( lst ) =
foldr(
lst.\x,dist:
if dist.empty() or x != dist.hd()
then x : dist
else dist
[]
, ;
)}
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
filter
, find
and filterMap
Function / Type and Description
filter
(Sequence['2]['1] * ('1 -> Bool) -> Sequence['2]['1])
Extracts the sequence elements which satisfy the given
condition
filterN
(Sequence['2]['1] * ('1 -> Bool) * Int -> Sequence['2]['1])
Extracts at most N elements which satisfy the given condition.
find
(Sequence['2]['1] * ('1 -> Bool) -> Sequence['2][Int])
Finds the indexes of the elements which satisfy the given
condition.
findN
(Sequence['2]['1] * ('1 -> Bool) * Int -> Sequence['2][Int])
Find at most N indexes of the elements which satisfy the given
condition.
filterMap
(Sequence['3]['1] * ('1 -> Bool) * ('1 -> '2) -> Sequence['3]['2])
Maps a function to sequence elements which satisfy the condition.
mapFilter(seq,cond,fn) == map(filter(seq,cond),fn)
filter
Function filter(lst,cond)
computes the list of
lst
elements that satisfy the given condition
cond(x)
. The element order is preserved.
{#[1,2,3,4,5]->filter( operator>(_,1)), // numbers > 1
[1,2,3,4,5]->filter( \x : x%2 > 0 ), // even numbers
[1,2,3,4,5]->filter( \x : x%2 = 0 ) // odd numbers
#}
{# [2, 3, 4, 5], [1, 3, 5], [2, 4] #}
filterN
Function filterN(lst,cond,n)
computes the list of
lst
elements that satisfy the given condition
cond(x)
, but it selects at most the first n
elements satisfying the condition.
The element order is preserved.
{#[1,2,3,4,5]->filterN( operator>(_,1), 2 ), // numbers > 1
[1,2,3,4,5]->filterN( \x : x%2 > 0, 1 ), // even numbers
[1,2,3,4,5]->filterN( \x : x%2 = 0, 1 ) // odd numbers
#}
{# [2, 3], [1], [2] #}
find
Function find(lst,cond)
computes the list of zero-based
indexes of lst
elements that satisfy the given condition
cond(x)
.
The indexes are selected in the increasing order.
{#[1,2,3,4,5]->find( operator>(_,1)), // numbers > 1
[1,2,3,4,5]->find( \x : x%2 > 0 ), // even numbers
[1,2,3,4,5]->find( \x : x%2 = 0 ) // odd numbers
#}
{# [1, 2, 3, 4], [0, 2, 4], [1, 3] #}
findN
Function findN(lst,cond,n)
computes the list of
zero-based indexes of lst
elements that satisfy the given
condition cond(x)
, but it selects at most the first
n
elements satisfying the condition.
The indexes are selected in the increasing order.
{#[1,2,3,4,5]->findN( operator>(_,1), 2 ), // numbers > 1
[1,2,3,4,5]->findN( \x : x%2 > 0, 1 ), // even numbers
[1,2,3,4,5]->findN( \x : x%2 = 0, 1 ) // odd numbers
#}
{# [1, 2], [0], [1] #}
filterMap
Function filter
is often used to select some list
elements before they are processed by map
. This is why
filterMap
function is included, to do both the filtering
and the processing in a same step.
Function filterMap(lst,filtFun,mapFun)
is equivalent to
the corresponding combination of filter
and
map
. The following example computes a list of squares of
odd list elements:
{#filterMap( odd, square ),
list.filter(odd).map(square)
list.
#}where {
list = 1..20;
odd( n ) = n % 2 != 0;
square( n ) = n * n;
}
{# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361], [1, 9, 25, 49, 81, 121, 169, 225, 289, 361] #}
Function filterMap
does not make the program code more
readable, but it is more efficient, because it does not create a
temporary list, as when separate filter
and
map
are used.
forall
and exists
Function / Type and Description
forall
(Sequence['2]['1] * ('1 -> Bool) -> Bool)
Check if the given condition applies to all sequence elements:
forall(list,cond).
exists
(Sequence['2]['1] * ('1 -> Bool) -> Bool)
Check if there is any sequence element satisfying the condition:
exists(sequence,cond)
Function forall(lst,cond)
checks if all list elements
satisfy the given condition cond(x)
. It is equivalent
to:
forall( lst , cond ) == lst.foldr( operator&&, true )
Function exists(lst,cond)
checks if any list element
satisfies the given condition cond(x)
. It is equivalent
to:
exists( lst , cond ) == lst.foldr( operator||, false )
{#[1,2,3,4,5].forall( operator>(_,1) ),
[1,2,3,4,5].forall( operator>=(_,1) ),
[1,2,3,4,5].exists( operator<(_,1) ),
[1,2,3,4,5].exists( operator<=(_,1) )
#}
{# false, true, false, true #}
count
and countRep
Function / Type and Description
count
(Sequence['2]['1] * ('1 -> Bool) -> Int)
Count the sequence elements satisfying the condition:
count(seq,cond)
countRep
(Sequence['2]['1] -> Map['1][Int])
Create a
catalog from the sequence. Keys are the distinct elements, and values
are the number of occurrences.
Function count(lst,cond)
counts how many list elements
satisfy the given condition cond(x)
.
{#[1,2,3,4,5].count( operator>(_,2) ),
[1,2,3,4,5].count( operator>=(_,2) )
#}
{# 3, 4 #}
Function countRep(lst)
counts how many times each
distinct element is repeated in the list. It returns a catalog, where
the keys are all distinct list elements and the values are the number of
occurrences.
['a','b','a','a','b','c'].countRep()
[<'a',3>, <'b',2>, <'c',1>]
['a','b','a','a','b','c'].countRep()['b']
2
sort
and
sortBy
Function / Type and Description
sort
(Sequence['2][Value['1]] -> Sequence['2][Value['1]])
Sort value sequence.
sortBy
(Sequence['2]['1] * ('1 * '1 -> Bool) -> Sequence['2]['1])
Sort the sequence using given comparator.
Function sort(lst)
return list sorted in increasing
order. It works for sequences with elements of primitive types.
Function sortBy(lst,lessThan)
return list sorted in
increasing order, using the given comparison function
lessThan(x,y)
. It works for sequences with any element
types, if a corresponding comparison function is provided.
{#[2,1,4,3,5].sort(),
[2,1,4,3,5].sortBy( operator< ),
[2,1,4,3,5].sortBy( operator> )
#}
{# [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [5, 4, 3, 2, 1] #}
in
and
contains
Function / Type and Description
in
('1 * Sequence['2]['1] -> Bool)
Checks if the
element is in the sequence:
el.in(seq)
contains
(Sequence['2]['1] * '1 -> Bool)
Checks if the
element is in the sequence:
seq.contains(el)
Function in(x,lst)
checks if the list lst
contains x
. It is usually applied in a form
x.in(lst)
. It works for sequences of any type.
{#1 .in([1,2,3]),
11 .in([1,2,3]),
:5}.in([ {a:4}, {a:5} ]),
{a:6}.in([ {a:4}, {a:5} ])
{a #}
{# true, false, true, false #}
Function contains
does the same, but with reversed
operand order: contains(lst,x)
is the same as
in(x,lst)
.
{#[1,2,3].contains( 1 ),
[1,2,3].contains( 11 ),
[ {a:4}, {a:5} ].contains( {a:5} ),
[ {a:4}, {a:5} ].contains( {a:6} )
#}
{# true, false, true, false #}
Wafl is an eager language, but like in the most of the languages, there are some lazy elements to prevent unnecessary computations. One such lazy element is the fetching of the database query results. The query result is a lazy list - from the database server only as much rows are fetched as required in the computation.
However, sometimes we know that we need to process all rows, but that
may take some time and it may be useful to finish the communication with
the database server as soon as possible. In such cases we can require
the eager evaluation of the query list using the force
function.
Function / Type and Description
forced
(List['1] -> List['1])
Returns the list, but
forces any delayed evaluation.
Function force(lst)
returns the same list, but
forces its eager evaluation.