|
|
@ -136,7 +136,7 @@ fn empty? {
|
|
|
|
"Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)."
|
|
|
|
"Returns true if something is empty. Otherwise returns false (including for things that can't logically be empty, like numbers)."
|
|
|
|
([]) -> true
|
|
|
|
([]) -> true
|
|
|
|
(#{}) -> true
|
|
|
|
(#{}) -> true
|
|
|
|
(s as :set) -> eq (s, ${})
|
|
|
|
(s as :set) -> eq? (s, ${})
|
|
|
|
(()) -> true
|
|
|
|
(()) -> true
|
|
|
|
("") -> true
|
|
|
|
("") -> true
|
|
|
|
(_) -> false
|
|
|
|
(_) -> false
|
|
|
@ -165,6 +165,8 @@ fn set? {
|
|
|
|
(_) -> false
|
|
|
|
(_) -> false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
& add a contains? or has? function
|
|
|
|
|
|
|
|
|
|
|
|
fn fold {
|
|
|
|
fn fold {
|
|
|
|
"Folds a list."
|
|
|
|
"Folds a list."
|
|
|
|
(f as :fn, xs as :list) -> fold (f, xs, f ())
|
|
|
|
(f as :fn, xs as :list) -> fold (f, xs, f ())
|
|
|
@ -179,7 +181,7 @@ fn fold {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn map {
|
|
|
|
fn map {
|
|
|
|
"Maps over a list."
|
|
|
|
"Maps a function over a list: returns a new list with elements that are the result of applying the function to each element in the original list. E.g., `map ([1, 2, 3], inc) &=> [2, 3, 4]`."
|
|
|
|
(f as :fn, xs) -> {
|
|
|
|
(f as :fn, xs) -> {
|
|
|
|
fn mapper (prev, curr) -> append (prev, f (curr))
|
|
|
|
fn mapper (prev, curr) -> append (prev, f (curr))
|
|
|
|
fold (mapper, xs, [])
|
|
|
|
fold (mapper, xs, [])
|
|
|
@ -191,7 +193,7 @@ fn map {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn filter {
|
|
|
|
fn filter {
|
|
|
|
"Takes a list and a predicate function, and returns a new list with only the items that produce truthy values when the function is called on them."
|
|
|
|
"Takes a list and a predicate function, and returns a new list with only the items that produce truthy values when the function is called on them. E.g., `filter ([1, 2, 3, 4], odd?) &=> [1, 3]`."
|
|
|
|
(p? as :fn, xs) -> {
|
|
|
|
(p? as :fn, xs) -> {
|
|
|
|
fn filterer (filtered, x) -> if p? (x)
|
|
|
|
fn filterer (filtered, x) -> if p? (x)
|
|
|
|
then append (filtered, x)
|
|
|
|
then append (filtered, x)
|
|
|
@ -375,7 +377,7 @@ fn mult {
|
|
|
|
() -> 1
|
|
|
|
() -> 1
|
|
|
|
(x as :number) -> x
|
|
|
|
(x as :number) -> x
|
|
|
|
(x as :number, y as :number) -> base :mult (x, y)
|
|
|
|
(x as :number, y as :number) -> base :mult (x, y)
|
|
|
|
(x, y, ...zs) -> fold (base :mult, mult (x, y), zs)
|
|
|
|
(x, y, ...zs) -> fold (base :mult, zs, mult (x, y))
|
|
|
|
(scalar as :number, (x, y)) -> (mult (x, scalar), mult (y, scalar))
|
|
|
|
(scalar as :number, (x, y)) -> (mult (x, scalar), mult (y, scalar))
|
|
|
|
((x, y), scalar as :number) -> mult (scalar, (x, y))
|
|
|
|
((x, y), scalar as :number) -> mult (scalar, (x, y))
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -413,10 +415,20 @@ fn div/safe {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn inv {
|
|
|
|
|
|
|
|
"Returns the inverse of a number: 1/n or `div (1, n)`. Panics on division by zero."
|
|
|
|
|
|
|
|
(x as :number) -> div (1, x)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn inv/0 {
|
|
|
|
|
|
|
|
"Returns the inverse of a number: 1/n or `div/0 (1, n)`. Returns 0 on division by zero."
|
|
|
|
|
|
|
|
(x as :number) -> div/0 (1, x)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn abs {
|
|
|
|
fn abs {
|
|
|
|
"Returns the absolute value of a number."
|
|
|
|
"Returns the absolute value of a number."
|
|
|
|
(0) -> 0
|
|
|
|
(0) -> 0
|
|
|
|
(n) -> if neg? (n) then mult (-1, n) else n
|
|
|
|
(n as :number) -> if neg? (n) then mult (-1, n) else n
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn neg {
|
|
|
|
fn neg {
|
|
|
@ -483,6 +495,14 @@ fn lte? {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn between? {
|
|
|
|
|
|
|
|
"Returns true if a number is in the range [lower, higher): greater than or equal to the lower number, less than the higher."
|
|
|
|
|
|
|
|
(lower as :number, higher as :number, x as :number) -> and (
|
|
|
|
|
|
|
|
gte? (x, lower)
|
|
|
|
|
|
|
|
lt? (x, higher)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn neg? {
|
|
|
|
fn neg? {
|
|
|
|
"Returns true if a value is a negative number, otherwise returns false."
|
|
|
|
"Returns true if a value is a negative number, otherwise returns false."
|
|
|
|
(x as :number) if lt? (x, 0) -> true
|
|
|
|
(x as :number) if lt? (x, 0) -> true
|
|
|
@ -497,16 +517,30 @@ fn pos? {
|
|
|
|
|
|
|
|
|
|
|
|
fn even? {
|
|
|
|
fn even? {
|
|
|
|
"Returns true if a value is an even number, otherwise returns false."
|
|
|
|
"Returns true if a value is an even number, otherwise returns false."
|
|
|
|
(x as :number) if eq (0, mod (x, 2)) -> true
|
|
|
|
(x as :number) if eq? (0, mod (x, 2)) -> true
|
|
|
|
(_) -> false
|
|
|
|
(_) -> false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn odd? {
|
|
|
|
fn odd? {
|
|
|
|
"Returns true if a value is an odd number, otherwise returns false."
|
|
|
|
"Returns true if a value is an odd number, otherwise returns false."
|
|
|
|
(x as :number) if eq (1, mod (x, 2)) -> true
|
|
|
|
(x as :number) if eq? (1, mod (x, 2)) -> true
|
|
|
|
(_) -> false
|
|
|
|
(_) -> false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn min {
|
|
|
|
|
|
|
|
"Returns the number in its arguments that is closest to negative infinity."
|
|
|
|
|
|
|
|
(x as :number) -> x
|
|
|
|
|
|
|
|
(x as :number, y as :number) -> if lt? (x, y) then x else y
|
|
|
|
|
|
|
|
(x, y, ...zs) -> fold (min, zs, min (x, y))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn max {
|
|
|
|
|
|
|
|
"Returns the number in its arguments that is closest to positive infinity."
|
|
|
|
|
|
|
|
(x as :number) -> x
|
|
|
|
|
|
|
|
(x as :number, y as :number) -> if gt? (x, y) then x else y
|
|
|
|
|
|
|
|
(x, y, ...zs) -> fold (max, zs, max (x, y))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&&& keywords: funny names
|
|
|
|
&&& keywords: funny names
|
|
|
|
fn keyword? {
|
|
|
|
fn keyword? {
|
|
|
|
"Returns true if a value is a keyword, otherwise returns false."
|
|
|
|
"Returns true if a value is a keyword, otherwise returns false."
|
|
|
@ -590,6 +624,7 @@ fn or {
|
|
|
|
|
|
|
|
|
|
|
|
&&& associative collections: dicts, structs, namespaces
|
|
|
|
&&& associative collections: dicts, structs, namespaces
|
|
|
|
& TODO?: get_in, update_in, merge
|
|
|
|
& TODO?: get_in, update_in, merge
|
|
|
|
|
|
|
|
& TODO?: consider renaming these: put & take, not assoc/dissoc
|
|
|
|
fn assoc {
|
|
|
|
fn assoc {
|
|
|
|
"Takes a dict, key, and value, and returns a new dict with the key set to value."
|
|
|
|
"Takes a dict, key, and value, and returns a new dict with the key set to value."
|
|
|
|
() -> #{}
|
|
|
|
() -> #{}
|
|
|
@ -677,6 +712,7 @@ fn assoc? {
|
|
|
|
(_) -> false
|
|
|
|
(_) -> false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
& TODO: consider merging `get` and `at`
|
|
|
|
fn get {
|
|
|
|
fn get {
|
|
|
|
"Takes a dict or struct, key, and optional default value; returns the value at key. If the value is not found, returns nil or the default value. Returns nil or default if the first argument is not a dict or struct."
|
|
|
|
"Takes a dict or struct, key, and optional default value; returns the value at key. If the value is not found, returns nil or the default value. Returns nil or default if the first argument is not a dict or struct."
|
|
|
|
(key as :keyword) -> get (key, _)
|
|
|
|
(key as :keyword) -> get (key, _)
|
|
|
@ -684,6 +720,7 @@ fn get {
|
|
|
|
(key as :keyword, coll, default) -> base :get (key, coll, default)
|
|
|
|
(key as :keyword, coll, default) -> base :get (key, coll, default)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
& TODO: add sets to this?
|
|
|
|
fn has? {
|
|
|
|
fn has? {
|
|
|
|
"Takes a key and a dict, and returns true if there is a non-`nil` value stored at the key."
|
|
|
|
"Takes a key and a dict, and returns true if there is a non-`nil` value stored at the key."
|
|
|
|
(key as :keyword) -> has? (key, _)
|
|
|
|
(key as :keyword) -> has? (key, _)
|
|
|
@ -691,8 +728,7 @@ fn has? {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn dict {
|
|
|
|
fn dict {
|
|
|
|
"Takes a struct or ns, and returns it as a dict. Or, takes a list or tuple of (key, value) tuples and returns it as a dict. Returns dicts unharmed."
|
|
|
|
"Takes an ns, and returns it as a dict. Or, takes a list or tuple of (key, value) tuples and returns it as a dict. Returns dicts unharmed."
|
|
|
|
(struct as :struct) -> base :to_dict (struct)
|
|
|
|
|
|
|
|
(ns_ as :ns) -> base :to_dict (ns_)
|
|
|
|
(ns_ as :ns) -> base :to_dict (ns_)
|
|
|
|
(dict as :dict) -> dict
|
|
|
|
(dict as :dict) -> dict
|
|
|
|
(list as :list) -> fold (assoc, list)
|
|
|
|
(list as :list) -> fold (assoc, list)
|
|
|
@ -829,12 +865,25 @@ fn dist {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&&& more number functions
|
|
|
|
&&& more number functions
|
|
|
|
& TODO: add max, min
|
|
|
|
|
|
|
|
fn random {
|
|
|
|
fn random {
|
|
|
|
"Returns a random number. With zero arguments, returns a random number between 0 (inclusive) and 1 (exclusive). With one argument, returns a random number between 0 and n. With two arguments, returns a random number between m and n."
|
|
|
|
"Returns a random number. With zero arguments, returns a random number between 0 (inclusive) and 1 (exclusive). With one argument, returns a random number between 0 and n. With two arguments, returns a random number between m and n. Alternately, given a list, it returns a random member of that list."
|
|
|
|
() -> base :random ()
|
|
|
|
() -> base :random ()
|
|
|
|
(n as :number) -> base :random (n)
|
|
|
|
(n as :number) -> base :random (n)
|
|
|
|
(m as :number, n as :number) -> add (m, random (n))
|
|
|
|
(m as :number, n as :number) -> add (m, random (n))
|
|
|
|
|
|
|
|
(l as :list) -> {
|
|
|
|
|
|
|
|
let i = do l > count > random > floor
|
|
|
|
|
|
|
|
at (l, i)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
(d as :dict) -> {
|
|
|
|
|
|
|
|
let key = do d > keys > random
|
|
|
|
|
|
|
|
get (d, key)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn random_int {
|
|
|
|
|
|
|
|
"Returns a random integer. With one argument, returns a random integer between 0 and that number. With two arguments, returns a random integer between them."
|
|
|
|
|
|
|
|
(n as :number) -> do n > random > floor
|
|
|
|
|
|
|
|
(m as :number, n as :number) -> floor (random (m, n))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn floor {
|
|
|
|
fn floor {
|
|
|
@ -853,7 +902,7 @@ fn round {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn range {
|
|
|
|
fn range {
|
|
|
|
"Returns the set of integers between start (inclusive) and end (exclusive) as a list. With one argument, starts at 0. If end is less than start, returns an empty list."
|
|
|
|
"Returns the set of integers between start (inclusive) and end (exclusive) as a list: [start, end). With one argument, starts at 0. If end is less than start, returns an empty list."
|
|
|
|
(end as :number) -> base :range (0, end)
|
|
|
|
(end as :number) -> base :range (0, end)
|
|
|
|
(start as :number, end as :number) -> base :range (start, end)
|
|
|
|
(start as :number, end as :number) -> base :range (start, end)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -906,15 +955,25 @@ fn assert! {
|
|
|
|
&&& Turtle & other graphics
|
|
|
|
&&& Turtle & other graphics
|
|
|
|
|
|
|
|
|
|
|
|
& some basic colors
|
|
|
|
& some basic colors
|
|
|
|
&&& TODO: add colors
|
|
|
|
& these are the "basic" css colors
|
|
|
|
|
|
|
|
& https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
|
|
|
|
let colors = #{
|
|
|
|
let colors = #{
|
|
|
|
:white (255, 255, 255, 255)
|
|
|
|
|
|
|
|
:light_gray (150, 150, 150, 255)
|
|
|
|
|
|
|
|
:dark_gray (50, 50, 50, 255)
|
|
|
|
|
|
|
|
:red (255, 0, 0, 255)
|
|
|
|
|
|
|
|
:green (0, 255, 0, 255)
|
|
|
|
|
|
|
|
:blue (0, 0, 255, 255)
|
|
|
|
|
|
|
|
:black (0, 0, 0, 255)
|
|
|
|
:black (0, 0, 0, 255)
|
|
|
|
|
|
|
|
:silver (192, 192, 192, 255)
|
|
|
|
|
|
|
|
:gray (128, 128, 128, 255)
|
|
|
|
|
|
|
|
:white (255, 255, 255, 255)
|
|
|
|
|
|
|
|
:maroon (128, 0, 0, 255)
|
|
|
|
|
|
|
|
:red (255, 0, 0, 255)
|
|
|
|
|
|
|
|
:purple (128, 0, 128, 255)
|
|
|
|
|
|
|
|
:fuchsia (255, 0, 255, 255)
|
|
|
|
|
|
|
|
:green (0, 128, 0, 255)
|
|
|
|
|
|
|
|
:lime (0, 255, 0, 255)
|
|
|
|
|
|
|
|
:olive (128, 128, 0, 255)
|
|
|
|
|
|
|
|
:yellow (255, 255, 0, 255)
|
|
|
|
|
|
|
|
:navy (0, 0, 128, 255)
|
|
|
|
|
|
|
|
:blue (0, 0, 255, 255)
|
|
|
|
|
|
|
|
:teal (0, 128, 128, 255)
|
|
|
|
|
|
|
|
:aqua (0, 255, 25, 255)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
& the initial turtle state
|
|
|
|
& the initial turtle state
|
|
|
@ -986,6 +1045,7 @@ fn render_turtle! () -> {
|
|
|
|
add_call! ((:vertex, x2, y2))
|
|
|
|
add_call! ((:vertex, x2, y2))
|
|
|
|
add_call! ((:vertex, x3, y3))
|
|
|
|
add_call! ((:vertex, x3, y3))
|
|
|
|
add_call! ((:endShape))
|
|
|
|
add_call! ((:endShape))
|
|
|
|
|
|
|
|
& there's a happy bug here: the stroke will be the same width as the pen width. Keep this for now. Consider also showing the pen colour here?
|
|
|
|
add_call! ((:stroke, 0))
|
|
|
|
add_call! ((:stroke, 0))
|
|
|
|
add_call! ((:line, 0, 0, x1, y1))
|
|
|
|
add_call! ((:line, 0, 0, x1, y1))
|
|
|
|
add_call! ((:pop))
|
|
|
|
add_call! ((:pop))
|
|
|
@ -1219,6 +1279,8 @@ ns prelude {
|
|
|
|
div
|
|
|
|
div
|
|
|
|
div/0
|
|
|
|
div/0
|
|
|
|
div/safe
|
|
|
|
div/safe
|
|
|
|
|
|
|
|
inv
|
|
|
|
|
|
|
|
inv/0
|
|
|
|
angle
|
|
|
|
angle
|
|
|
|
abs
|
|
|
|
abs
|
|
|
|
neg
|
|
|
|
neg
|
|
|
@ -1231,6 +1293,9 @@ ns prelude {
|
|
|
|
gte?
|
|
|
|
gte?
|
|
|
|
lt?
|
|
|
|
lt?
|
|
|
|
lte?
|
|
|
|
lte?
|
|
|
|
|
|
|
|
min
|
|
|
|
|
|
|
|
max
|
|
|
|
|
|
|
|
between?
|
|
|
|
keyword?
|
|
|
|
keyword?
|
|
|
|
nil?
|
|
|
|
nil?
|
|
|
|
some?
|
|
|
|
some?
|
|
|
@ -1269,6 +1334,7 @@ ns prelude {
|
|
|
|
sum_of_squares
|
|
|
|
sum_of_squares
|
|
|
|
dist
|
|
|
|
dist
|
|
|
|
random
|
|
|
|
random
|
|
|
|
|
|
|
|
random_int
|
|
|
|
pi
|
|
|
|
pi
|
|
|
|
tau
|
|
|
|
tau
|
|
|
|
floor
|
|
|
|
floor
|
|
|
|