With LispBM comes a small library of dynamically loadable functions and macros. These functions occupy flash memory (if on an embedded platform) but only use heap if they are actually used by the application. Providing dynamically loadable operations is a way to save space while offering more sweet functionality.
LispBM can be built with varying amount of included dynamic loadable functionality. It is up to the LispBM integrator to decide what is included. As an integrator you can also decide to roll your entirely own set of dynamically loadable operations.
The inclusion of dynamically loadable functionality from this library is determined when LispBM is compiled using the following flags:
- LBM_USE_DYN_FUNS : Add a library of functions to the dynamic loader.
- LBM_USE_DYN_MACROS : Add a library of macros to the dynamic loader.
- LBM_USE_DYN_DEFSTRUCT : Add the defstruct mechanism, requires LBM_USE_DYN_FUNS and LBM_USE_DYN_MACROS.
- LBM_USE_DYN_LOOPS : Add loop macros, requires LBM_USE_DYN_MACROS.
- LBM_USE_DYN_ARRAYS : Add functions on arrays. Requires LBM_USE_DYN_MACROS and LBM_USE_DYN_LOOPS.
The flags should be given to the compiler as -Dx for example -DLBM_USE_DYN_FUNS.
Compute the absolute value
Example | Result |
(abs -1) |
1 |
(abs -1.000000f32) |
1.000000f32 |
(abs 1) |
1 |
(abs 1.000000f32) |
1.000000f32 |
(abs (sin -3.140000f32)) |
0.001593f32 |
Apply a function taking n arguments to a list of n elements
Example | Result |
(apply + (list 1 2 3)) |
6 |
Filter a list from unwanted elements as decided by a predicate
Example | Result |
(define even (lambda (x) (= (mod x 2) 0))) |
(closure (x) (= (mod x 2) 0) nil) |
(filter even (list 1 2 3 4 5 6 7 8 9)) |
(2 4 6 8) |
foldl
walks through a list, left to right, while accumulating a result from applying a function to the element at hand and the result of its previous step. foldl takes an initial value used as the that is combined with the leftmost value of the list in the first iteration.
Example | Result |
(foldl + 0 (list 1 2 3 4 5 6 7 8 9 10)) |
55 |
foldl
has an advantage over foldr
in that it is implemented in a tail-recursive and constant-storage way.
Funnily enough, foldl
using function cons
and initial value nil
converts a list to a snoc-list.
Example | Result |
(foldl cons nil (list 1 2 3 4 5 6 7 8 9 10)) |
((((((((((nil . 1) . 2) . 3) . 4) . 5) . 6) . 7) . 8) . 9) . 10) |
Now we are going off on a tangent, but car
and cdr
switches roles with each other when operating on a snoc-list.
Example | Result |
(define my-snoc-list (foldl cons nil (list 1 2 3 4 5 6 7 8 9 10))) |
((((((((((nil . 1) . 2) . 3) . 4) . 5) . 6) . 7) . 8) . 9) . 10) |
(cdr my-snoc-list) |
10 |
(car my-snoc-list) |
(((((((((nil . 1) . 2) . 3) . 4) . 5) . 6) . 7) . 8) . 9) |
foldr
walks through a list, right to left, while combining value and prev result step by step. An initial value is provided and here used in the first, rightmost, operation.
foldr
has a disadvantage compared to foldl
as I don't think it is possible to give foldr
a constant-space and tail-recursive implementation. One can make foldr
tail-recursive by writing it in continuation passing style (CPS). The CPS version of foldr will run without building upon the return stack, but instead it will construct and expand upon a continuation object each iteration. This continuation object will grow at the same rate as the call-stack otherwise would but it would grow in heap usage.
Example | Result |
(foldr + 0 (list 1 2 3 4 5 6 7 8 9 10)) |
55 |
Much less amusingly compared to foldl
, foldr
of cons
with initial value nil
is the identity function on proper lists.
Example | Result |
(foldr cons nil (list 1 2 3 4 5 6 7 8 9 10)) |
(1 2 3 4 5 6 7 8 9 10) |
iota
takes one number as argument and generates a list of values up to (not including) the given number.
Example | Result |
(iota 4) |
(0 1 2 3) |
second
extracts the second element from a list.
Example | Result |
(second (list 1 2 3 4)) |
2 |
(second (list 1)) |
nil |
compare strings according to alphabetical order ascending.
Example | Result |
(str-cmp-asc "apa" "bepa") |
t |
(str-cmp-asc "bepa" "apa") |
nil |
compare strings according to alphabetical order descending.
Example | Result |
(str-cmp-dsc "apa" "bepa") |
nil |
(str-cmp-dsc "bepa" "apa") |
t |
str-merge
is an alternative name for the str-join
operations. It is kept around for backwards compatibility.
Example | Result |
(str-merge "Kurt" " " "Russel") |
Kurt Russel |
third
extracts the third element from a list.
Example | Result |
(third (list 1 2 3 4)) |
3 |
(third (list 1)) |
nil |
defun
is a macro that provides a shorthand form for defining a named function. (defun name args body)
is expanded into (define name (lambda args body))
.
Example | Result |
(defun f (x) (+ x 1)) |
(closure (x) (+ x 1) nil) |
(f 1) |
2 |
defunret
is like defun
but you are allowed to return
at any point in the function body.
Example | Result |
(defunret g (x) (progn
1
2
(return 55)
3
4
x)) |
(closure (x) (call-cc-unsafe (lambda (return) (progn 1 2 (return 55) 3 4 x))) nil) |
(g 10) |
55 |
defmacro
is a macro that provides a shorthand form for defining macros. (defmacro name args body)
expands into (define name (macro args body))
.
Example | Result |
(defmacro my-macro (a) `(list 'apa ,a)) |
(macro (a) (append (quote (list)) (list (quote (quote apa))) (list a))) |
(my-macro 10) |
(apa 10) |
loopfor
has the form (loopfor it start cond update body)
and implements a for loop as familiar from for example C.
it
is the iterator, start
is what it is initialized to, cond
is the condition that has the be true for the loop to continue running, update
is how to update the iterator after each iteration and body is the code to execute each iteration. The iterator can be accessed from within body.
Example | Result |
(define r 0)
(loopfor i 0 (< i 5) (+ i 1)
(setq r (+ r i)))
r |
10 |
loopwhile
has the form (loopwhile cond body)
and implements a while loop as familiar from for example C.
cond
is the condition that has the be true for the loop to continue running and body
is the code to execute each iteration.
Example | Result |
(define a 0)
(loopwhile (< a 10)
(setq a (+ a 1)))
a |
10 |
looprange
has the form (looprange it start end body)
and implements a loop over a range similar to python's for i in range(n)
.
Iterate it
from start
to end
and evaluate body
for each iteration. The iterator it can be accessed from within body.
Example | Result |
(define b 0)
(looprange i 0 11 (setq b (+ b i)))
b |
55 |
Example | Result |
(define my-ls nil)
(looprange i 0 5 (setq my-ls (cons i my-ls)))
my-ls |
(4 3 2 1 0) |
loopforeach
has the form (loopforeach it lst body)
and implements a loop over the elements of a list similar to python's for e in ...
.
Iterate over every element in the list lst
and evaluate body
for each iteration. The iterator it
can be accessed from within body.
Example | Result |
(define m 0)
(loopforeach e (list 2 4 6 8) (setq m (+ m e)))
m |
20 |
loopwhile-thd
is like loopwhile
but the thread runs in its own thread asynchronously. The form of a loopwhile-thd
is (loopwhile-thd stack cond body)
.
A While-loop that starts in a new thread. The argument stack
is the stack-size of the thread, cond
is the condition that has the be true for the loop to continue running and body
is the code to execute each iteration. The difference from the regular loopwhile is that the evaluator will continue running the code after this one before this one finishes, as this loop is evaluated in a new thread.
The following examples assumes that your LispBM integration has a way to print
.
Example that forever prints "Hello World" every two seconds:
(loopwhile-thd 100 t {
(print "Hello World")
(sleep 2)
})
The above is equivalent to the following code
(spawn 100 (fn () (loopwhile t {
(print "Hello World")
(sleep 2)
})))
It is possible to give the thread a name and/or a stack size. That gives the following combinations of possibilities:
No name and default stack size
(loopwhile-thd () t {
(print "Hello World1")
(sleep 2)
})
No name and stack size 100
(loopwhile-thd 100 t {
(print "Hello World2")
(sleep 2)
})
Name ThdTest and default stack size
(loopwhile-thd "ThdTest" t {
(print "Hello World3"
) (sleep 2)
})
Name ThdTest2 and stack size 100
(loopwhile-thd ("ThdTest2" 100) t {
(print "Hello World4")
(sleep 2)
})
Convert a list to an array
Example | Result |
(list-to-array (list 1 2 3)) |
[|1 2 3|] |
(list-to-array '(nil nil nil)) |
[|nil nil nil|] |
Convert an array to a list
Example | Result |
(array-to-list (list-to-array (list 1 2 3))) |
(1 2 3) |
(array-to-list [|1 2 3 4|]) |
(1 2 3 4) |
Array predicate is true for arrays.
Example | Result |
(array? [|1 2 3|]) |
t |
(array? 1) |
nil |
(array? 'apa) |
nil |
(array? (list 1 2 3)) |
nil |
defstruct
defines a datastructure with named fields similar to a struct
in C. defstruct
takes two arguments, a struct name and a list of fields (defstruct name list-of-fields)
.
Structs are implemented as arrays of lisp values and offer constant time lookup of each of its fields. The struct itself does not occupy heap cells, but the values stored in the fields may.
As structs are allocated from array memory (lbm_memory), there is a potential for causing memory fragmentation.
The example below creates a structure type called my-struct with three fields called a
, b
and c
.
Example | Result |
(defstruct my-struct (a b c)) |
t |
Now instances of my-struct
can be creted using make-my-struct
.
Example | Result |
(define s1 (make-my-struct)) |
[|my-struct nil nil nil|] |
when a struct is defined using defstruct
a number of functions for manipulation of that kind of struct is automatically generated.
- make-name : function for creation of struct with name
name
. - name? : predicate that is true for instances of the struct named
name
. - name-x : setter/getter for struct
name
and fieldx
.
This will be more clear by showing with my-struct
as example.
Example | Result |
(my-struct? s1) |
t |
(my-struct? "hej") |
nil |
(my-struct? 10) |
nil |
my-struct?
is a predicate that is true for instances of my.struct
.
Example | Result |
(my-struct-a s1 10) |
[|my-struct 10 nil nil|] |
(my-struct-b s1 20) |
[|my-struct 10 20 nil|] |
(my-struct-c s1 30) |
[|my-struct 10 20 30|] |
my-struct-x
with 2 arguments is a setter. with just the struct instance as argument it is a getter.
Example | Result |
(my-struct-a s1) |
10 |
(my-struct-b s1) |
20 |
(my-struct-c s1) |
30 |
This document was generated by LispBM version 0.30.3