This chapter contain detailed descriptions on the following datatypes:
We will also cover list comprehension, lazy sequences, iterators, bytearrays and bitarrays.
But before all that, we discuss functions that test two objects for equality.
function: is_eq(obj1, obj2)
returns: true if `obj1` and `obj2` are identical, false otherwise.
Two objects are considered identical if they internally reference to the same memory location.
is_eq can efficiently check for pointer equivalence of symbols and allocated objects like pairs,
arrays, and record instances. is_eq cannot be used to compare numbers reliably.
Slogan may use different objects internally for representing two big-integers that has the
same value.
// Examples:
is_eq('a, 'b);
//=> false
is_eq('a, 'a);
//=> true
is_eq(\a, \a);
//=> true
is_eq(10, 10);
//=> true
is_eq(38492948403840248034824037496494864917349749247947249749327482479,
38492948403840248034824037496494864917349749247947249749327482479);
//=> false
function: is_eqv(obj1, obj2)
returns: true if `obj1` and `obj2` are equivalent, false otherwise.
is_eqv is similar to is_eq except that it return true for two numbers that are
considered equal by the predicate number_is_eq. There is one exception for this rule though.
is_eqv will return false for `is_eqv(+0.0, -0.0)`, whereas number_is_eq will return
true. This provides a way to distinguish between positive and negative zeros.
// Example:
is_eqv(38492948403840248034824037496494864917349749247947249749327482479,
38492948403840248034824037496494864917349749247947249749327482479);
//=> true
function: is_equal(obj1, obj2)
returns: true if `obj1` and `obj2` have the same structure and contents, false otherwise.
Two objects are equal if each of their constituent parts are equal according to is_eqv.
The equals operator (==) have the same behavior as is_equal and the not-equals operator (<>)
have the same behavior as `not(is_equal(a, b))`.
`is_equal` is a generic function by default and can be overridden for custom datatypes without an
explicit `declare generic`.
// Examples:
is_equal([1, 2, [3, 4]], [1, 2, [3, 4]]);
//=> true
[1, 2, [3, 4]] == [1, 2, [3, 4]];
//=> true
is_equal("hello", "hello");
//=> true
is_equal("hello", "hEllo");
//=> false
not(is_equal("hello", "hEllo"));
//=> true
"hello" <> "hEllo";
//=> true
function: compare(obj1, obj2)
returns: see description below.
If `obj1` is less than `obj2`, returns -1.
If `obj1` is greater than `obj2`, returns 1.
If `obj1` is equal to `obj2`, returns 1.
For sequence objects like strings and lists, a lexicographical comparison is done.
First the first two items are compared, and if they differ this determines the outcome of
the comparison; if they are equal, the next two items are compared, and so on, until either
sequence is exhausted. For strings, character comparison is used, for lists and arrays
`compare` is called recursively for each element and for numeric arrays number comparison
operators are used.
`compare` is a generic function by default and can be overridden for custom datatypes without
an explicit `declare generic`.
// Example:
record point(x, y);
let p1 = point(x = 10, y = 20);
let p2 = point(x = 20, y = 30);
p1 < p2;
//-> error: compare_not_supported # #
function pointc(p1) ^(msg) | 'compare -> ^(p2) compare(point_x(p1), point_x(p2));
let pc1 = pointc(p1);
pc1 < p2;
//=> true
pc1 > p2;
//=> false
The pair is the most fundamental of all datastructures in Slogan. The most common use for pairs is to build lists, which are ordered sequences of pairs linked one to the next by the tail field. The elements of the list occupy the heads of the pairs. The tail of the last pair in a proper list is the empty list - []. The tail of the last pair in an improper list can be anything other than the empty list. Pairs are also used to build datastructures like trees and simple lookup tables (known as association lists).
syntax: obj1:obj2
syntax: obj:
function: pair(obj1, obj2)
returns: a new pair whose head is `obj1` and tail is `obj2`.
The form `obj:` will return the pair `obj:void`. void is an object that has no printed
representation. This form is useful for specifying spliced indices into lists and arrays.
// Examples:
pair(3, 4);
//=> 3:4
pair(3, pair(4, 5));
//=> 3:4:5
pair(3, 4:(5:[]));
//=> [3, 4, 5]
function: is_pair(obj)
returns: true if `obj` is a pair, false otherwise.
// Examples:
is_pair([]);
//=> false
is_pair([1]);
//=> true
is_pair(1:2);
//=> true
is_pair([1, 2, 'a]);
//=> true
function: head(p)
returns: the head of the pair `p`, false if `p` is not a pair.
function: tail(p)
returns: the tail of the pair `p`, false if `p` is not a pair.
function: car(p)
returns: the head of the pair `p`.
function: cdr(p)
returns: the tail of the pair `p`.
car and cdr will raise an error if `p` is not a pair object.
function: set_head(p, obj)
Changes the head of the pair `p` to point to `obj`, returns void.
function: set_tail(p, obj)
Changes the tail of the pair `p` to point to `obj`, returns void.
syntax: [obj, ...]
function: list(obj, ...)
returns: a list with elements `obj, ....`.
function: list_of(obj, n)
returns: a list with `n` copies of `obj`.
// Examples:
list();
//=> []
list(1, 2, 3);
//=> [1, 2, 3]
list_of("hello", 3);
//=> ["hello", "hello", "hello"]
syntax: lst[index]
returns: the value at `index` from the list `lst`. if `index` is out-of-range, returns false.
if `index` is a pair of integers, returns the sublist in that range.
syntax: lst[index] = obj
returns: void
`lst` is destructively updated by changing the element at `index` to `obj`.
raises an error if `index` is out of bounds.
// Examples:
let a = [1,2,3,4,5];
a[1];
//=> 2
a[1:3];
//=> [2, 3]
a[10:23];
//=> []
a[1:];
//=> [2, 3, 4, 5]
a[:1];
//=> [1]
a[:3];
//=> [1, 2, 3]
a[3] = 100;
a;
//=> [1, 2, 3, 100, 5]
function: is_empty(obj)
returns: true if `obj` is the empty list `[]`, false otherwise.
function: is_list(obj)
returns: true if `obj` is a proper list, false otherwise.
function: length(lst)
function: count(lst)
returns: the number of elements in the list `lst`.
function: at(lst, n)
returns: the nth (zero based) element in the list `lst`.
`at` will throw an error if `n` is not in `0 <= n < length(lst)`.
// Example:
at(["this", "is", "a", "list", "of", "strings"], 3);
//=> "list"
function: list_tail(lst, n)
returns: the nth (zero based) tail of the list `lst`.
`list_tail` will throw an error if `n` is not in `0 <= n < length(lst)`.
// Example:
list_tail([1,2,3,4,5], 3);
//=> [4, 5]
function: drop(n, lst)
returns: a new list with the first `n` elements removed from the list `lst`.
// Examples:
drop(3, [1, 2, 3, 4, 5]);
//=> [4, 5]
drop(30, [1, 2, 3, 4, 5]);
//=> []
drop(0, [1, 2, 3, 4, 5]);
//=> [1, 2, 3, 4, 5]
drop(-3, [1, 2, 3, 4, 5]);
//=> [1, 2, 3, 4, 5]
function: take(n, lst)
returns: a new list with the first `n` elements from the list `lst`.
// Examples:
take(3, [1, 2, 3, 4, 5]);
//=> [1, 2, 3]
take(30, [1, 2, 3, 4, 5]);
//=> [1, 2, 3, 4, 5]
take(0, [1, 2, 3, 4, 5]);
//=> []
take(-3, [1, 2, 3, 4, 5]);
//=> []
function: take_while(p, lst)
returns: the longest prefix of elements from `lst` that satisfy the predicate `p`.
// Example:
take_while(is_positive, [1, 2, 3, -4, 5]);
//=> [1, 2, 3]
function: drop_while(p, lst)
returns: the suffix of `lst` remaining after removing elements that satisfy the predicate `p`.
// Example:
drop_while(is_positive, [1, 2, 3, -4, 5]);
//=> [-4, 5]
function: append(list1, ..., obj)
returns: the concatenation of the lists.
append returns a new list consisting of the elements of the first list followed by
the elements of the second list, the elements of the third list, and so on. The new list
is made from new pairs for all arguments but the last, which need not be a list,
is merely placed at the end of the new structure.
// Examples:
append();
//=> []
append([1, 2], [3]);
//=> [1, 2, 3]
append([1, 2], 3);
//=> 1 : 2 : 3
function: reverse(lst)
returns: a new list with the elements of `lst` in the reverse order.
function: memq(obj, lst)
function: memv(obj, lst)
function: member(obj, lst)
returns: the first tail of `lst` whose head is equivalent to `obj`, or false.
The equivalence test for memq is is_eq, for memv is_eqv, and for member is_equal.
// Examples:
memq(2, [1, 2, 3, 4, 5]);
//=> [2, 3, 4, 5]
memq("hi", [1, 2, "hi", 4, 5]);
//=> false
member("hi", [1, 2, "hi", 4, 5]);
//=> ["hi", 4, 5]
memq(\a, [1, 2, \a, 4, 5]);
//=> [\a, 4, 5]
memq(1020032003209020020020, [1, 2,<PHONE_NUMBER>209020020020, 4, 5]);
//=> false
memv(1020032003209020020020, [1, 2,<PHONE_NUMBER>209020020020, 4, 5]);
//=> [1020032003209020020020, 4, 5]
member(1020032003209020020020, [1, 2,<PHONE_NUMBER>209020020020, 4, 5]);
[1020032003209020020020, 4, 5]
function: memp(f, lst)
returns: the first tail of `lst` for whose head the function `f` returns true, or false.
`f` should accept one argument and return a single value. It should not modify `lst`.
// Examples:
memp(is_odd, [1, 2, 3, 4, 5]);
//=> [1, 2, 3, 4, 5]
memp(is_even, [1, 2, 3, 4, 5]);
//=> [2, 3, 4, 5]
function: remq(obj, lst)
function: remv(obj, lst)
function: remove(obj, lst)
returns: a list containing the elements of `lst` with all occurrences of `obj` removed.
The equivalence test for remq is is_eq, for remv is_eqv, and for remove is_equal.
// Examples:
remq(1, [1, 2, 1, 3]);
//=> [2, 3]
remv(1020032003209020020020, [1020032003209020020020, 2,<PHONE_NUMBER>209020020020, 3]);
//=> [2, 3]
remove([1,2,3], [[1,2,3], [4,5,6], [1,2,3]]);
//=> [[4, 5, 6]]
function: remp(f, lst)
returns: a list of the elements of `lst` for which the funtion `f` returns false.
`f` should accept one argument and return a single value. It should not modify `lst`.
// Example:
remp(is_odd, [1, 2, 3, 4, 5]);
//=> [2, 4]
function: filter(f, lst)
returns: a list of the elements of `lst` for which `f` returns true.
`f` should accept one argument and return a single value. It should not modify `lst`.
// Example:
filter(is_odd, [1, 2, 3, 4, 5]);
//=> [1, 3, 5]
function: partition(f, lst)
returns: a pair of two lists. the first list contain the elements of `lst` for which
`f` returned true and the second list contain the elements of `lst` for which `f` returned false.
`f` should accept one argument and return a single value. It should not modify `lst`.
The elements of the returned lists appear in the same order as they appeared in the original list.
// Example:
match(partition(is_odd, [1, 2, 3, 4, 5]))
os:es -> showln("odds=", os, ", evens=", es);
//-> odds=[1, 3, 5], evens=[2, 4]
function: sublist(lst, start = 0, end = length(lst))
returns: a copy of the subsequence of the list `lst` bounded by `start` (inclusive)
and `end` (exclusive).
// Examples:
sublist([1,2,3,4,5], 1, 3);
//=> [2, 3]
sublist([1,2,3,4,5], 1);
//=> [2, 3, 4, 5]
sublist([1,2,3,4,5], 1, 3);
//=> [2, 3]
sublist([1,2,3,4,5], 1, 30);
//=> [2, 3, 4, 5]
sublist([], 1, 30);
//=> []
sublist([1,2,3,4,5], -1, 30);
//=> [1, 2, 3, 4, 5]
function: range(start, end, next_f = inc, compare_f = number_is_lteq)
returns: a list with the sequence of values from `start` to `end` (both inclusive).
// Examples:
range(1, 5);
//=> [1, 2, 3, 4, 5]
range(\a, \h, ^(c) integer_to_char(inc(char_to_integer(c))), char_is_lteq);
//=> [\a, \b, \c, \d, \e, \f, \g, \h]
function: find(f, lst)
returns: the first element in `lst` for which `f` returns true. If no such element, false.
`f` should accept one argument and return a single value. It should not modify `lst`.
If a program must distinguish between finding false in the list and finding no
element at all, `memp` should be used instead.
// Examples:
find(is_even, [1, 2, 3, 4, 5]);
//=> 2
find(is_even, [1, 3, 5]);
//=> false
function: position(obj, lst, @key start = 0, test = is_eq)
returns: the index of `obj` in the list `lst`, false if `obj` is not found in the list.
// Examples:
position(10, [1, 2, 10, 3]);
//=> 2
position(10, [1, 2, 10, 3]);
//=> 2
position("hi", [1, 2, "hi", 3]);
//=> false
position("hi", [1, 2, "hi", 3], test = is_equal);
//=> 2
position("hi", [1, 2, "hi", 3], test = is_equal, start = 3);
//=> false
position("hi", [1, 2, "hi", 3], test = is_equal, start = 2);
//=> 2
function: copy_list(lst)
returns: an exact copy of `lst`.
function: zip(alst, blst)
returns: a list that contains the corresponding pairs from `alst` and `blst`.
// Examples:
zip([1, 2, 3], [4, 5, 6]);
//=> [1:4, 2:5, 3:6]
zip([1, 2, 3], [4, [5, 6]]);
//=> [1:4, [2, 5, 6]]
function: zip_with(f, alst, blst)
returns: a list that contains the results of applying the function `f` to the
corresponding pairs from `alst` and `blst`.
// Example:
zip_with(`+`, [1, 2, 3], [4, 5, 6]);
//=> [5, 7, 9]
function: unzip(pairs)
returns: a pair of lists.
The head of the returned pair will all heads from `pairs` and its tail will contain
all tails from `pairs`.
// Example:
let ys = unzip([1:"one", 2:"two", 3:"three"]);
head(ys);
//=> [1, 2, 3]
tail(ys);
//=> ["one", "two", "three"]
function: sort(lst, @optional test, type)
returns: a list containing the elements of `lst` sorted according to `test`.
`test` must be a function that take two arguments and return true if its first argument must
precede its second in the sorted list. Duplicate elements are not removed. The default value of
`test` is number_is_lt which compares two numeric values.
`type` specifies the sorting algorithm to use. In the current implementation `type` can take
two values: 'quick for "quicksort" and 'merge for "mergesort".
// Examples:
sort([3, 4, 2, 1, 2, 5]);
//=> [1, 2, 2, 3, 4, 5]
list_to_string(sort(string_to_list("hello"), char_is_lt, 'merge));
//=> "ehllo"
function: quicksort(lst, @optional test)
function: mergesort(lst, @optional test)
returns: a list containing the elements of `lst` sorted according to `test`.
A list comprehension is a notational convenience for constructing lists from other lists.
Example:
[x * x | x <- [1, 2, 3, 4, 5]];
//=> [1, 4, 9, 16, 25]
syntax: [out_expr | var_expr where_clause, ...]
`out_expr` constructs each element in the output list. `var_expr ...` assign values
variables used in `out_expr`. Each value is "extracted" from a list that follows the
"extractor" operator (<-). `where_clause` is optional and is used to filter values
that go into the variable in `var_expr`.
More examples:
[i : j | i <- range(1, 5), j <- range(i, 5) where is_even(i)];
//=> [2:2, 2:3, 2:4, 2:5, 4:4, 4:5]
function triads(n)
let (elems = range(1, n))
[[x, y, z] | x <- elems, y <- elems, z <- elems where x * x + y * y == z * z];
triads(15);
//=> [[3, 4, 5], [4, 3, 5], [5, 12, 13], [6, 8, 10],
[8, 6, 10], [9, 12, 15], [12, 5, 13], [12, 9, 15]]
function concat(xss) [x | xs <- xss, x <- xs];
concat([[1, 2, 3], [4, 5, 6]]);
//=> [1, 2, 3, 4, 5, 6]
An association list is a proper list whose elements are pairs of the form key:value
.
function: assq(obj, lst)
function: assv(obj, lst)
function: assoc(obj, lst)
returns: first element of `lst` whose head is equivalent to `obj`, or false.
The equivalence test for assq is is_eq, for assv is_eqv, and for assoc is_equal.
// Examples:
assq('b, ['a:1, 'b:2, 'c:3]);
//=> b:2
assoc("b", ["a":1, "b":2, "c":3]);
//=> "b":2
function: assp(f, lst)
returns: the first element in `lst` for whose head `f` returns true, or false.
The argument `lst` must be an association list. `f` should accept one argument and return
a single value. It should not modify lst.
// Examples:
assp(is_odd, [1:'a, 2:'b]);
// => 1:a
assp(is_even, [1:'a, 2:'b]);
//=> 2:b
function: get(lst, obj, default = false)
returns: tail of first element of `lst` whose head is equivalent to `obj`, or `default`.

The functions that work with lazy pairs are described below.
function: first(s)
returns: the first element of `s`, false if `s` is not a valid sequence.
function: rest(s)
returns: the tail of `s`, false if `s` is not a valid sequence.
// Example:
function ints(n) n:~ints(n + 1);
let s = ints(0);
s;
//=> 0:<promise>
first(s);
//=> 0
s = rest(s);
first(s);
//=> 1
Both `first` and `rest` are generic functions recognized at the language level.
Any user-defined object can override them, as shown in the following program.
// Example:
function make_bag(xs)
let (len = array_length(xs))
^(msg)
| 'first -> when (len > 0) array_at(xs, 0)
| 'rest -> when (len > 1) make_bag(subarray(xs, 1, len))
| 'count -> len
let b = make_bag(#[10, 20, 30])
first(b)
//=> 10
first(rest(b))
//=> 20
nth(2, b)
//=> 30
count(b)
//=> 3
function: nth(n , s)
returns: the `n`th element of `s`, false if `s` is not a valid sequence.
// Example:
nth(100, s);
//=> 101
function: nth_tail(n , s)
returns: the `n`th tail of the `s`, false if `s` is not a valid sequence.
// Example:
nth_tail(100, s);
//=> 101:<promise>
function: second(s)
function: third(s)
function: fourth(s)
function: fifth(s)
function: sixth(s)
function: seventh(s)
function: eighth(s)
function: ninth(s)
function: tenth(s)
returns: respectively the second, third, fourth, fifth, sixth, seventh, eighth, ninth, and tenth
elements of the sequence `s`, false if `s` is not a valid sequence.
function: accumulate(f, obj, s)
returns: the sequence formed by applying `f` to `obj` and the tails of the sequence `s`.
// Example:
function ints(n) when (n < 10) n:~ints(n+1);
let s = ints(1);
s = accumulate(`+`, 0, s);
//=> 1:<promise>
second(s);
//=> 3
third(s);
//=> 6
function: enumerate(start, end, compare_f = number_is_lteq, next_f = inc)
returns: a sequence with the values from `start` to `end` (both inclusive by default).
// Example:
let s = enumerate(1, 5);
s;
//=> 1:<promise>
fourth(s);
//=> 4
fifth(s);
//=> 5
let s = enumerate(1, 5, `<`);
fifth(s);
//=> false
fourth(s);
//=> 4
let s = enumerate(1, 5, `<`, ^(x) x + 2);
fourth(s);
//=> false
second(s);
//=> 3
function: realize(n, s)
returns: a pair with the first `n` elements of the sequence `s` in its head
and the rest of `s` in its tail.
// Example:
let xs = ints(10)
realize(5, xs)
//=> [10, 11, 12, 13, 14]:15:<promise>
The yield
statement is used to define generator functions that behaves like a lazy sequence of values.
A sequence generated by yield
is known as an iterator.
// Example:
function ints()
let loop (i = 1)
{ yield i ;
loop (i + 1) };
let iter = ints();
first(iter);
//=> 1
iter = rest(iter);
let iter_2 = map(^(x) x + x , iter);
first(iter_2);
//=> 4
iter_2 = rest(iter_2);
first(iter_2);
//=> 6
Remember that moving forward an iterator will destructively change the current position in the iterator object.
A function that generates an iterator should not call itself recursively. Instead use a named let
as shown in the example.
Lists, lazy pairs and iterators together form the "sequence" type in Slogan. In addition to the functions defined for lazy pairs the following functions are defined to work with all sequences: `map`, `for_each`, `take`, `drop`, `take_while`, `drop_while` and `filter`.
Numbers are classified as integers, rational numbers, real numbers, or complex numbers. This classification is hierarchical, in that all integers are rational, all rational numbers are real, and all real numbers are complex.
A number may also be classified as exact or inexact, depending upon the quality of operations used to derive the number and the inputs to these operations. Most operations on numbers are exactness preserving: if given exact operands they return exact values, and if given inexact operands or a combination of exact and inexact operands they return inexact values.
Exact integer and rational arithmetic is supported to arbitrary precision; the size of an integer or of the denominator or numerator of a ratio is limited only by system storage constraints. Although other representations are possible, inexact numbers are represented by floating-point numbers supported by the host computer's hardware or by system software.
Numbers are written in a straightforward manner not much different from ordinary conventions for writing numbers. An exact
integer is normally written as a sequence of numerals preceded by an optional sign. For example, 3
, +19
,
-100000
, and<PHONE_NUMBER>37489374
all represent exact integers. Underscores (_
) may be added
to a numeric constant to increase readability. For example, the integer 150000
may be written as 1_50_000
or 150_000
.
An exact rational number is normally written as two sequences of numerals separated by a slash (/
) and preceded by an optional
sign. For example, 3/4
, -6/5
, and 1/1208203823
are all exact rational numbers. A ratio is
reduced immediately to lowest terms when it is read and may even get reduced to an exact integer.
Inexact real numbers are normally written in either floating-point or scientific notation. Floating-point notation consists of a
sequence of numerals followed by a decimal point and another sequence of numerals, all preceded by an optional sign. Scientific notation
consists of an optional sign, a sequence of numerals, an optional decimal point followed by a second string of numerals, and an exponent;
an exponent is written as the letter e
followed by an optional sign and a sequence of numerals. For example, 1.0
and -200.0
are valid inexact integers, and 1.5
, 0.034
, -10e-10
and 1.5e-5
are valid inexact rational numbers. The exponent is the power of ten by which the number preceding the exponent should be scaled, so that
2e3
is equivalent to 2000.0
.
The exactness of a numeric representation may be overridden by preceding the constant by either 0e
or 0i
.
0e
forces the number to be exact, and 0i
forces it to be inexact. For example, 1
, 0e1
,
1/1
, 0e1/1
, 0e1.0
, and 0e1e0
all represent the exact integer 1
, and
0i3/10
, 0.3
, 0i0.3
, and 3e-1
all represent the inexact rational 0.3
.
Numbers are written by default in base 10
, although the special prefixes 0b
(binary), 0o
(octal),
0d
(decimal), and 0x
(hexadecimal) can be used to specify base 2, base 8, base 10, or base 16. For radix 16,
the letters a
through f
or A
through F
serve as the additional numerals required to
express digit values 10
through 15
. For example, 0b10101
is the binary equivalent of
2110
, 0o72
is the octal equivalent of 5810
, and 0xC7
is the hexadecimal equivalent of 19910
.
Numbers written in floating-point and scientific notations are always written in base 10.
Complex number literals are written using the syntax `R+Ii`, where `R` is the real part and `I` is the imaginary part. E.g: `3+7i`.
If both are present, the exactness prefix must appear before the radix prefix.
function: is_number(obj)
returns: true if `obj` is a number, false otherwise.
function: is_complex(obj)
returns: true if `obj` is a complex number, false otherwise.
function: is_real(obj)
returns: true if `obj` is a real number, false otherwise.
function: is_rational(obj)
returns: true if `obj` is a rational number, false otherwise.
function: is_integer(obj)
returns: true if `obj` is an integer, false otherwise.
These number types form a hierarchy: any integer is rational, any rational is real,
any real is complex, and any complex is numeric.
function: is_exact(num)
returns: true if `num` is exact, false otherwise.
// Examples:
is_exact(1);
//=> true
is_exact(-15/10);
//=> true
is_exact(3.01);
//=> false
function: is_inexact(num)
returns: true if `num` is inexact, false otherwise.
// Examples:
is_inexact(1);
//=> false
is_inexact(-15/10);
//=> false
is_inexact(3.01);
//=> true
function: string_to_number(s, @optional radix)
returns: the number represented by the string `s`, or false.
The number is interpreted in `radix`, which must be an exact integer in the set {2, 8, 10, 16}.
If not specified, `radix` defaults to 10. Any radix specifier within the
string, e.g., 0x, overrides the radix argument.
// Examples:
string_to_number("3.4e3");
//=> 3400.
string_to_number("0x-2e2", 10);
//=> -738
string_to_number("0x-2e2", 16);
//=> -738
function: number_to_string(num, @optional radix)
returns: an external representation of `num` as a string.
The number is expressed in `radix`, which must be an exact integer in the set {2, 8, 10, 16}.
If not specified, radix defaults to 10. In any case, no radix specifier appears in the resulting string.
// Examples:
number_to_string(220/9);
//=> "220/9"
number_to_string(220/9, 16);
//=> "dc/9"
function: number_is_eq(num1, ...)
function: number_is_lt(num1, ...)
function: <(num1, ...)
function: number_is_gt(num1, ...)
function: >(num1, ...)
function: number_is_lteq(num1, ...)
function: <=(num1, ...)
function: number_is_gteq(num1, ...)
function: >=(num1, ...)
returns: true if the relation holds, false otherwise.
number_is_eq return true if its arguments are equal.
number_is_lt and < return true if their arguments are monotonically increasing, i.e., each
argument is greater than the preceding ones.
number_is_gt and > return true if their arguments are monotonically decreasing, i.e., each
argument is less than the preceding ones.
number_is_lteq and <= return true if their arguments are monotonically nondecreasing,
i.e., each argument is not less than the preceding ones.
number_is_lteq and >= return true if its arguments are monotonically non-increasing.
<, >, <= and >= are commonly used in the infix operator position with two arguments.
// Examples:
number_is_eq(7, 7);
//=> true
`<=`(1, 2, 3, 3, 4, 5);
//=> true
number_is_lteq(1, 2, 3, 3, 4, 5);
//=> true
// Numeric comparisions using infix syntax:
1 < 2;
//=> true
2 == 3 - 1;
//=> true
2 <> 2 + 1;
//=> true
function: add(num1, ...)
function: +(num1, ...)
returns: the sum of `num1, ...`.
`+` is commonly used as an infix operator with two argumens.
function: sub(num1, num2, ...)
function: -(num1, num2, ...)
returns: the difference between `num1` and the sum of `num2, ....`
`-` is commonly used as an infix operator with two argumens.
syntax: -num
returns: the additive inverse of `num`.
function: mult(num1, ...)
function: *(num1, ...)
returns: the product of `num1, ...`.
`*` is commonly used as an infix operator with two argumens.
function: div(num1, num2, ...)
function: /(num1, num2, ...)
returns: the result of dividing `num1` by the product of `num2, ....`.
`/` is commonly used as an infix operator with two argumens.
function: inc(n)
returns: the result of adding 1 to `n`.
function: dec(n)
returns: the result of subtracting 1 from `n`.
function: is_zero(num)
returns: true if `num` is zero, false otherwise.
function: is_positive(num)
returns: true if `num` is greater than zero, false otherwise.
function: is_negative(num)
returns: true if `num` is less than zero, false otherwise.
function: is_even(num)
returns: true if `num` is even, false otherwise.
function: is_odd(num)
returns: true if `num` is odd, false otherwise.
function: is_prime(num)
returns: true if `num` is a prime number, false otherwise.
function: is_finite(num)
returns: true if `num` is finite, false otherwise.
function: is_infinite(num)
returns: true if `num` is infinite, false otherwise.
function: is_nan(num)
returns: true if `num` is NaN, false otherwise.
function: quotient(int1, int2)
returns: the integer quotient of `int1` and `int2`.
function: remainder(int1, int2)
returns: the integer remainder of `int1` and `int2`. The result has the same sign as `int1`.
function: modulo(int1, int2)
returns: the integer remainder of `int1` and `int2`. The result has the same sign as `int2`.
function: quo(real1, real2)
returns: the real quotient of `real1` and `real2`.
function: rem(real1, real2)
returns: the real remainder of `real1` and `real2`. The result has the same sign as `real1`.
function: mod(real1, real2)
returns: the real remainder of `real1` and `real2`. The result has the same sign as `real2`.
function: truncate(real)
returns: the integer closest to `real` toward zero.
function: floor(real)
returns: the integer closest to `real` toward negative infinity.
If `real` is infinity or NaN, floor returns `real`.
function: ceiling(real)
returns: the integer closest to `real` toward positive infinity.
If `real` is infinity or NaN, ceiling returns `real`.
function: round(real)
returns: the integer closest to `real`.
function: abs(real)
returns: the absolute value of `real`.
function: max(real1, ...)
returns: the maximum of `real1, ...`.
function: min(real1, ...)
returns: the minimum of `real1, ...`.
function: gcd(int1, ...)
returns: the greatest common divisor of `int1, ...`.
function: lcm(int1, ...)
returns: the least common multiple of `int1, ...`.
function: expt(num1, num2)
returns: `num1` raised to the `num2` power.
function: inexact(num)
returns: an inexact representation of `num`.
// Examples:
inexact(3);
//=> 3.
inexact(-1/4);
//=> -.25
inexact(expt(10, 20));
//=> 1e20
function: exact(num)
returns: an exact representation of `num`.
// Examples:
exact(3.0);
//=> 3
exact(-.25);
//=> -1/4
exact(1e20);
//=><PHONE_NUMBER>00000000000
function: rationalize(real1, real2)
returns: the simplest rational number that differs from `real1` by no more than `real2`.
// Examples:
rationalize(3/10, 1/10);
//=> 1/3
rationalize(.3, 1/10);
//=> .3333333333333333
function: numerator(num)
returns: the numerator of `num`.
// Examples:
numerator(2/3);
//=> 2
numerator(-2.25);
//=> -9.0
numerator(10);
//=> 10
function: denominator(num)
returns: the denominator of `num`.
// Examples:
denominator(0);
//=> 1
denominator(10);
//=> 1
denominator(2/3);
//=> 3
denominator(-2.25);
//=> 4.0
function: rectangular(r, i)
returns: a complex number with real component `r` and imaginary component `i`.
function: polar(m, a)
returns: a complex number with magnitude `m` and angle `a`.
// Examples:
rectangular(-2, 7);
//=> -2+7i
rectangular(2/3, -1/2);
//=> 2/3-1/2i
polar(2, 0);
//=> 2
polar(2.0, 0.0);
//=> 2.0+0.0i
Note that complex number literals that contain fractions should be written using inexact numbers. For example, the literal that represent `2/3-1/2i` should be written as `2/3-0.5i`.
function: real_part(num)
function: imag_part(num)
returns: the real/imaginary component of the complex number `num`.
function: angle(num)
function: magnitude(num)
returns: the angle/magnitude part of the complex number `num`.
function: sqrt(num)
returns: the principal square root of `num`.
function: exp(num)
returns: e to the `num` power.
See https://en.wikipedia.org/wiki/E_(mathematical_constant)
function: log(num)
returns: the natural logarithm of `num`.
function: logb(num1, num2)
returns: the base-`num2` logarithm of `num1`.
function: sin(num)
function: cos(num)
function: tan(num)
returns: the sine, cosine or tangent of `num`.
function: asin(num)
function: acos(num)
function: atan(num, @optional num1)
returns: the arc sine, arc cosine or arc tangent of num.
When passed a single complex argument `num`, atan returns the arc tangent of `num`.
When passed two real arguments, atan is equivalent to `^(y x) angle(rectangular(x y))`.
function: bnot(int)
function: band(int_a, int_b)
function: bior(int_a, int_b)
function: bxor(int_a, int_b)
returns: the bitwise not, and, inclusive or, or exclusive or of `int`, `int_a` and `int_b`.
The inputs are treated as if represented in two's complement, even if they are not
represented that way internally.
// Examples:
bnot(0);
//=> -1
bnot(3);
//=> -4
band(0b01101, 0b00111);
//=> 5
bior(0b01101, 0b00111);
//=> 15
bxor(0b01101, 0b00111);
//=> 10
function: bif(int1, int2, int3)
returns: the bitwise "if" of its arguments.
The inputs are treated as if represented in two's complement, even if they are not
represented that way internally.
For each bit set in `int1`, the corresponding bit of the result is taken from `int2`, and
for each bit not set in `int1`, the corresponding bit of the result is taken from `int3`.
// Example:
bif(0b101010, 0b111000, 0b001100);
//=> 44
function: bit_count(int)
returns: the number of bits set in `int`.
For nonnegative inputs, bit_count returns the number of bits set in the two's complement
representation of `int`. For negative inputs, it returns a negative number whose magnitude
is one greater than the number of bits not set in the two's complement representation of `int`.
// Examples:
bit_count(0b00000);
// => 0
bit_count(0b00001);
// => 1
bit_count(0b10101);
// => 3
bit_count(-1);
// => -1
bit_count(-2);
// => -2
bit_count(-4);
// => -3
function: blength(int)
returns: the number of bits set in `int`.
This function returns the number of bits of the smallest two's complement representation
of `int`, not including the sign bit for negative numbers. For 0 blength will return 0.
// Examples:
blength(4);
//=> 3
blength(-6);
//=> 3
function: first_bit_set(int)
returns: the index of the least significant bit set in `int`.
The input is treated as if represented in two's complement, even if it is not
represented that way internally.
// Examples:
first_bit_set(0b00000);
//=> -1
first_bit_set(0b00001);
//=> 0
first_bit_set(0b01100);
//=> 2
function: is_bit_set(int, index)
returns: true if the bit at `index` of `int` is set, false otherwise.
The two's complement representation of a nonnegative number conceptually extends to the left
(toward more significant bits) with an infinite number of zero bits, and the two's complement
representation of a negative number conceptually extends to the left with an infinite
number of one bits.
// Examples:
is_bit_set(0b01011, 0);
//=> true
is_bit_set(0b01011, 2);
//=> false
is_bit_set(-1, 0);
//=> true
is_bit_set(-1, 20);
//=> true
is_bit_set(-3, 1);
//=> false
is_bit_set(0, 5000);
//=> false
is_bit_set(-1, 5000);
//=> true
function:is_any_bits_set(int1, int2)
returns: true if the bitwise and of `int1` and `int2` is different from zero, false otherwise.
// Examples:
is_any_bits_set(0b0010, 0b1110);
//=> true
is_any_bits_set(0b0010, 0b1100);
//=> false
function: is_all_bits_set(int1, int2)
returns: true if the bitwise and of `int1` and `int2` is equal to `int1`, false otherwise.
// Examples:
is_all_bits_set(0b0010, 0b0010);
//=> true
is_all_bits_set(0b0010, 0b0100);
//=> false
function: copy_bit_field(int1, start, end, int2)
returns: `int1` with n bits from `start` (inclusive) to `end` (exclusive)
replaced by the low-order n bits of `int2`.
// Examples:
copy_bit_field(0b10000, 0, 3, 0b10101);
//=> 21
copy_bit_field(0b10000, 1, 3, 0b10101);
//=> 18
function: bshift(int, n)
returns: the result of arithmetically shifting `int` by `n` bits.
If `n` is negative, the bits are shifted right. Otherwise, the bits are shifted left.
// Examples:
bshift(-64, -3);
//=> -8
bshift(-64, 3);
//=> -512
function: rotate_bit_field(int1, start, end, int2)
returns: the result of shifting the bits of `int1` from the bit `start` (inclusive)
through bit `end` (exclusive) left by `mod(int2, end - start)` bits, with the bits shifted out
of the range inserted at the bottom end of the range.
function: reverse_bit_field(int1, start, end)
returns: the result of reversing the bits of `int1` from the bit `start` (inclusive) through
the bit `end` (exclusive).
Fixnums represent exact integers in the fixnum range, which is required to be a closed range
[-2w-1, 2w-2 - 1]
, where w
(the fixnum width) is at least 24
.
The platform-specific value of w
may be determined via the function fixnum_width
, and the
endpoints of the range may be determined via the functions least_fixnum
and greatest_fixnum
.
The names of arithmetic functions that operate only on fixnums begin with the prefix "fx" to set them apart from their generic counterparts.
function: least_fixnum()
function: greatest_fixnum()
returns: the most negative or most positive fixnum supported by the platform.
function: fixnum_width()
returns: the platform dependent fixnum width.
function: is_fixnum(obj)
returns: true is `obj` is a fixnum, false otherwise.
// Examples:
is_fixnum(greatest_fixnum());
//=> true
is_fixnum(greatest_fixnum() + 1);
//=> false
function: fx_is_eq(fx1, ...)
function: fx_is_lt(fx1, ...)
function: fx_is_gt(fx1, ...)
function: fx_is_lteq(fx1, ...)
function: fx_is_gteq(fx1, ...)
returns: true if the relatiion holds, false otherwise.
fx_is_eq returns true if its arguments are equal.
fx_is_lt returns true if its arguments are monotonically increasing, i.e., each
argument is greater than the preceding ones.
fx_is_gt returns true if its arguments are monotonically decreasing.
fx_is_lteq returns true if its arguments are monotonically nondecreasing, i.e., each
argument is not less than the preceding ones.
fx_is_gteq returns true if its arguments are monotonically non-increasing.
These functions will raise an error if an argument is not a fixnum.
function: fxadd(fx1, ...)
function: fxsub(fx1, ...)
function: fxmult(fx1, ...)
function: fxdiv(fx1, fx2)
function: fxmod(fx1, fx2)
function: fxadd_wrap(fx1, ...)
function: fxsub_wrap(fx1, ...)
function: fxmult_wrap(fx1, ...)
returns: the result of performing the arithmetic operation on `fx1, ...` (or `fx1` and `fx2`).
When an ordinary fixnum addition, subtraction, or multiplication operation overflows,
an exception is raised. The functions with the `_wrap` suffix instead return a
wrapped-around result.
function: fx_is_zero(fx)
function: fx_is_positive(fx)
function: fx_is_negative(fx)
returns: true if `fx` is zero, greater than zero or less than zero, false otherwise.
function: fx_is_even(fx)
function: fx_is_odd(fx)
returns: true if `fx` is even/odd, false otherwise.
function: fxmin(fx1, ...)
function: fxmax(fx1, ...)
returns: the minimum/maximum of `fx1, ...`.
function: fxnot(fx)
function: fxand(fx1, ...)
function: fxior(fx1, ...)
function: fxxor(fx1, fx2)
returns: the bitwise not/and/inclusive or/exclusive or of `fx, fx1, ...`.
function: fxif(fx1, fx2, fx3)
returns: the bitwise "if" of its arguments.
function: fxbit_count(fx)
returns: the number of bits set in the two's complement representation of `fx`.
For negative inputs, fxbit_count returns a negative number whose magnitude is one greater
than the number of bits not set in `fx`.
function: fxlength(fx)
returns: the number of bits of the smallest two's complement representation of `fx`.
(does not include the sign bit for negative numbers).
function: fxfirst_bit_set(fx)
returns: the index of the least significant bit set in `fx`. If `fx` is 0, returns -1.
function: fxbit_is_set(fx, i)
returns: true if bit `i` of `fx` is set, false otherwise.
`i` must be nonnegative. It is taken as a zero-based index for the bits in the two's complement
representation of `fx`, with the sign bit virtually replicated an infinite number of
positions to the left.
function: fxshift(fx, i)
function: fxshift_left(fx, i)
function: fxshift_right(fx, i)
function: fxshift_wrap(fx, i)
function: fxshift_left_wrap(fx, i)
function: fxshift_right_wrap(fx, i)
function: fxlshift_right_wrap(fx, i)
returns: `fx` arithmetically/logically shifted left/right by `i`.
For all functions except fxshift and fxshift_wrap, `i` must be non-negative.
If `i` is non-negative, it must be less than the value of fixnum_width().
If `i` is negative, fxshift returns the result of arithmetically shifting `fx` right by `i` bits.
Otherwise, fxshift returns the result of shifting `fx` left by `i` bits.
Flonums represent inexact real numbers represented using the IEEE double-precision floating-point representation for flonums.
Flonum-specific function names begin with the prefix "fl" to set them apart from their generic counterparts.
function: is_flonum(obj)
returns: true if `obj` is a flonum, false otherwise.
function: fl_is_eq(fl1, ...)
function: fl_is_lt(fl1, ...)
function: fl_is_gt(fl1, ...)
function: fl_is_lteq(fl1, ...)
function: fl_is_gteq(fl1, ...)
returns: true if the relation holds, false otherwise.
fl_is_eq return true if its arguments are equal.
fl_is_lt return true if its arguments are monotonically increasing, i.e., each
argument is greater than the preceding ones.
fl_is_gt return true if its arguments are monotonically decreasing.
fl_is_lteq return true if its arguments are monotonically nondecreasing, i.e., each
argument is not less than the preceding ones.
fl_is_gteq return true if its arguments are monotonically non-increasing.
When passed only one argument, each of these predicates returns true.
Comparisons involving NaNs always return false.
function: fl_is_zero(fl)
function: fl_is_positive(fl)
function: fl_is_negative(fl)
returns: true if fl is zero/greater than zero/less than zero, false otherwise.
function: fl_is_integer(fl)
returns: true if `fl` is an integer, false otherwise.
function: fl_is_finite(fl)
function: fl_is_infinite(fl)
function: fl_is_nan(fl)
returns: true if `fl` is finite/infinite/NaN, false otherwise.
function: fl_is_even(fl)
function: fl_is_odd(fl)
returns: true if `fl` is even or odd, false otherwise.
function: flmin(fl1, ...)
function: flmax(fl1, ...)
returns: the minimum or maximum of `fl1 ...`.
function: fladd(fl1, ...)
function: flsub(fl1, ...)
function: flmult(fl1, ...)
function: fldiv(fl1, fl2)
function: flmod(fl1, fl2)
returns: the result of the arithmetic operation on `fl1, fl, ...`.
function: flround(fl)
returns: the integer closest to `fl`.
function: fltruncate(fl)
returns: the integer closest to `fl` towards zero.
function: flfloor(fl)
returns: the integer closest to `fl` towards negative infinity.
function: flceiling(fl)
returns: the integer closest to `fl` towards positive infinity.
function: flnumerator(fl)
function: fldenominator(fl)
returns: the numerator or denominator of `fl`.
function: flabs(fl)
returns: the absolute value of `fl`.
function: flexp(fl)
returns: e to the `fl` power.
function: fllog(fl)
function: fllogb(fl, fl_b)
returns: the natural of base fl_b logarithm of `fl`.
function: flsin(fl)
function: flcos(fl)
function: fltan(fl)
function: flasin(fl)
function: flacos(fl)
function: flatan(fl)
returns: the sine/cosine/tangent/arc sine/arc cosine/arc tangent of `fl`.
function: flatan(fl1, fl2)
returns: the arc tangent of `fl1/fl2`.
function: flsqrt(fl)
returns: the principle square root of `fl`.
function: flexpt(fl1, fl2)
returns: `fl1` raised to the `fl2` power.
function: fixnum_to_flonum(fx)
returns: the flonum representation closest to `fx`.
variable: inf
value: positive infinity (+inf.0).
variable: _inf
value: negative infinity (-inf.0).
variable: nan
value: NaN (+nan.0).
variable: zero
value: positive zero (+0.0).
variable: _zero
value: negative zero (-0.0).
function: random_integer(n)
returns: a pseudo random exact integer in the range 0 to n - 1.
function: random_real()
returns: a pseudo random inexact real between, but not including, 0 and 1.
function: random_byte_array(n)
returns: a byte array of length `n` containing pseudo random exact integers in the range 0 to 255.
function: default_random_source()
returns: the random source object which is used by the random_integer, random_real and
random_byte_array functions.
function: random_source()
returns: a new random source object initialized to a predetermined state.
function: is_random_source(obj)
returns: true if `obj` is a random source object, false otherwise.
function: random_source_state(rs)
returns: an array containing the state of the random source `rs`.
function: random_source_set_state(rs, s)
returns: void
Sets the state of the random source `rs` to `s` which must be an array returned
by random_source_state.
function: random_source_randomize(rs)
function: random_source_pseudo_randomize(rs, i , j)
returns: void
Change the state of the random source object `rs`. random_source_randomize sets the
random source object to a state that depends on the current time (which for typical
uses can be considered to randomly initialize the state).
random_source_pseudo_randomize sets the random source object to a state that is determined
only by the current state and the nonnegative exact integers `i` and `j`.
function: random_source_for_integers(rs)
function: random_source_for_reals(rs, @optional precision)
function: random_source_for_byte_arrays(rs)
returns: a function for generating pseudo random exact integers, inexact reals or
byte arrays using the random source object `rs`.
For random_source_for_reals, the optional parameter precision specifies an upper bound on the
minimum amount by which two generated pseudo-random numbers can be separated.
// Example:
let rs = random_source();
let ri = random_source_for_integers(rs);
let rr = random_source_for_reals(rs);
let ra = random_source_for_byte_arrays(rs);
ri(10);
//=> 8
ri(10);
//=> 2
rr();
//=> .7342236104231586
rr();
//=> .8162050798467028
ra(10);
//=> #u8[53, 192, 40, 176, 200, 53, 29, 202, 3, 85]
ra(10);
//=> #u8[134, 119, 240, 127, 113, 8, 182, 120, 138, 103]
While every object except false
is counted as true, Slogan has a dedicated constant true
for use when a value of an expression should convey nothing more than that it is true.
function: is_boolean(obj)
returns: true if `obj` is either true or false, false otherwise.
function: is_true(obj)
returns: true if `obj` evaluates to the constant true value.
function: is_false(obj)
returns: true if `obj` evaluates to the constant false value.
The void
object has no printed representation. It is often used as a return value for functions called only for
their side-effects.
function: void()
returns: the void object.
function: is_void(obj)
returns: true if `obj` is the void object.
Characters are atomic objects representing letters, digits, special symbols such as $ or _, and certain non-graphic control
characters such as space and newline. Characters literals have the \
prefix. The written character
representation of the letter A
, for example, is \A
.
The following are special literals that represent non-graphic characters:
Any Unicode character may be written with the syntax '\xhh', '\uhhhh' or '\Uhhhhhhhh' where n
consists of two, four
or eight hexadecimal digits representing a valid Unicode scalar value.
function: is_char(obj)
returns: true if `obj` is a character, false otherwise.
function: char_is_eq(char1, ...)
function: char_is_lt(char1, ...)
function: char_is_gt(char1, ...)
function: char_is_lteq(char1, ...)
function: char_is_gteq(char1, ...)
function: char_is_ci_eq(char1, ...)
function: char_is_ci_lt(char1, ...)
function: char_is_ci_gt(char1, ...)
function: char_is_ci_lteq(char1, ...)
function: char_is_ci_gteq(char1, ...)
returns: true if the relation holds, false otherwise.
The `char_is_ci_***` predicates ignores the case of the characters being compared.
// Example:
char_is_eq(\a, \A);
//=> false
char_is_ci_eq(\a, \A);
//=> true
function: char_is_alphabetic(char)
function: char_is_numeric(char)
function: char_is_whitespace(char)
returns: true if the `char` is a letter, digit or whitespace, false otherwise.
function: char_is_lower_case(char)
function: char_is_upper_case(char)
returns: true if the `char` is lower case/upper case, false otherwise.
function: char_upcase(char)
function: char_downcase(char)
returns: the upper case/lower case counterpart of `char`.
function: char_to_integer(char)
returns: the Unicode scalar value of `char` as an exact integer.
function: integer_to_char(n)
returns: the character corresponding to the Unicode scalar value `n`.
Strings are sequences of characters and are often used as containers for blocks of text. A string is
written as a sequence of characters enclosed in double quotes, e.g., "hi there"
. A double quote may be introduced into a
string by preceding it by a backward slash, e.g., "two \"quotes\" within"
.
A backward slash may also be included
by preceding it with a backward slash, e.g., "a \\slash"
. Various special characters can be
inserted with other two-character
sequences, e.g., \n
for newline, \r
for carriage return, and \t
for tab. Any Unicode character
may be inserted with the syntax \xhh
, \uhhhh
or \Uhhhhhhhh
where h
consists of two,
four or eight hexadecimal digits and represents a valid Unicode scalar value.
Strings are indexed by exact nonnegative integers, and the index of the first element of any string is 0
. The highest
valid index for a given string is one less than its length.
function: is_string(obj)
returns: true if `obj` is a string, false otherwise.
function: string_is_eq(string1, ...)
function: string_is_lt(string1, ...)
function: string_is_gt(string1, ...)
function: string_is_lteq(string1, ...)
function: string_is_gteq(string1, ...)
function: string_is_ci_eq(string1, ...)
function: string_is_ci_lt(string1, ...)
function: string_is_ci_gt(string1, ...)
function: string_is_ci_lteq(string1, ...)
function: string_is_ci_gteq(string1, ...)
returns: true if the relation holds, false otherwise.
The `string_is_ci_***` predicates ignores the case of the strings being compared.
function: string_starts_with(string, prefix)
function: string_ends_with(string, suffix)
function: string_ci_starts_with(string, prefix)
function: string_ci_ends_with(string, suffix)
returns: true if `string` starts with/ends with the `prefix`/`suffix`, false otherwise.
The `string_ci_***` predicates ignores the case of the strings being compared.
function: string(char1, ...)
returns: a string containing the characters `char1 ...`.
function: make_string(n, char = \nul)
returns: a string of length `n`.
If `char` is supplied, the string is filled with `n` occurrences of `char`.
// Example:
make_string(5);
//=> "\0\0\0\0\0"
make_string(5, \y);
//=> "yyyyy"
function: string_length(string)
function: count(string)
returns: the number of characters in `string`.
function: string_at(string, n)
returns: the `n`th element of `string`.
`n` must be an exact nonnegative integer less than the length of `string`.
Note the returned value is the nth byte in `string`, not the nth character,
because the UTF-8 encoding of a non-ASCII code point requires two or more bytes.
// Example:
string_at("hello", 1);
//=> \e
function: string_set(string, n, b)
returns: void
Changes the `n`th byte of `string` to `b`.
`n` must be an exact nonnegative integer less than the length of `string`.
// Example:
let s = "hello";
string_set(s, 1, \E);
s;
//=> "hEllo"
syntax: string[index]
returns: if `index` is an integer, the byte at `index`.
if `index` is a pair of integers, a string that contains the bytes in that range.
if `index` is out of bounds, an error is raised.
// Examples:
let s = "hello, world";
s[3];
//=> \l
s[1:5];
//=> "ello"
s[3:];
//=> "lo, world"
slogan> s[:3];
//=> "hel"
syntax: string[index] = b
returns: void
destructively sets the byte at `index` to `b`, raises an error if `index` is out of bounds.
// Example:
s;
//=> "hello, world"
s[3] = \L;
s[2] = \L;
s;
//=> "heLLo, world"
function: strings_at(strings, n)
returns: a list of the `n`th bytes of all strings in the list strings.
`n` must be an exact nonnegative integer less than the length of all `strings`.
// Example:
strings_at(["hello", "icon"], 0);
//=> [\h, \i]
function: string_replace_all(string, char1, char2)
returns: a new string with all occurences of `char1` in string replaced with `char2`.
function: string_copy(string)
returns: a new copy of string.
// Example:
let s = "hello";
let r = string_copy(s);
string_set(r, 1, \E);
s;
//=> "hello"
r;
//=> "hEllo"
function: string_append(string1, ...)
returns: a new string formed by concatenating the `string1 ...`.
// Example:
string_append("abc", "def");
//=> "abcdef"
function: substring(string, start, end)
returns: a copy of `string` from `start` (inclusive) to `end` (exclusive).
`start` and `end` must be exact nonnegative integers; `start` must be less than the
length of `string`, while `end` may be less than or equal to the length of `string`.
If `end <= start` is true, a string of length zero is returned.
function: string_fill(string, char)
returns: void
sets every character in `string` to `char`.
// Example:
let s = "hello";
string_fill(s, \a);
s;
//=> "aaaaa"
function: string_upcase(string)
function: string_downcase(string)
function: string_titlecase(string)
returns: the upper-case/lower-case/title-case equivalent of `string`.
function: string_ltrim(string)
function: string_rtrim(string)
function: string_trim(string)
returns: a new copy of `string` with whitespaces removed from its left,
right or both left and right.
function: string_map(f, string1, string2, ...)
returns: a new string with characters returned by the function `f`.
`f` should accept as many arguments as there are strings and return a single character.
function: string_for_each(f, string1, string2, ...)
returns: void.
string_for_each is similar to string_map except for the fact that `f` is called
for its side-effect.
// Example:
let (s = make_string(10), i = 0)
{ string_for_each(^(c, d) { string_set(s, i, c);
string_set(s, i + 1, d);
i = i + 2 },
"hello", "world");
s };
//=> "hweolrllod"
function: string_split(string, @optional delim = \space, include_empty = false)
returns: a list of strings that contain the parts of `string`.
`delim` can be a character, a list of characters or a predicate that accepts a character as its
sole argument. If `include_empty` is true empty parts are also included in the result.
If `delim` is a character, `string` is split at each position where `delim` occurs.
// Examples:
string_split("hello, world");
//=> ["hello,", "world"]
string_split("hello, world", \,);
//=> ["hello", " world"]
string_split("hello,, world", \,);
//=> ["hello", " world"]
string_split("hello,, world", \,, true);
//=> ["hello", "", " world"]
If `delim` is a list of characters, `string` is split at each position where one of those
characters occur.
// Examples:
string_split("1;2.3,4.5;6", [\;, \., \,]);
//=> ["1", "2", "3", "4", "5", "6"]
string_split("1;2.3,4.5;6", [\;, \,]);
//=> ["1", "2.3", "4.5", "6"]
If `delim` is a function, each character in `string` is send to `delim`.
If `delim` returns true for a character, `string` is split at that point and that part is
added to the result list.
// Examples:
string_split("hello world");
//=> ["hello", "world"]
string_split("hello world", ^(c) char_is_eq(c, \l));
//=> ["he", "o wor", "d"]
string_split("hello world", ^(c) char_is_eq(c, \l), true);
//=> ["he", "", "o wor", "d"]
function: strings_join(infix, strings)
returns: a new string that was created by appending each string in the list `strings` with `infix`.
// Example:
strings_join("-", ["abc", "123", "def"]);
//=> "abc-123-def"
function: string_to_list(string)
returns: a list of the characters in `string`.
function: list_to_string(list)
returns: a string of the characters in `list`.
// Examples:
string_to_list("hello");
//=> [\h, \e, \l, \l, \o]
list_to_string(string_to_list("hello")) == "hello";
//=> true
function: string_to_u8array(string)
returns: the bytes in the string as an byte-array.
function: u8array_to_string(byte-array)
returns: a string initialized from the bytes in the byte-array.
// Examples:
string_to_u8array("hello");
//=> #u8[104, 101, 108, 108, 111]
u8array_to_string(#u8[65, 66, 67]);
//=> "ABC"
function: to_string(obj)
returns: a string representation of the generic object `obj`.
`to_string` is a generic function by default and can be overridden for
custom datatypes without an explicit `declare generic`.
function: string_index_of(string, substr, offset = 0)
returns: the index of `substr` within `string` or -1.
// Examples:
string_index_of("hello, world", "ll");
//=> 2
string_index_of("hello, world", "0");
//=> -1
string_index_of("hello, world", "o");
//=> 4
string_index_of("hello, world", "o", 5);
//=> 8
Symbols are used for a variety of purposes as symbolic names in Slogan programs. Strings could be used for most of the same purposes,
but an important characteristic of symbols makes comparisons for equality much more efficient. Two symbols with the same name are
identical in the sense of is_eq
. This is so because two symbols with the same sequence of characters are always stored
in the same location in memory.
Symbol constants are written by prefixing identifiers with the quote mark ('
). All characters valid in identifiers can
be used in symbols. Symbols with spaces and special characters are written by enclosing the symbol in tick (`
) quotes.>
function: is_symbol(obj)
returns: true if `obj` is a symbol, false otherwise.
function: string_to_symbol(string)
returns: a symbol whose name is `string`.
// Example:
is_eq('hello, string_to_symbol("hello"));
//=> true
function: symbol_to_string(symbol)
returns: the name of `symbol` as a string.
// Example:
symbol_to_string('`hello world!`);
//=> "hello world!"
function: gensym(prefix = 'g)
returns: a new uninterned symbol.
Uninterned symbols are guaranteed to be distinct from the symbols created in
program source code or generated by functions like `read` and `string_to_symbol`.
`prefix` is a symbol that will be used as the prefix for the generated symbol.
// Examples:
gensym();
//=> g0
gensym('me);
//me1
An array literal looks similar to a list literal, except that the opening bracket is preceded by a hash (#
).
For example, an array consisting of the elements 1
, 2
, and 3
will be written as #[1, 2, 3]
.
function: array(obj1, ...)
returns: an array of the objects `obj1 ...`.
// Examples:
array();
//=> #[]
array(1, 2, 3);
//=> #[1, 2, 3]
function: make_array(d, fill = false)
returns: an array of dimension `d`.
`d` can be either an exact nonnegative integer or a list of exact nonnegative integers.
All the elements of the array is inistalized to the value `fill`.
// Examples:
make_array(5);
//=> #[false, false, false, false, false]
make_array(5, 'hello);
//=> #[hello, hello, hello, hello, hello]
make_array([2, 3], 'hello);
//=> #[#[hello, hello, hello], #[hello, hello, hello]]
function: is_array(obj)
returns: true if `obj` is an array, false otherwise.
function: array_length(array)
function: count(array)
returns: the number of elements in `array`.
function: array_at(array, index)
function: array_ref(array, index)
syntax: array[index]
returns: the element at the (zero-based) `index` position of `array`.
array_at and [] will raise an "Out of range" error if `index < 0 or index >= array_length(array)`.
For `array_ref`, `index` can also be a list of integers that indicates the position inside
a multi-dimensional array.
// Examples:
let a = #[1, 2, "hi", 'abc];
array_at(a, 3);
//=> abc
a[2];
//=> "hi"
a = #[#[1,2], #[3,4]];
array_ref(a, [1, 0]);
//=> 3
function: arrays_at(arrays, index)
returns: a list of elements at the (zero-based) `index` of the arrays in the list `arrays`.
`index` can also be a list of integers that indicates the position inside a
multi-dimensional array.
// Example:
arrays_at([#[1, 2, 3], #['a, 'b, 'c]], 2);
//=> [3, c]
function: array_set(array, index, obj)
function: array_ref_set(array, index, obj)
returns: void
syntax: array[index] = obj
Sets the element at the (zero-based) index of the array arr to the object obj.
For `array_ref_set`, the index argument can also be a
list of integers that indicates the position inside a multi-dimensional array.
// Examples:
let a = make_array([2, 3]);
a;
//=> #[#[false, false, false], #[false, false, false]]
array_ref(a, [1, 2]);
//=> false
array_ref_set(a, [1, 2], "hello");
array_ref(a, [1, 2]);
//=> hello
a[1][2];
//=> hello
a[0][1] = 100;
a;
//=> #[#[false, 100, false], #[false, false, "hello"]]
function: array_fill(array, object)
returns: void
sets all elements of `array` to `object`.
function: array_to_list(array)
returns: a list with all the elements of `array`.
function: list_to_array(list)
returns: an array with all the elements of `list`.
function: array_copy(array)
returns: a new array with all the elements of `array`.
function: array_append(array, @rest a1, ...)
returns: a new array with all the elements of the arrays `a1 ...` appended to `array`.
// Examples:
array_append(#[1, 2, 3], #[4, 5, 6], #[100]);
//=> #[1, 2, 3, 4, 5, 6, 100]
array_append(#[1, 2, 3]);
//=> #[1, 2, 3]
function: array_shrink(array, n)
returns: void
reduces the size of `array` to `n`.
// Example:
let a = #['a, 'b, 'c];
array_shrink(a, 1);
a;
//=> #[a]
function: subarray(array, start, end)
returns: a new array with the elements of `array` from `start` (inlusive) to `end` (exclusive).
// Examples:
subarray(#[1, 2, 3, 4, 5], 2, 4);
//=> #[3, 4]
subarray(#[1, 2, 3, 4, 5], 2, 5);
//=> #[3, 4, 5]
function: subarray_fill(array, start, end, object)
returns: void
This function is like array_fill, but fills only a selected part of `array`.
It sets the elements of `array`, beginning with index `start` (inclusive) and ending
with index `end` (exclusive) to `object`.
// Examples:
let a = #[1, 2, 3, 4, 5];
subarray_fill(a, 2, 4, 'x);
a;
//=> #[1, 2, x, x, 5]
function: subarray_move(array, start, end, dst_array, dst_start)
returns: void
Replaces part of the contents of `dst_array` with part of the contents of `array`.
Copies elements from `array`, beginning with index `start` (inclusive) and ending with
index `end` (exclusive) to `dst_array` beginning with index `dst_start` (inclusive).
// Example:
let a = #[1, 2, 3, 4, 5];
let b = #['a, 'b, 'c, 'd, 'e];
subarray_move(a, 3, 5, b, 1);
b;
//=> #[a, 4, 5, d, e]
function: array_map(f , array1, ...)
returns: an array containing the result of applying the function `f` to
the elements of `array1, ...`.
The arrays `array1 ...` must be of the same length.
`f` must accept as many arguments as there are arrays.
// Example:
array_map(^(x, y) x * y, #[1, 2, 3], #[4, 5, 6]);
//=> #[4, 10, 18]
function: array_for_each(f , array1, ...)
returns: void
Similar to array_map but the `f` is called only for its side-effect.
// Example:
array_for_each(^(x, y) showln(x * y), #[1, 2, 3], #[4, 5, 6]);
//-> 4
10
18
Bytearrays are arrays of raw binary data. They are organized as a sequence of exact unsigned or signed 8-bit integers. The length of a bytearray is the number of 8-bit bytes it store, and indices into a bytearray are always given as byte offsets.
Unsigned bytearray literals are written with the #u8
prefix and signed byte arrays are written with the #s8
prefix.
For example, this is an unsigned bytearray literal: #u8[1, 10, 250]
, and this is a signed bytearray literal:
#s8[-10, 90, -100]
. An unsigned bytearray can contain integers in the range 0 to 255
and a
signed bytearray can contain integers in the range -128 to 127
.
This section describe the functions that operate on both signed and unsigned bytearrays. The functions that deal with unsigned values
have the suffix or infix u8
and the functions that deal with signed values have the suffix or infix s8
.
function: is_u8array(obj)
function: is_s8array(obj)
returns: true if `obj` is an array of unsigned/signed values, false otherwise.
function: u8array(ubyte1, ...)
function: s8array(sbyte1, ...)
returns: an array of the unsigned/signed values.
function: make_u8array(d, fill = 0)
function: make_s8array(d, fill = 0)
returns: an array of unsigned/signed values of size `d`.
`d` must be an unsigned integer. If `fill` is provided, the array elements are initialized
to that value.
function: u8array_length(bytearray)
function: s8array_length(bytearray)
function: count(bytearray)
returns: the number of elements in `bytearray`.
function: u8array_at(bytearray, n)
function: s8array_at(bytearray, n)
returns: the `n`th element of `bytearray`.
function: u8array_set(bytearray, n, b)
function: s8array_set(bytearray, n, b)
returns: void
Sets the `n`th element of `bytearray` to `b`.
syntax: bytearray[index]
returns: the byte at `index`.
Raises an error if `index` is out of bounds.
syntax: bytearray[index] = b
returns: void
Destructively sets the byte at `index` to `b`.
Raises an error is `index` is out of bounds.
// Examples:
let ub = #u8[1,2,3];
ub[2];
//=> 3
ub[2] = 100;
ub;
//=> #u8[1, 2, 100]
let sb = #s8[-1,-2,-3];
sb[2];
//=> -3
sb[2] = -90;
sb;
//=> #s8[-1, -2, -90]
function: u8array_to_list(bytearray)
function: s8array_to_list(bytearray)
returns: a list with all the elements of `bytearray`
function: list_to_u8array(list)
function: list_to_s8array(list)
returns: a bytearray with all the elements of `list`.
function: object_to_u8array(obj, encoder = identity)
returns: a bytearray that encodes the object `obj`.
function: u8array_to_object(bytearray, decoder = identity)
returns: an object deserialized from `bytearray`.
The optional `encoder` and `decoder` parameters are functions that take a single argument.
`encoder` is called during serialization. As the serializer walks through `obj`,
it calls `encoder` on each sub-object that is encountered. Similarly `decoder`
is called during deserialization once for each sub-object encountered.
The `encoder` and `decoder` are used to perform transformations on the objects.
function: u8array_fill(bytearray, fill)
function: s8array_fill(bytearray, fill)
returns: void
Sets all elements in `bytearray` to the object `fill`.
function: u8array_append(bytearray, @rest bytearray_1, ...)
function: s8array_append(bytearray, @rest bytearray_1, ...)
returns: a new bytearray created by appending `bytearray_1 ...` to `bytearray`.
function: u8array_copy(bytearray)
function: s8array_copy(bytearray)
returns: a new bytearray with all the elements of bytearray.
function: u8array_shrink(bytearray, n)
function: s8array_shrink(bytearray, n)
returns: void
Reduces the size of `bytearray` to `n`.
function: subu8array(bytearray, start, end)
function: subs8array(bytearray, start, end)
returns: a new bytearray with all the elements of `bytearray` from the index `start` (inclusive)
to `end` (exclusive).
function: subu8array_fill(bytearray, start, end, fill)
function: subs8array_fill(bytearray, start, end, fill)
returns: void
Similar to u8array_fill or s8array_fill, but only the elements from `start` (inclusive) to `end`
(exclusive) are replaced with `fill`.
function: subu8array_move(bytearray, start, end, dst_bytearray, dst_start)
function: subs8array_move(bytearray, start, end, dst_bytearray, dst_start)
returns: void
Replaces part of the contents of `dst_bytearray` with part of the contents of `bytearray`.
It copies elements from `bytearray`, beginning with index `start` (inclusive) and ending with
index `end` (exclusive) to `dst_bytearray` beginning at index `dst_start` (inclusive).
Slogan also supports arrays of 16-bit, 32-bit and 64-bit signed/unsigned integers and 32-bit and 64-bit floats. The prefixes assigned to these arrays are:
#s16
, #u16
, #s32
, #u32
, #s64
, #u64
, #f32
and #f64
respectively. The functions that work with these arrays are also similar to those of bytearrays, just use the appropriate type prefix or infix. For example, the predicate to check if an array is an #f32
is is_f32array
.
function: is_u8array(obj)
function: is_s8array(obj)
function: is_u16array(obj)
function: is_s16array(obj)
function: is_u32array(obj)
function: is_s32array(obj)
function: is_u64array(obj)
function: is_s64array(obj)
function: is_f32array(obj)
function: is_f64array(obj)
returns: true if `obj` is a numeric-array of type unsigned 8-bit integer, signed 8-bit integer,
unsigned 16-bit integer, signed 16-bit integer, unsigned 32-bit integer, signed 32-bit integer,
unsigned 64-bit integer, signed 64-bit integer, 32-bit floating point or 64-bit floating point
respectively, false otherwise.
// Example
is_u8array(#[1, 2, 3]);
//=> false
is_u8array(#u8[1, 2, 3]);
//=> true
is_s8array(#s8[-1, 2, 3]);
//=> true
A bitarray is a compact array of bit values.
Bitarray literals have the prefix #b
. For example, this is a bitarray literal with 4 bits: #b[1, 0, 1, 1]
.
function: bit_array(bit1, ...)
returns: a new bitarray with `bit1 ...` as elements.
Each of the values `bit1 ...` should be either 0 or 1.
(All values other than 0 will turn the corresponding bit on).
// Example:
bit_array(1, 0, 0, 1, 1);
//=> #b[1, 0, 0, 1, 1]
function: make_bit_array(n)
returns: a new bitarray of size `n`.
All the elements are initialized to 0.
function: is_bit_array(obj)
returns: true if `obj` is a bit-array, false otherwise.
// Examples:
is_bit_array(#b[1, 0, 1, 1]);
//=> true
is_bit_array(#u8[1, 0, 1, 1]);
//=> false
function: bit_array_length(bitarray)
function: count(bitarray)
returns: the number of bits in `bitarray`.
function: bit_array_set(bitarray, n)
returns: void
Turns the `n`th bit on in `bitarray`.
function: bit_array_is_set(bitarray, n)
returns: true if the `n`th bit is on in `bitarray`.
// Example:
let a = make_bit_array(5);
bit_array_set(a, 2);
bit_array_is_set(a, 2);
//=> true
bit_array_is_set(a, 1);
//=> false
function: bit_array_at(bitarray, n)
returns: the bit value at `n`.
syntax: bitarray[index]
returns: true if the bit at `index` is on in `bitarray`.
If `index` is out of bounds, returns false.
syntax: bitarray[index] = b
returns: void
If `index` is out of bounds, no change will happen to `bitarray`.
Turns on the bit at `index` if `b` is a truthy, turns it off if `b` is false.
Note that `0` is a truth value.
// Examples:
a;
//=> #b[0, 0, 1, 0, 0]
a[0] = true;
a;
//=> #b[1, 0, 1, 0, 0]
a[0];
//=> true
a[0] = 0;
a;
//=> #b[1, 0, 1, 0, 0]
a[0] = false;
a;
//=> #b[0, 0, 1, 0, 0]
function: bit_array_is_all_set(bitarray)
returns: true if all bits are on in `bitarray`.
function: bit_array_is_any_set(bitarray)
returns: true if atleast one bit is on in `bitarray`.
function: bit_array_clear(bitarray, n)
returns: void
Turns the `n`th bit off in `bitarray`.
function: bit_array_set_all(bitarray)
returns: void
Turns all bits on in `bitarray`.
function: bit_array_clear_all(bitarray)
returns: void
Turns all bits off in `bitarray`.
function: bit_array_to_list(bitarray)
returns: a list of integers corresponding to the bits in `bitarray`.
function: list_to_bit_array(lst)
returns: a bitarray initialized from the list of integers `lst`.
function: bit_array_to_string(bitarray)
returns: a string of the characters '1' and '0' corresponding to the bits in `bitarray`.
// Example:
bit_array_to_string(#b[1, 0, 1, 1]);
//=> "1011"
function: string_to_bit_array(string)
returns: a bitarray initialized from `string`.
`string` should only contain the characters '1' and '0'.
// Example:
string_to_bit_array("1011");
//=> #b[1, 0, 1, 1]
function: u8array_to_bit_array(u8array)
returns: a bitarray initialized from the bits in `u8array`.
function: bit_array_is_eq(arr1, arr2)
returns: true if the bitarrays `arr1` and `arr2` has the same bits set.
function: bit_array_blit(bitarray, offset, dest_bitarray, dest_offset, count)
returns: the updated bitarray dest_bitarray.
Copies the bit-pattern in `bitarray` to `dest_bitarray`. `count` bits are copied starting
at `offset` in `bitarray`. Bits are copied to `dest_bitarray` starting at `dest_offset`.
function: bit_arrays_concat(arrays)
returns: a new bit array which is the concatenation of the bitarrays in the list `arrays`.
// Example:
bit_arrays_concat([#b[1, 0, 1], #b[0, 1, 0], #b[1, 1, 1]]);
//=> #b[1, 0, 1, 0, 1, 0, 1, 1, 1]
function: subbitarray(bitarray, start, end)
returns: a new bitarray with elements from `bitarray` starting at `start` (inclusive)
and ending at `end` (exclusive).
Hashtables represent sets of associations between arbitrary values. They serve essentially the same purpose as
association lists but are typically much faster when large numbers of associations are involved. Hashtable literals are written
by enclosing pairs of associations in #{}
, for example #{'name:'slogan, 'type:'dynamic}
.
function: make_hashtable(hash_f, eq_predicate, @optional size)
retruns: a new hashtable.
`hash_f` and `eq_predicate` must be functions. If `size` is provided, it must be a nonnegative
exact integer indicating approximately how many elements the hashtable should initially hold.
Hashtables grow as needed, but when the hashtable grows it generally must rehash all of the
existing elements. Providing a nonzero size can help limit the amount of rehashing that must
be done as the table is initially populated.
The new hashtable computes hash values using `hash_f` and compare keys using `eq_predicate`,
neither of which should modify the hashtable. `eq_predicate` should compare two keys and return
false only if the two keys should be distinguished. `hash_f` should accept a key as an
argument and return a nonnegative exact integer value that is the same each time it is called
with arguments that `eq_predicate` does not distinguish.
// Example:
let h = make_hashtable(string_hash, string_is_eq);
hashtable_set(h, "name", "mat");
hashtable_at(h, "name");
//=> "mat"
function: make_eq_hashtable(@optional size)
function: make_eqv_hashtable(@optional size)
retruns: a new hashtable that uses is_eq/is_eqv for comparing keys.
An eq_hashtable or a eqv_hashtable employs a hash function based on object addresses.
Their hash and equivalence functions are suitable for any object.
function: make_equal_hashtable(@optional size)
retruns: a new hashtable that uses is_equal for comparing keys.
An equal_hashtable uses the built-in equal_hash function which takes into
consideration the current structure and contents of a key.
function: is_hashtable(obj)
retruns: true if `obj` is a hashtable, false otherwise.
function: eq_hash(obj)
function: eqv_hash(obj)
function: equal_hash(obj)
function: string_hash(str)
function: string_ci_hash(str)
function: symbol_hash(sym)
retruns: an exact nonnegative integer hash value.
These hash function are suitable for use with the appropriate predicate:
is_eq for eq_hash and symbol_hash, is_eqv for eqv_hash, is_equal for equal_hash,
string_is_eq for string_hash, and string_is_ci_eq for string_ci_hash.
The hash values returned by equal_hash, string_hash, and string_ci_hash are typically
dependent on the current structure and contents of the input values and are thus unsuitable
if keys are modified while they have associations in a hashtable.
function: hashtable_set(hashtable, key, value)
retruns: void
`key` should be of the appropriate type expected by the hash and equivalence functions.
`value` may be any object. Any existing association that key holds in the hashtable
is replaced by `value`.
function: hashtable_at(hashtable , key, default = false)
retruns: the value associated with `key` in `hashtable`, `default` if no
association is found.
`key` should be of the appropriate type expected by the hash and equivalence functions.
`default` may be any object.
syntax: hashtable[key]
returns: the value associated with `key` or false.
syntax: hashtable[key] = value
returns: void
Associates `key` with `value` in `hashtable`.
// Example:
h;
//=> #{"name": "mat"}
h["n"];
//=> false
h["n"] = 100;
h;
//=> #{"n": 100, "name": "mat"}
h["n"];
//=> 100
function: hashtable_contains(hashtable , key)
retruns: true if an association for `key` exists in `hashtable`, false otherwise.
function: hashtable_update(hashtable , key, f, default)
retruns: void
`key` should be of an appropriate type for the hash and equivalence functions.
`default` may be any object. The function `f` should accept one argument, should
return one value, and should not modify the hashtable.
hashtable_update applies `f` to the value associated with `key` in the hashtable,
or to `default` if no value is associated with `key`. If `f` returns, key is
associated with the value returned by `f`, replacing the old association, if any.
function: hashtable_size(hashtable)
retruns: the number of entries in `hashtable`.
function: hashtable_copy(hashtable)
retruns: a new hashtable containing the same entries as in `hashtable`.
function: hashtable_keys(hashtable)
retruns: an array containing all the keys of `hashtable`.
function: hashtable_values(hashtable)
retruns: an array containing all the values in `hashtable`.
function: hashtable_entries(hashtable)
retruns: a pair of two arrays: the first one with the keys and the second one
with the values from `hashtable`.
function: hashtable_for_each(f, hashtable)
retruns: void
For each entry in `hashtable`, calls the function `f` by passing it the key
and value as arguments.
// Example:
let h = make_eq_hashtable();
hashtable_set(h, 'name, "nemo");
hashtable_set(h, 'age, 1);
hashtable_for_each(^(k, v) println(k, ": ", v), h);
//-> name: nemo
age: 1
A set stores an unordered sequence of objects without duplicates. It is an implementation of the mathematical concept of a finite set.
Unlike most other collection types, rather than retrieving a specific element from a set, one typically tests a value for membership in a set.
A set literal is written by enclosing the objects in a #()
. Example: #(1, 2, 3, 4, 5)
.
function: set(object)
returns: a set with the elements in `object`
`object` should be either a set, list, vector, string or a hashtable.
If `object` is a hashtable, each key-value pair will become an entry in the set
// Examples:
set(#{1:2, 3:4});
//=> #(1:2,3:4)
set("hello, world");
//=> #(\,,\o,\e,\w,\r,\h,\d,\l,\space)
set([1,2,3,2,3,4]);
//=> #(1,2,3,4)
set(#[1,2,3,2,3,4]);
//=> #(1,2,3,4)
set(#(1,2,3,2,3,4));
//=> #(1,2,3,4)
function: make_set(obj1, ...)
returns: a set of `obj1, ...`.
// Examples:
make_set();
//=> #()
make_set(1);
//=> #(1)
make_set(1,2,3,2,3,4);
//=> #(1,2,3,4)
function: set_length(set)
function: count(set)
returns: the number of elements in the set.
function: is_set(object)
returns: true if `object` is a set, false otherwise.
function: is_set_member(set, object)
returns: true if `set` contains `object`, false otherwise.
The comparison predicate used is `is_equal`.
// Examples:
is_set_member(#(1,2,3), 2);
//=> true
is_set_member(#(1,2,3), 4);
//=> false
is_set_member(#(1, 2, "hi", 3), "hi");
//=> true
function: set_difference(set1, ...)
returns: the first set without the elements of `set1, ...`.
// Examples:
set_difference(#(1,2,3));
//=> #(1,2,3)
set_difference(#(1,2), #(2,3));
//=> #(1)
set_difference(#(1,2,3), #(1), #(1,4), #(3));
//=> #(2)
set_difference(#('a, 'b, 'c, 'd), set_difference(#('a, 'b, 'c, 'd), #('c, 'e, 'a, 'f, 'd)));
//=> #(c,d,a)
function: set_intersection(set1, ...)
returns: a set that is the intersection of `set1, ...`.
// Examples:
set_intersection(#(1,2));
//=> #(1,2)
set_intersection(#(1,2), #(2,3));
//=> #(2)
function: set_union(set1, ...)
returns: a set that is the union of `set1, ...`.
// Examples:
set_union(#(1));
//=> #(1)
set_union(#(1,2), #(3,4,1));
//=> #(1,2,3,4)
function: is_superset(set1, set2)
returns: true if `set1` is a superset of `set2`.
// Examples:
is_superset(#(1,2), #(2));
//=> true
is_superset(#(1,2), #(2, 1));
//=> true
is_superset(#(1,2), #(2, 3));
//=> false
function: is_subset(set1, set2)
returns: true if `set1` is a subset of `set2`.
// Examples:
is_subset(#(1,2), #(2));
//=> false
is_subset(#(1,2), #(1,2));
//=> true
is_subset(#(1,2,3), #(1,2));
//=> false
is_subset(#(3), #(1,2));
//=> false
is_subset(#(2), #(1,2));
true
function: set_to_list(set)
returns: a list with all the elements of the set.
function: ref(object, index, @optional default = false)
returns: returns the element at `index`. if `index` is out of bounds, return `default`.
function: ref_set(object, index, value)
returns: void
`Ref_set` sets the element at `index` to value.
`Ref` and `ref_set` are generic functions that can be overriden by a user-defined object.
Strings, lists, arrays and hashtables implement them by default. Any object that
implement these functions automatically gets to work with the `[]` syntax.
// Examples:
s;
//=> "heLLo, world"
ref(s, 10);
//=> \l
s[11];
//=> \d
ref(#{"one":1, "two":2}, "three");
//=> false
ref(#{"one":1, "two":2}, "one");
//=> 1
function make_bag(xs)
let (len = array_length(xs)
bag_ref = ^(index) xs[index]
bag_ref_set = ^(index, value) xs[index] = value)
^(msg)
| 'first -> when (len > 0) array_at(xs, 0)
| 'rest -> when (len > 1) make_bag(subarray(xs, 1, len))
| 'count -> len
| 'ref -> bag_ref
| 'ref_set -> bag_ref_set;
let b = make_bag(#[10, 20, 30]);
b[0];
//=> 10
b[1] = 100;
b[1];
//=> 100
ref(b, 2);
//=> 30
ref_set(b, 2, 200);
b[2];
//=> 200


HERE Maps for ArcGIS JavaScript API
About
HERETiledMapServiceLayer module allows for adding a reference layer with content provided by HERE Maps API for JavaScript to a map constructed with ArcGIS API for JavaScript.
Please see HERE Licensing and Terms
Please see the docs for constructor options.
The module was inspired by http://gmaps-utility-gis.googlecode.com/svn/trunk/gmapslayer
Use
An example can be found at: your-working-copy/examples/nokiamapslayer.html.
It is necessary to provide valid app_id and app_code either in the code of the example or in the query string, i.e. ?app_id=xxx&app_code=yyy
(Without valid app_id and app_code access to the HERE content will be limited.)
The application settings can be obtained at https://developer.here.com/myapps and are issued under one's Nokia account.
To include in a page, one will need to load the script and the ArcGIS API. The minified and non-minified version of the module will be located at: your-working-copy/src.
The HERE API is loaded when the layer is added to the map and the layer's visibility is set to true.
Known Limitations
This was originally developed for internal needs and tested only against ArcGIS API for JavaScript version 2.6.
While the script provides ArcGIS Tiled Map Service Layer interface for HERE content the use of the HERE API may be limited.
In IE9/IE10 it requires Compatibility View and Document Mode: Internet Explorer 7 standards (can be set with X-UA-Compatible Meta Tag).
Alternatives
As of version 3.2, ArcGIS API for JavaScript allows for adding non-ArcGIS tiled services with the WebTiledLayer class.
An example of consuming HERE content provided by REST or/and JavaScript API-s with a customized BasemapGallery dijit can be found at: your-working-copy/examples/herebasemapgallery.html.


{% extends 'layouts/layout.html' %} {% block pageTitle %} About - NHS.UK prototype kit {% endblock %} {% block beforeContent %} {{ breadcrumb({ items: [ { href: "/diat/home", text: "Home" }, { href: "/diat/guidance", text: "Guidance and tools" } ], href: "/diat/content-books", text: "Recommended reading" }) }} {% endblock %} {% block content %}
Specialist reading recommended by the Service Design community.
You may also find the following content useful:


First published in 1939, Steinbeck’s Pulitzer Prize winning epic of the Great Depression chronicles the Dust Bowl migration of the 1930s and tells the story of one Oklahoma farm family, the Joads, driven from their homestead and forced to travel west to the promised land of California. Out of their trials and their repeated collisions against the hard realities of an America divided into haves and have-nots evolves a drama that is intensely human yet majestic in its scale and moral vision, elemental yet plainspoken, tragic but ultimately stirring in its human dignity.
To the red country and part of the gray country of Oklahoma, the last rains came gently, and they did not cut the scarred earth. The plows crossed and recrossed the rivulet marks. The last rains lifted the corn quickly and scattered weed colonies and grass along the sides of the roads so that the gray country and the dark red country began to disappear under a green cover. In the last part of May the sky grew pale and the clouds that had hung in high puffs for so long in the spring were dissipated. The sun flared down on the growing corn day after day until a line of brown spread along the edge of each green bayonet. The clouds appeared, and went away, and in a while they did not try any more. The weeds grew darker green to protect themselves, and they did not spread any more. The surface of the earth crusted, a thin hard crust, and as the sky became pale, so the earth became pale, pink in the red country and white in the gray country.


Laveen FC was founded in the summer of 2014 as an extension of play from Laveen Soccer. Most of the boys and girls coached had aged out and their options to continue was limited by location and by level. Laveen FC set a goal to provide a low cost alternative to the surrounding clubs but still providing the club level experience.
Laveen FC/Academy wants to empower, encourage, mentor and build our youth to become the best they can be through sportsmanship and respect of their coaches, referees, fellow players and their parents.
Laveen FC will be holding tryouts for the 2016-2017 season. All boys and girls between U8 and U1 are encouraged and welcomed at one of our tryout sessions.
Are you a new or returning player to Laveen FC? Find out how to get started in Laveen FC. We're looking for youth players dedicated to learning all about our sport.


Complete the lessons below to advance to the next level. Click on the lesson name to expand/collapse the description.
Try to think how the concepts that are demonstrated in the lesson apply to your own code. While examples may not match the platform or language you are familiar with, the concepts apply to any software: desktop applications, mobile applications or operating system services.
Here's a visual representation of one of the attacks possible using this category.
Mission: {{challenge.mission}}
Click the button below to access the play link.
Play PlayThe play link has been provided to you when solving the previous challenge. If you have missed it read the challenge description carefully and try to figure out what it is.
Once you were able to complete the challenge you can generate a code which you can submit below.
Submit CodeIf you get stuck you can use the button below for help, but don't give up yet, read the challenge description and tips carefully.
View Solution


### From .NET Framework to .NET Core #### A (Hopefully) Brief History ![Jamie](images/jamie.jpg) Jamie Taylor
https://dotnetcore.show
### Other Stuff What I Do #### In Case You're Interested | | | |--- |--- | | The .NET Core Podcast | https://dotnetcore.show/ | | Waffling Taylors | https://wafflingtaylors.rocks/ | | Dev Otaku | https://devotaku.transistor.fm/ | | Ask A Brit | https://www.askabrit.us/ | | Jay & Jay Media | https://jayandjay.media/ | | | |
### Sources I'll be "borrowing" heavily from the following places: - Richard Campbell's many talks on the history of .NET - The ASP NET Community Standups - My Podcast - My .NET Core blog - A blog post I wrote for Audacia
Be sure to pull the slides from GitHub, run them (it's a node app) and us the `s` key to see the slide notes
### In the Beginning #### There was Java - It _wasn't_ great - It _was_ the defacto enterprise language - It _was_ cross plat - It _wasn't_ RAD - There was also C++, Python, PHP, and Ruby
### Enter: NGWS ![NGWS diagram](images/ngws.jpg) - Look familiar? - Original name for .NET - Used XML for communication
### .NET #### What's In a Name > mirrored the domain suffix of every ISP...intended to remind users that "web-enabling your software" > was more approachable to business types... geekier names like "Universal Runtime" or "COM+ 2.0" > short, easy to spell, globalized well, could leverage already-owned domain names for every Microsoft product, etc. > actually passed legal/trademark review (surprisingly difficult!)
### February 2002! > Every platform, one language - Visual Studio.NET is announced500px - Bold statement - Directly opposing Java's tagli500px
### Fast Forward to 2016 ![](images/microsoft-heart-linux.jpg) - Xamarin (and Mono) is bought by Microsoft - .NET Framework 4 is eight years old - Balmer retires, Satya Nadella promoted (2014) - Azure starts offering Linux VMs
### One step back: #### September 2nd, 2014 ![Scott Hunter Quote on .NET apps on Linux Servers](images/hunter-quote.jpg)
".NET Apps on Linux Servers"
### We Already Have .NET On Linux ### Right? > I want you to be able to host ASP.NET apps on Linux servers as easily as you can on a Windows Server - Linux VMs on Azure - Docker becomes a very big thing - Mono already exists, though - .NET Core == cross plat rewrite, utilising .NET Standard - Mono == cross plat .NET Framework
### Wait! What's .NET Standard? ![](images/standard-table.jpg) - credit and debit adhere to standards by banking industries - .NET Standard is that but for .NET - Think of it as an interface
### So What Do I Target? - tl;dr: Target Standard - [Rocky Lhotka on .NET Rocks!](https://dotnetrocks.com?show=1620) - Pretty soon, we'll have 4+ platforms to support - Targetting Standard means that your code is guaranteed to work everywhere
### An Interface? #### Prepare for a terrible slide ``` csharp interface INetStandard_1_0 { System; System.Collections; System.Colloections.Generic; /* ... */ System.IO; System.Linq; System.RegularExpressions; /* etc. */ } interface INetStandard_1_1 : INetStandard_1_0 { System.Collections.Concurrent; System.Numerics; /* etc. */ } public class DotNetFramework_FourPointFive : INetStandard_1_0 { /* Actual implementations of the APIs here */ } public class DotNetFramework_FourPointSixPointOne : INetStandard_2_0 { /* Actual implementations of the APIs here */ } ```
### .NET Framework vs .NET Core Cadence - .NET Core's dev cadence is super quick - we're on v2.2 of .NET Core 2 years in - v3 is in preview, too - .NET Framework is much slower - because Windows _REQUIRES_ .NET Framework in order to run - no, .NET Framework isn't going away
### Sources #### Cite Them, Yo - [ASP+: The Next Generation Web Services Framework](https://people.apache.org/~jim/NewArchitect/webrevu/2000/11_17/developers/11_17_00_4.html) - [The History of .NET by Richard Campbell of .NET Rocks! at SSW](https://www.youtube.com/watch?v=FFCn_z7dn_A) - [The History of .NET - Richard Campbell](https://vimeo.com/239969756) - [ASP.NET Community Standup - Sept 2nd, 2014 - Introduction to ASP.NET vNext, how and why?](https://www.youtube.com/watch?v=1rLbT6pBtak) - [Episode 1 - A Brief History of .NET Core](https://dotnetcore.show/episode-1-a-brief-history-of-net-core/) - [.NET Core History](https://dotnetcore.gaprogman.com/2018/02/23/net-core-history/) - [What Is .NET Core?](https://www.audacia.co.uk/blog/2016/11/what-is-net-core)
### Any Questions?
![Jamie](images/twitter-avatar.png) - [@dotnetcoreshow](https://twitter.com/dotnetcoreshow) - [The .NET Core Podcast](https://dotnetcore.show/) - will happily answer questions here, on twitter, or at the pub
### Thank you so very much #### You've all been wonderful


freeCodeCamp
API Basejump: File Metadata Microservice
User stories:
I can submit a FormData object that includes a file upload.
When I submit something, I will receive the file size in bytes within the JSON response
Add a File
Powered by
Clementine.js


I have more than 8+ years of experience in designing and developing databases for transaction systems and data warehouse for analytical system. I also have a hands on experience in creating visualizations that communicate the message to various business users. Knowledgable in cloud computing and advanced analytics.
Apart from being a DW/BI engineer, I enjoy playing badminton, watching cricket and documentaries. I also have an interest in reading about history and psychology.
I spend a large amount of my free time exploring the latest technology advancements in cloud computing, data visualization and analytics. Recently I find myself knowing more about Arduino and Raspberry Pi.
Rajanand is a dedicated knowledgeable resource on Data warehousing area. he is experienced with latest Microsoft technologies. I have closely observed his transformation from a junior resource to experience resource within very short time with his eagerness to learn, dedication and thrust for knowledge. He is a good asset for any organisation.
Suman Chandra, Enterprise Architect, Capgemini Sverige AB
As a leader one always wants technical experts like Raj on her/his team! He always brought sound technical solutions and analysis along with calmness and team mentality. I trusted Raj with many important customer-facing issues and he never disappointed. His methodical and structured approach was always bulletproof and stood customers' challenges over and over. The quality of his work was top-notch.
Vahag Karayan, Head of Professional Services
I worked with Raj while leading projects for large entertainment studios on behalf of my client. He was one of the developer involved with both new development and post implementation activities. His account management skills while supporting customer issues and system problems were impeccable. He was very detailed and meticulous in identifying the source of the issues and super responsive and cordial to the need of the clients.
Arsiné Gaprelian, Project Manager
I had the pleasure of working with Rajanand for several years at Länsförsäkringar AB with Business Intelligence/Data warehouse. Above all, I was impressed with Rajanands ’s ability to provide high quality deliverables on complex tasks on daily basis. As a hard working team member, Rajanand earns my highest recommendation.
Nicklas Johansson, Business Solutions Manager, Länsförsäkringar AB
Very high quality and service minded are two things that come to mind when I think about Rajanand. I’ve had the pleasure of working with Rajanand for some 3 years, during which Rajanand has helped us develop our data warehouse. By asking the right questions and delivering results in a very structured way with high quality Rajanand soon became someone you could trust with the most complex work items. Rajanand is a very likeable person and has even started learning some Swedish to facilitate his Swedish client. Rajanand would be a true asset for any positions requiring advanced SSIS coding and good communication skills and comes with my heartfelt recommendation.
Fredrik Lindstedt, BI Project Lead, Länsförsäkringar AB


Here are some of the projects I have been working on while teaching
myself how to code:
Blogs
Confessions of a programming newb 06092019
I'm going to ramble on here for a bit:
It dawned on me that I have typed more in my first week of code bootcamp than I ever have in my entire life.
Still, I type away. I think I like this...
I can't wait to have the skills and proficiency to make this site look much, much better
Balancing work, class, homework, jiujitsu, and quality time with my loved ones will be difficult, if this
first week is any indication. I'm blessed to have such a supportive and encouraging circle.
I'm definitely going to gain some weight in the next four months!
I sit in a spot in class where there aren't any outlets within reach.  So far, so good.
Tech and code used to be this unreachable, magical thing people did. I never had the resources to explore it when
I was young...
I'm definitely goint to cry at some point within the next four months!
I'm quickly realizing that being organized buy using nesting and comments properly while coding saves me a lot
of time
Random snippets about first Kickstart homework class and project 06122019
I'm gonna ramble here for a bit:
It looks like my email link that isn't working is browser-specific. I'm currently using 'Chromium' browser for Ubuntu
where my link isn't working. However, when opening the html file on 'Firefox' the link worked just fine. Upon further
research, it
confirmed
to me that it is browser-specific. Whew...
We learned more about git, and using GitHub today in class. I'm starting to understand how powerful these tools are
especially when used by a big group of folks. I imagine we're gonna be using a lot of these tools for the rest of the
curriculum. My head is currently in a tizzy.
We used Bootstrap templates and, for me, it was my first experience with it. I picked the first template that caught my eye
in the first page. Had I known that there were much more, I would've likely taken my time in selecting and picked a 'fancier'
one. I'm really glad that I didn't! I think this 'simple' layout was the best for me to focus on code and not get as
consumed by asthetics... for now, at least.
Survey Form
In this project, after spending plenty of time trying too
hard using Flexbox or Grid, I learned that using Margins and Paddings were
enough to align and rearrange each input fields, even during Media Queries.
Landing Page
Creating a Landing Page was intimidating. I learned here that plenty of
considerations need to be made in presenting your product with minimal and streamlined
information as possible.
Technical Documentation Page
Since learning to format a Technical Documentation page, I have used it
extensively for note-taking to also continue practice writing code. Here, I have written
one for the CSS Flexbox lessons I was studying.
Tribute Page
While creating this Tribute Page I realized that, while studying, content
should come secondary. I spent too much time researching the topic and not enough time
getting repetition in writing code.
Technical Documentation Page
Since learning to format a Technical Documentation page, I have used it
extensively for note-taking to also continue practice writing code. Here, I have written
one for the introductory lessons for JavaScript I am currently studying in my "free time".
(still in progress).
Personal Portfolio
In my first attempt at a Portfolio Page the thumbnail images of my projects
wouldn't always work. On my second day at Kickstart Coding, I
learned about Relative File Paths that fixes that problem!


I’m a designing designer & a developing developer. I consider myself a naturally curious and very intuitive self-starter. My co-workers describe be as “easy going, positive, bright, enthusiastic, inquisitive, diligent, collaborative, willing & loyal”, some pretty nice words if you ask me. I'm a team player at heart & I like to make things happen. In the last year, I’ve realized I really like programming. I hope to combine my love for art, design & coding to make interactive & meaningful work.
here are some things I love...
here are some things I love in order of how much I love them...
An interactive, digital projection project. Built in Processing, using javascript. Uses a kinect as hardware.
Github Repo


Comment Policy
Keep it civil aka don’t be a jerk
We’re going to get into the thick of a lot of heated discussions and that’s okay. These discussions often entail topics that we all personally care a lot about and will passionately defend. But in order for discussions to thrive here, we need to remember to criticize ideas, not people.
So, remember to avoid:
- name-calling
- ad hominem attacks
- Responding to a post’s tone instead of its actual content.
- knee-jerk contradiction
- Comments that we find to be hateful, inflammatory, or harassing may be removed. If you don’t have something nice to say about another user, don’t say it. Treat others the way you’d like to be treated.
Always strive to add value to every interaction and discussion you participate in
There are a lot of discussions that happen every day on Disqus. Before joining in a discussion, browse through some of the most recent and active discussions happening in the community, especially if you’re new there.
If you are not sure your post adds to the conversation, think over what you want to say and try again later.
Keep it tidy
Help make moderators’ lives easier by taking a moment to ensure that what you’re about to post is in the right place. That means:
- don’t post off-topic comments or discussions: check the Explore page to find the appropriate channel for your topic.
- don’t cross-post the same thing multiple times
- review any specific posting guidelines for the community: some communities such as a movies community may have specific rules regarding spoilers.
- check if another active discussion on your topic has already been posted
If you see something, say something
Moderators are at the forefront of combatting spam, mediating disputes and enforcing community guidelines and, so are you.
If you see an issue, contact the moderators if possible or flag any comments for review. If you believe someone has violated the Basic Rules, report it to Disqus by flagging the user’s profile.