Cordy Standard Library
The Cordy standard library consists of a number of functions which are placed in the global namespace. These are not shadow-able or able to be overridden.
Type Signatures
[top]
The below type signatures are entirely for documentation purposes, as Cordy does not have explicit type signatures and is entirely dynamically typed. The type signatures below obey the following conventions:
any
refers to any type.iterable<T>
refers to an iterable type with element typeT
, which includeslist<T>
,set<T>
,dict<K, V>
(whereT = vector<K, V>
),heap<T>
,vector<T>
, orstr
(whereT = str
)fn(A1, A2, ...) -> B
refers to a function which accepts arguments of typesA1, A2, ...
, and returns a value of typeB
...
is used to indicate a function has multiple possible signatures, which will be noted below.f(T, ...)
is used to indicate the functionf
can take any number of arguments of the typeT
|
is used to indicate an argument can be one of multiple types, i.e.it: A | B
indicatesit
can be of typeA
orB
.bool
is considered a subtype ofint
, andint
is considered a subtype of bothcomplex
andrational
Print print(any, ...)
[top]
Prints each argument, space separated and with a single \n
suffix, to standard output. Non-string types will have str()
called on them before printing.
When called with no arguments, prints a single \n
character.
Returns nil
Read read() -> str
[top]
Reads from stdin
until end of file. Returns the result as a string.
N.B. When reading from external sources, newline \r\n
sequences will be replaced with a single \n
.
Read Line read_line() -> str
[top]
Reads a single line from stdin
. Returns the result as a string, with the newline suffix removed.
Read Text read_text(path: str) -> str
[top]
Reads from a text file, located at path
. Any error reading the file will cause the program to exit. Returns the result as a string.
N.B. When reading from external sources, newline \r\n
sequences will be replaced with a single \n
.
Write Text write_text(path: str, content: str) -> str
[top]
Writes the string content
to the file at path
, in overwrite mode. A file will be created if it does not exist, and if it does it will be overwritten.
Env env(...) -> any
[top]
Possible signatures:
env(): dict<str, str>
env(key: str) -> str | nil
When invoked with no arguments, will return a dictionary of all currently present environment variables. When invoked with one argument, will query that specific environment variable and return its value if defined, or nil
if not.
Prefer using env(key)
over env()[key]
.
Example
$ cat example.cor
env('DUCKS') . repr . print
$ cordy example.cor
nil
$ DUCKS=1337 cordy example.cor
'1337'
Argv argv() -> list<str>
[top]
Returns the list of user defined program arguments when invoked. These are arguments appended to the cordy invocation after the file name.
Example
$ cat example.cor
argv() . repr . print
$ cordy example.cor --number-of-ducks 2 -run
['--number-of-ducks', '2', '-run']
Bool bool(x: any) -> bool
[top]
Returns the argument as a boolean. nil
, 0
, false
, ''
, and empty collections, will return false
, everything else will return true
.
The keyword bool
can also be used in an is
expression, to check if a value is of the type bool
.
Int <T> int(x: any, def?: T) -> int | T
[top]
Attempts to convert the first argument to an integer.
nil
,false
will return0
true
will return1
int
values will return themselvesrational
values will return their numerator, if the denominator is equal to1
, and the numerator is small enough to fit in anint
without loss of precision-
str
values will attempt to parse as an integer, and raise an error if the string is not parsable as an integer.If a second argument is provided, in the case of an error it will return
def
instead
The keyword int
can also be used in an is
expression, to check if a value is of the type int
.
Example
>>> int('3')
3
>>> 3 is int
true
>>> '3' is int
false
Complex complex
[top]
The keyword complex
can be used in an is
expression, to check if a value is of the type complex
Note that due to subtype relationships, bool
and int
values are also considered to be is complex
.
Rational rational(numer: rational, denom: rational = 1) -> rational
[top]
rational
is used to produce rational numbers from rational-like integers. When provided with one argument, this returns the rational form of that integer, so rational(x)
produces x / 1
. When provided with two arguments, this returns the rational of the given numerator and denominator, so rational(x, y)
produces x / y
.
rational
can also be used to parse strings representing rational numbers, much like int
. These take the form of either 'N'
or 'N/D'
, where N
and D
can be parsed as arbitrary precision integers.
The keyword rational
can also be used in an is
expression, to check if a value is of the type complex
Note that due to subtype relationships, bool
and int
values are also considered to be is complex
.
Example
>>> rational(1)
1 / 1
>>> rational(2, 7)
2 / 7
>>> rational('123/345')
41 / 152
Str str(x: any) -> str
[top]
Returns the argument as a string. See also repr
.
The keyword str
can also be used in an is
expression, to check if a value is of the type str
.
List list(...) -> list
[top]
Possible signatures:
list() -> list
<T> list(it: iterable<T>) -> list<T>
<T> list(T, ...) -> list<T>
With no arguments, creates an empty list, the same as []
. With one argument, treats the argument as an iterable and copies each element into a new list. With more than one argument, collects each argument into a list.
The keyword list
can also be used in an is
expression, to check if a value is of the type list
.
Example
>>> list()
[]
>>> list('hello')
['h', 'e', 'l', 'l', 'o']
>>> list(1, 2, 3, 4)
[1, 2, 3, 4]
Set set(...) -> set
[top]
Possible signatures:
set() -> set
<T> set(it: iterable<T>) -> set<T>
<T> set(T, ...) -> set<T>
With no arguments, creates an empty set. With one argument, treats the argument as an iterable and copies each element into a new set. With more than one argument, collects each argument into a set.
The keyword set
can also be used in an is
expression, to check if a value is of the type set
.
Example
>>> set()
{}
>>> set('blahaj')
{'b', 'l', 'h', 'a', 'j'}
>>> set(1, 2, 3, 4)
{1, 2, 3, 4}
Dict dict(...) -> dict
[top]
Possible signatures:
dict() -> dict
<K, V> dict(it: iterable<vector<K, V>>) -> dict<K, V>
<K, V> dict(vector<K, V>, ...) -> dict<K, V>
With no arguments, creates an empty dictionary. With one argument, treats the argument as an iterable of key-value pairs and collects it into a new dictionary. With more than one argument, treats each argument as a key-value pair and collects each argument into a dictionary.
The keyword dict
can also be used in an is
expression, to check if a value is of the type dict
.
Heap heap(...) -> heap
[top]
Possible signatures:
heap() -> heap
<T> heap(it: iterable<T>) -> heap<T>
<T> heap(T, ...) -> heap<T>
With no arguments, creates an empty heap. With one argument, treats the argument as an iterable and copies each element into a new heap, maintaining the heap invariant. With more than one argument, collects each argument into a heap, maintaining the heap invariant.
Note: Heaps of different types behavior is unspecified, as different types will compare equal and can have any internal ordering.
The keyword heap
can also be used in an is
expression, to check if a value is of the type heap
.
Vector vector(...) -> vector
[top]
Possible signatures:
vector() -> vector
<T> vector(it: iterable<T>) -> vector<T>
<T> vector(T, ...) -> vector<T>
With no arguments, creates an empty vector. With one argument, treats the argument as an iterable and copies each element into a new vector. With more than one argument, collects each argument into a vector.
The keyword vector
can also be used in an is
expression, to check if a value is of the type vector
.
Function function
[top]
The keyword function
can be used in an is
expression, to check if a value is of the type function
.
Example
>>> fn() {} is function
true
>>> print is function
true
>>> 'hello' is function
false
Iterable iterable
[top]
The keyword iterable
can be used in an is
expression, to check if a value is of any iterable
type.
Example
>>> '123' is iterable
true
>>> 123 is iterable
false
Repr repr(x: any) -> str
[top]
Returns the full representation of x
, as a string. Strings are wrapped in single quotes, unlike str
, although is functionally similar in other respects.
Example
>>> repr('hello')
'hello'
Eval eval(x: str) -> any
[top]
Compiles and evaluates the Cordy expression represented by the string x
. This is the inverse operation of repr
. Note that eval
cannot reference any variables and cannot define any (unless inside an anonymous function). Raises an error if the string x
is not valid and evaluable Cordy code.
Example
>>> '1 + 2' . eval
3
Type Of typeof(x: any) -> any
[top]
Returns the type of the argument. This returns either the value, or a function representing the type for each individual input. For example, typeof(3)
will return the function int
, typeof([1, 2, 3])
will return the native function list
. The typeof
function has a few fundamental guarantees:
The return value of this function will always be comparable using
==
to distinguish different types. That is, iftypeof(x) == typeof(y)
, these objects are of the exact same underlying type.The expression
x is typeof(x)
will always betrue
, for any value ofx
.-
Note that
x is y
does not imply thattypeof(x) == y
. This assumption is broken by some types which are considered subtypes of another:bool
is a subtype ofint
, andint
is a subtype ofcomplex
.Collection types, and
str
are subtypes ofiterable
All types are a subtype of
any
Example
>>> typeof(nil)
nil
>>> typeof('hello')
str
>>> typeof([1, 2, 3])
list
>>> typeof(fn() -> nil)
function
Monitor monitor(cmd: str) -> any
[top]
Executes commands to monitor and inspect the state of the Cordy VM at runtime. cmd
must be one of the following values:
'stack'
: Returns a (copy of) the current program stack as alist<any>
. The returned list will not mutate the original stack, although mutable types on the stack can be modified and will reflect modifications done.'call-stack'
: Returns a view of the current program call stack as alist<(int, int)>
Each entry consists of a pair offrame_pointer
andreturn_ip
.'code'
: Returns a disassembly view of the current executing code as alist<str>
. This is not modifiable in any way.
Any other value of cmd
will cause a MonitorError
to be raised.
Example
>>> struct Box(value)
>>> let box = Box('hello') // lvt offset = 1
>>> box
Box(value='hello')
>>> let *_, (fp, _) = monitor 'call-stack' // access frame pointer of current call frame
>>> let sp = monitor 'stack' [fp + 1] // access stack to get pointer to the original 'box'
>>> sp->value = 'goodbye' // which we can mutate
goodbye
>>> box // and will reflect changes
Box(value='goodbye')
Len len(x: iterable) -> int
[top]
Returns the length of x
. For strings, this returns the number of Unicode Scalar Values. It is O(1)
except for str
, which is O(n)
.
Range range(...) -> list<int>
[top]
Possible signatures:
range(stop: int) -> list<int>
range(start: int, stop: int) -> list<int>
range(start: int, stop: int, step: int) -> list<int>
Returns a range of int
, from start
inclusive, to stop
exclusive, counting by step
. The default value of start
is 0
, and step
is 1 when not provided.
Note: this function is lazy, and will produce elements when iterated through, i.e. by calling list
.
Enumerate <A> enumerate(x: iterable<A>) -> list<vector<int, A>>
[top]
Returns a list
of pairs, of index and value of each element in the iterable x
.
Note: this function is lazy, and will produce elements when iterated through, i.e. by calling list
.
Example
>>> list(enumerate('hey'))
[(0, 'h'), (1, 'e'), (2, 'y')]
Sum sum(...) -> int
[top]
Possible signatures:
sum(it: iterable<int>) -> int
sum(int, ...) -> int
With one argument, returns the sum of each value in the iterable. With more than one argument, returns the sum of all the arguments. Raises an error when invoked with no arguments.
Min min(...) -> int
[top]
Possible signatures:
<T> min(it: iterable<T>) -> T
<T> min(T, ...) -> T
With one argument, returns the minimum of each value in the iterable. With more than one argument, returns the minimum of all the arguments. Raises an error when invoked with no arguments.
Note that the special case min(int)
or int.min
will return the lowest possible signed 63-bit integer representable.
Min By min_by(...) -> int
[top]
Possible signatures:
<A, B> min_by(key: fn(A) -> B, it: iterable<A>)
<A> min_by(cmp: fn(A, A) -> int, it: iterable<A>)
Returns either a minimum of it
by the key key
, or a minimum by the comparator function cmp
, depending on the number of arguments required by key
/ cmp
. Raises an error when it
is an empty iterable.
Max max(...) -> int
[top]
Possible signatures:
<T> max(it: iterable<T>) -> T
<T> max(T, ...) -> T
With one argument, returns the maximum of each value in the iterable. With more than one argument, returns the maximum of all the arguments. Raises an error when invoked with no arguments.
Note that the special case max(int)
or int.max
will return the highest possible signed 63-bit integer representable.
Max By max_by(...) -> int
[top]
Possible signatures:
<A, B> max_by(key: fn(A) -> B, it: iterable<A>)
<A> max_by(cmp: fn(A, A) -> int, it: iterable<A>)
Returns either a maximum of it
by the key function key
, or a minimum by the comparator function cmp
, depending on the number of arguments required by key
/ cmp
. Raises an error when it
is an empty iterable.
Map <A, B> map(f: fn(A) -> B, it: iterable<A>) -> list<B>
[top]
Applies the function f
to each value in the iterable it
, and returns the list of each result.
Example
>>> [1, 3, 5] . map(+1)
[2, 4, 6]
Filter <A> filter(f: fn(A) -> any, it: iterable<A>) -> list<A>
[top]
Applies the function f
to each value in the iterable it
, and retains that value if it returns a truthy value. Returns a list of all elements which returned a truthy value.
Example
>>> [-2, 4, -4, 2] . filter(>0)
[4, 2]
Flat Map <A, B> flat_map(f: fn(A) -> iterable<B>, it: iterable<A>) -> list<B>
[top]
Applies the function f
to each element in it
, and then concatenates the results. This is equivalent to . map(f) . concat
.
Example
>>> [1, 2, 3, 4] . flat_map(fn(i) -> range(i))
[0, 0, 1, 0, 1, 2, 0, 1, 2, 3]
Concat <A> concat(it: iterable<iterable<A>>) -> list<A>
[top]
Concatenates the iterables in the input into one list. This is equivalent to flat_map(fn(x) -> x)
, but should be preferred over that due to performance.
Example
>>> [[1, 2, 3], [4, 5, 6], [7, 8, 9]] . concat
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Zip <A> zip(...) -> list<A>
[top]
Possible signatures:
<A> zip(it: iterable<iterable<A>>) -> list<A>
<A> zip(iterable<A>, ...) -> list<A>
When invoked with a single argument, treats the argument as an iterable and each element as an individual argument. Then, iterates each iterable in parallel, returning a list of vectors until the shortest iterable is exhausted.
Example
>>> zip([1, 2, 3], [10, 20, 30])
[(1, 10), (2, 20), (3, 30)]
>>> zip(['hello', 'the', 'world'])
[('h', 't', 'w'), ('e', 'h', 'o'), ('e', 'e', 'r')]
Reduce <A> reduce(f: fn(A, A) -> A, it: iterable<A>) -> A
[top]
Reduces an iterable to a single value by successively applying f
on the first two elements in the iterable, until only one remains. Raises an error if the argument was an empty iterable
Example
>>> [1, 3, 5, 7] . reduce(+)
16
>>> ['hello', 'the', 'world'] . reduce(fn(a, b) -> a + ' ' + b)
'hello the world'
Sort <A> sort(it: iterable<A>) -> list<A>
[top]
Returns a list of the elements in it
, sorted in ascending order. Note that if it
contains multiple different types the returned order is unspecified as different types will compare as equal.
Example
>>> [1, 5, 3, 2, 4] . sort
[1, 2, 3, 4, 5]
Sort By <A> sort_by(...) -> list<A>
[top]
Possible signatures:
<A, B> sort_by(key: fn(A) -> B, it: iterable<A>) -> list<A>
<A> sort_by(cmp: fn(A, A) -> int, it: iterable<A>) -> list<A>
Returns the elements from it
in a sorted ascending order, either by the key function key
, or by the comparator function cmp
, depending on the number of arguments required by key
/ cmp
.
Group By <T, K> group_by(by: int | fn(T) -> K, it: iterable<T>) -> list<vector<T>> | dict<K, vector<T>>
[top]
Possible signatures:
<T> group_by(by: int, it: iterable<T>) -> list<vector<T>>
<T, K> group_by(by: fn(T) -> K, it: iterable<T>) -> dict<K, vector<T>>
When invoked with an int as the argument to by
, this will return a list of groups (vectors) of length by
from it
, until it is exhausted. If the length of it
does not perfectly divide by
, the last group will contain the remainder.
Example
>>> [1, 2, 3, 4, 5, 6] . group_by(2)
[(1, 2), (3, 4), (5, 6)]
>>> [1, 2, 3, 4, 5] . group_by(3)
[(1, 2, 3), (4, 5)]
When invoked with a function as the argument to by
, this will instead use the function on each element of the iterable as a key extractor. It will then create a dictionary mapping each key to its value.
Example
>>> [1, 2, 3, 4, 5] . group_by(%3)
{1: (1, 4), 2: (2, 5), 0: (3)}
>> [1, 2, 3, 4, 5] . group_by(fn(x) -> if x % 2 == 0 then 'even' else 'odd')
{'odd': (1, 3, 5), 'even': (2, 4)}
Reverse <A> reverse(it: iterable<A>) -> list<A>
[top]
Returns a list of the elements in it
, in reverse order.
Example
>>> [1, 3, 5, 7] . reverse
[7, 5, 3, 1]
Permutations <A> permutations(n: int, it: iterable<A>) -> list<vector<A>>
[top]
Returns a list of all permutations of n
elements from it
. If n
is larger than the length of it
, nothing will be returned. Raises an error if n
is negative.
Example
>>> [1, 2, 3] . permutations(2)
[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
Combinations <A> combinations(n: int, it: iterable<A>) -> list<vector<A>>
[top]
Returns a list of all combinations of n
elements from it
. If n
is larger than the length of it
, nothing will be returned. Raises an error if n
is negative.
Example
>>> [1, 2, 3] . combinations(2)
[(1, 2), (1, 3), (2, 3)]
Any <A> any(f: fn(A) -> bool, it: iterable<A>) -> bool
[top]
Returns true
if any of the values in it
return true
to the function f
. This is lazy and only evaluates as many elements in it
as needed.
>>> [-1, -6, 3, -2] . any(>0)
true
All <A> all(f: fn(A) -> bool, it: iterable<A>) -> bool
[top]
Returns true
if all the values in it
return true
to the function f
. This is lazy and only evaluates as many elements in it
as needed.
Example
>>> [1, 6, 3, 2] . all(>0)
true
Memoize <A> memoize(f: fn(...) -> A) -> fn(...) -> A
[top]
This creates a memorizing wrapper around a function. The returned function will cache all values based on the input parameters. The return value is invoked identically to the provided function.
Example
>>> fn add(x, y) {
... print('add was called')
... x + y
... }
>>> let cached_add = memoize(add)
>>> add(1, 2)
add was called
3
>>> add(1, 2)
3
Pop <A> pop(it: iterable<A>) -> A
[top]
Pops a value from a collection. For list
, this will be a value at the back of the collection. For a heap
, this is the top of the heap, i.e. the minimum value. For a dict
, this will return a key-value pair.
Pop Front <A> pop_front(it: list<A>) -> A
[top]
Pops a value from the front of a list.
Push <A> push(x: A, it: list<A> | set<A> | heap<A>) -> iterable<A>
[top]
Pushes a value x
into a collection it
. For list
, this will be a value at the back of the collection. Returns the collection.
Push Front <A> push_front(x: A, it: list<A>) -> list<A>
[top]
Pushes a value x
into the front of a list. Returns the list.
Insert insert(...)
[top]
Possible signatures:
<A> insert(index: int, x: A, it: list<A>) -> list<A>
<K, V> insert(key: K, value: V, it: dict<K, V>) -> dict<K, V>
Inserts a value x
into a collection it
, either by key, or by value. For list
, will return an error if the index is out of bounds of the list. Returns the collection.
Remove remove(...)
[top]
Possible signatures:
<A> remove(index: int, it: list<A>) -> A
<A> remove(value: A, it: set<A>) -> bool
<K, V> remove(key: K, it: dict<K, V>) -> bool
Removes a value from a collection it
, with the behavior differing by collection. For list
, this removes a value by index. For set
, this will remove by value, and return true
if the value was present. For dict
, this will remove an entry by key, and return true
if the key was removed.
Clear clear(it: iterable) -> iterable
[top]
Clears the contents of a collection. Returns the collection.
Find <A> find(x: A | fn(A) -> bool, it: iterable<A>) -> A
[top]
If x
is a function, this will find the first value from the left in it
where a value returns true
to the function. If x
is a value, it will return the first value from the left in it
where a value is equal to x
.
find(x)
is equivalent to find(==x)
if x
is not a function.
Returns nil
if the value was not found.
Example
>>> [1, 2, 3, 4, 5] . find(4)
4
>>> [1, 2, 3, 4, 5] . find(fn(i) -> i % 3 == 0)
3
Right Find <A> rfind(x: A | fn(A) -> bool, it: iterable<A>) -> A
[top]
If x
is a function, this will find the first value from the right in it
where a value returns true
to the function. If x
is a value, it will return the first value from the right in it
where a value is equal to x
.
rfind(x)
is equivalent to rfind(==x)
if x
is not a function.
Returns nil
if the value was not found.
Example
>>> [1, 2, 3, 4, 5] . rfind(4)
4
>>> [1, 2, 3, 4, 5] . rfind(fn(i) -> i % 3 == 0)
3
Index Of <A> index_of(x: A | fn(A) -> bool, it: iterable<A>) -> int
[top]
Like find
, but for an indexable collection, returns the index where the value was found, not the value itself.
Right Index Of <A> rindex_of(x: A | fn(A) -> bool, it: iterable<A>) -> int
[top]
Like rfind
, but for an indexable collection, returns the index where the value was found, not the value itself.
(Int) Abs abs(x: int) -> int
[top]
Returns the absolute value of x
.
(Int) Sqrt sqrt(x: int) -> int
[top]
Returns the positive integer square root of x
, or the largest y
such that y*y <= x
.
(Int) Count Ones count_ones(x: int) -> int
[top]
Returns the number of ones in the 63-bit, signed, binary representation of x
(Int) Count Zeros count_zeros(x: int) -> int
[top]
Returns the number of zeros in the 63-bit, signed, binary representation of x
(Complex) Real real(x: complex) -> int
[top]
With a complex-like argument, returns the real part. For bool
and int
, this is the same as invoking int
. For complex
, this will return the real component as an integer.
Example
>>> true . real
1
>>> 5 . real
5
>>> 7 + 13i . real
7
(Complex) Imag imag(x: complex) -> int
[top]
With a complex-like argument, returns the imaginary part. For bool
and int
, this will always return 0
. For complex
, this will return the imaginary component as an integer.
Example
>>> true . imag
0
>>> 5 . imag
0
>>> 7 + 13i . imag
13
(Rational) Numer numer(x: rational) -> rational
[top]
Returns the numerator of a rational number as another rational. Note that this will return a rational
type, as it can express larger numbers than an int
Examples
>>> numer(rational 1)
1 / 1
>>> numer(rational(6, 7))
6 / 1
(Rational) Denom denom(x: rational) -> rational
[top]
Returns the denominator of a rational number as another rational. Note that this will return a rational
type, as it can express larger numbers than an int
Examples
>>> denom(rational 1)
1 / 1
>>> denom(rational(6, 7))
7 / 1
Lcm lcm(...) -> int
[top]
Possible signatures:
lcm(it: iterable<int>) -> int
lcm(int, ...) -> int
With one argument, returns the least common multiple of each value in the iterable. With more than one argument, returns the least common multiple of all the arguments. Raises an error when invoked with no arguments.
Gcd gcd(...) -> int
[top]
Possible signatures:
gcd(it: iterable<int>) -> int
gcd(int, ...) -> int
With one argument, returns the greatest common divisor of each value in the iterable. With more than one argument, returns the greatest common divisor of all the arguments. Raises an error when invoked with no arguments.
(Str) Split split(pattern: str, x: str) -> list<str>
[top]
Splits a string x
based on pattern
, with regular expression (regex) support. If pattern
is an empty string, this functions identical to list
applied to a string, and returns a list of all characters in the string. Otherwise, it will treat pattern
like a regex, and split the string on sequences matching the regex.
Regex syntax is the same as used by the replace
, and search
functions.
Example
>>> 'abc' . split('')
['a', 'b', 'c']
>>> 'hello the world' . split(' ')
['hello', 'the', 'world']
>>> ' hello \t the \n\n world !' . trim . split('\s+')
['hello', 'the', 'world', '!']
(Str) Join join(joiner: str, iter: iterable<any>) -> str
[top]
Joins an iterable into a single string. First calls str()
on any arguments, and joins them separated by joiner
.
Example
>>> [1, 2, 3, 4, 5] . join(' + ')
'1 + 2 + 3 + 4 + 5'
>>> 'hello' . join(' ')
'h e l l o'
This is a native optimized form of the below usage of reduce
:
>>> reduce(fn(x, y) -> str(x) + joiner + str(y), iter)
(Str) Replace replace(...) -> str
[top]
Possible Signatures
replace(pattern: str, replacer: str, x: str)
replace(pattern: str, replacer: fn(vector<str>) -> str, x: str)
Performs string replacement, with regular expression (regex) support. Regex syntax is the same as used by the Fancy Regex crate. Additionally, \r
, \n
, and \t
sequences are supported as part of a regex, and they will be replaced in the pattern with \\r
, \\n
, and \\t
respectively.
When replacer
is a string, this will replace all instances of pattern
in the string x
with replacer
. When replacer
is a function with one defined argument, this will invoke that function for each replacement to be made, to provide the result. This takes an argument of the match found.
A match will either consist of the matched string if the regex did not contain any (
groups )
, or a vector containing the full match, plus any numbered capture groups.
Note that capture groups can also be referenced in a string via $<number>
syntax, where $0
represents the entire match.
Examples
>>> 'bob and alice' . replace('and', 'or')
'bob or alice'
>>> 'bob and alice' . replace('\sa', ' Ba')
'bob Band Balice'
>>> 'bob and alice' . replace('[a-z]+', '$0!')
'bob! and! alice!'
>>> 'bob and alice' . replace('[a-z]+', fn(g) -> g . reverse . reduce(+))
'bob dna ecila'
>>> 'bob and alice' . replace('([a-z])([a-z]+)', fn((_, g1, g2)) -> to_upper(g1) + g2)
'Bob And Alice'
(Str) Search search(pattern: str, x: str) -> list<vector<str>>
[top]
Matches a string x
against a given pattern
, and returns a list of all results. The pattern is a regular expression (regex), with syntax identical to using replace
. When invoked, this returns a list of all matches in the string, or an empty list if no matches are found.
A match will either consist of the matched string if the regex did not contain any (
groups )
, or a vector containing the full match, plus any numbered capture groups.
Note, for simple substring searching, it is sufficient to test for truthiness, as no match will return an empty list. Using characters such as ^
and $
in the regex will also ensure that only one match is possible, and so can only return a list with at most one element.
Examples
>>> 'bob and alice' . search('bob')
['bob']
>>> 'bob and alice' . search('a[a-z]+')
['and', 'alice']
>>> 'bob and alice' . search('([a-z])[^ ]+([a-z])')
[('bob', 'b', 'b'), ('and', 'a', 'd'), ('alice', 'a', 'e')]
Trim trim(x: str) -> str
[top]
Returns a string with whitespace trimmed from it.
Ord ord(x: str) -> int
[top]
When called with a string containing exactly one character (unicode scalar value), returns the integer representing the unicode character.
Examples
>>> ord('a')
97
>>> ord('A')
65
Char char(x: int) -> str
[top]
Converts an integer to its unicode character representation, as a single-character string. If the integer represents an invalid character, an error will be raised instead.
Examples
>>> char(97)
'a'
>>> char(65)
'A'
Hex hex(x: str | int) -> str | int
[top]
When provided with a hexidecimal string, converts the string into an int
representing the same value. When provided with an int
, converts the integer into a hexidecimal string representing the same value.
Note that this will omit any leading 0x
or #
prefix, and does not expect one to be present when provided a string.
Examples
>>> hex 'ff'
255
>>> hex 255
'ff'
Bin bin(x: str | int) -> str | int
[top]
When provided with a binary string, converts the string into an int
representing the same value. When provided with an int
, converts the integer into a binary string representing the same value.
Note that this will omit any leading 0b
prefix, and does not expect one to be present when provided a string.
Examples
>>> bin 7
'111'
>>> bin '111'
7
(Set) Union <T> union(other: iterable<T>, this: set<T>) -> set<T>
[top]
Computes a union of this
and other
, mutating this
. This is functionally similar to this |= set(other)
, except this will directly mutate this
, which can be desirable for performance reasons with large sets. This will return this
.
Example
>>> let x = {1, 2, 3}
>>> x . union({3, 4})
{1, 2, 3, 4}
>>> x
{1, 2, 3, 4}
(Set) Intersect <T> intersect(other: iterable<T>, this: set<T>) -> set<T>
[top]
Computes an intersection of this
and other
, mutating this
. This is functionally similar to this &= set(other)
, except this will directly mutate this
, which can be desirable for performance reasons with large sets. This will return this
.
Example
>>> let x = {1, 2, 3}
>>> x . intersect({2, 3, 4})
{2, 3}
>>> x
{2, 3}
(Set) Difference <T> difference(other: iterable<T>, this: set<T>) -> set<T>
[top]
Computes a (non-symmetric) difference of this
and other
, mutating this
. This is functionally similar to this -= set(other)
, except this will directly mutate this
, which can be desirable for performance reasons with large sets. This will return this
.
Example
>>> let x = {1, 2, 3}
>>> x . difference({3, 4})
{1, 2}
>>> x
{1, 2}
Counter <T> counter(it: iterable<T>) -> dict<T, int>
[top]
Consumes the provided iterable in O(n) time, and produces a dictionary of elements of that iterable, to their number of occurrences in the iterable. The dictionary will be ordered as per the order the elements are first encountered in the iterable.
Example
>>> 'hello world' . counter
{'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}
Using counter
is equivalently implemented by the following Cordy function:
fn counter(it) {
let d = dict()
for e in it {
if e not in d {
d[e] = 1
} else {
d[e] += 1
}
}
d
}
Copy <T> copy(it: T) -> T
[top]
Computes a deep copy of a given value. This has a few additional notes:
Recursive and self-referential structures are copied correctly, producing identical references
Any mutable collections will be recursively copied, so mutations to the copy will never affect the original
It will always be true that
copy a == a
for any value ofa
(Dict) Default <K, V> default(x: V, it: dict<K, V>) -> dict<K, V>
[top]
Sets the default value of it
to x
, and then returns it
. This means that any future queries into it
via the index syntax, if the key is not in the dictionary, will return x
.
Note: If x
is a mutable value, such as a list, the same instance will be returned from each access to the default value.
Example
let d = dict()
d['hello'] // will raise an error
d . default('nope')
d['hello'] // returns 'nope'
(Dict) Keys <K, V> keys(it: dict<K, V>) -> set<K>
[top]
Returns a set of all keys in it
, maintaining insertion order.
(Dict) Values <K, V> values(it: dict<K, V>) -> list<V>
[top]
Returns a list of all values in it
, maintaining insertion order.