mirror of
https://github.com/carp-lang/Carp.git
synced 2024-10-26 13:51:50 +03:00
333 lines
10 KiB
Plaintext
333 lines
10 KiB
Plaintext
(defmodule Array
|
||
|
||
(doc reduce "will reduce an array `xs` into a single value using a function `f` that takes the reduction thus far and the next value. The initial reduction value is `x`.
|
||
|
||
As an example, consider this definition of `sum` based on `reduce`:
|
||
|
||
```
|
||
(defn sum [x]
|
||
(reduce + 0 x))
|
||
```
|
||
|
||
It will sum the previous sum with each new value, starting at `0`.")
|
||
(defn reduce [f x xs]
|
||
(let [total x]
|
||
(do
|
||
(for [i 0 (length xs)]
|
||
(set! total (~f total (nth xs i))))
|
||
total)))
|
||
|
||
(doc empty? "checks whether the array `a` is empty.")
|
||
(defn empty? [a]
|
||
(= (Array.length a) 0))
|
||
|
||
(doc any? "checks whether any of the elements in `a` match the function `f`.")
|
||
(defn any? [f a]
|
||
(let-do [res false]
|
||
(for [i 0 (length a)]
|
||
(when (~f (nth a i))
|
||
(do
|
||
(set! res true)
|
||
(break))))
|
||
res))
|
||
|
||
(doc all? "checks whether all of the elements in `a` match the function `f`.")
|
||
(defn all? [f a]
|
||
(let-do [res true]
|
||
(for [i 0 (length a)]
|
||
(when (not (~f (nth a i)))
|
||
(do
|
||
(set! res false)
|
||
(break))))
|
||
res))
|
||
|
||
(doc find "finds an element in `a` that matches the function `f` and wraps it in a `Just`.
|
||
|
||
If it doesn’t find an element, `Nothing` will be returned.")
|
||
(defn find [f a]
|
||
(let-do [res (Maybe.Nothing)]
|
||
(for [i 0 (length a)]
|
||
(when (~f (nth a i))
|
||
(do
|
||
(set! res (Maybe.Just @(nth a i)))
|
||
(break))))
|
||
res))
|
||
|
||
(doc find-index "finds the index of the first element in `a` that matches the function `f` and wraps it in a `Just`.
|
||
|
||
If it doesn’t find an index, `Nothing` will be returned.")
|
||
(defn find-index [f a]
|
||
(let-do [ret (Maybe.Nothing)]
|
||
(for [i 0 (length a)]
|
||
(when (~f (nth a i))
|
||
(do
|
||
(set! ret (Maybe.Just i))
|
||
(break))))
|
||
ret))
|
||
|
||
(doc unsafe-first "takes the first element of an array.
|
||
|
||
Generates a runtime error if the array is empty.")
|
||
(defn unsafe-first [a]
|
||
@(Array.nth a 0))
|
||
|
||
(doc first "takes the first element of an array and returns a `Just`.
|
||
|
||
Returns `Nothing` if the array is empty.")
|
||
(defn first [a]
|
||
(if (empty? a)
|
||
(Maybe.Nothing)
|
||
(Maybe.Just @(Array.nth a 0))))
|
||
|
||
(doc unsafe-last "takes the last element of an array.
|
||
|
||
Generates a runtime error if the array is empty.")
|
||
(defn unsafe-last [a]
|
||
@(Array.nth a (Int.dec (Array.length a))))
|
||
|
||
|
||
(doc last "takes the last element of an array and returns a `Just`.
|
||
|
||
Returns `Nothing` if the array is empty.")
|
||
(defn last [a]
|
||
(if (empty? a)
|
||
(Maybe.Nothing)
|
||
(Maybe.Just @(Array.nth a (Int.dec (Array.length a))))))
|
||
|
||
(doc = "compares two arrays.")
|
||
(defn = [a b]
|
||
(if (/= (length a) (length b))
|
||
false
|
||
(let-do [eq true]
|
||
(for [i 0 (length a)]
|
||
(when (/= (nth a i) (nth b i))
|
||
(do
|
||
(set! eq false)
|
||
(break))))
|
||
eq)))
|
||
|
||
(doc maximum "gets the maximum in an array (elements must support `<`) and wraps it in a `Just`.
|
||
|
||
If the array is empty, it returns `Nothing`.")
|
||
(defn maximum [xs]
|
||
(if (empty? xs)
|
||
(Maybe.Nothing)
|
||
(let-do [result (nth xs 0)
|
||
n (length xs)]
|
||
(for [i 1 n]
|
||
(let [x (nth xs i)]
|
||
(when (< result x)
|
||
(set! result x))))
|
||
(Maybe.Just @result))))
|
||
|
||
(doc minimum "gets the minimum in an array (elements must support `>`) and wraps it in a `Just`.
|
||
|
||
If the array is empty, returns `Nothing`")
|
||
(defn minimum [xs]
|
||
(if (empty? xs)
|
||
(Maybe.Nothing)
|
||
(let-do [result (nth xs 0)
|
||
n (length xs)]
|
||
(for [i 1 n]
|
||
(let [x (nth xs i)]
|
||
(when (> result x)
|
||
(set! result x))))
|
||
(Maybe.Just @result))))
|
||
|
||
(doc sum "sums an array (elements must support `+` and `zero`).")
|
||
(defn sum [xs]
|
||
(Array.reduce &(fn [x y] (+ x @y)) (zero) xs))
|
||
|
||
(doc subarray "gets a subarray from `start-index` to `end-index`.")
|
||
(defn subarray [xs start-index end-index]
|
||
(let-do [result []]
|
||
(for [i start-index end-index]
|
||
(set! result (push-back result @(nth xs i))))
|
||
result))
|
||
|
||
(doc prefix-array "gets a prefix array to `end-index`.")
|
||
(defn prefix-array [xs end-index]
|
||
(subarray xs 0 end-index))
|
||
|
||
(doc suffix-array "gets a suffix array from `start-index`.")
|
||
(defn suffix-array [xs start-index]
|
||
(subarray xs start-index (length xs)))
|
||
|
||
(doc reverse "reverses an array.")
|
||
(defn reverse [a]
|
||
(let-do [i 0
|
||
j (Int.dec (length &a))]
|
||
(while (Int.< i j)
|
||
(let-do [tmp @(nth &a i)]
|
||
(aset! &a i @(nth &a j))
|
||
(set! i (Int.inc i))
|
||
(aset! &a j tmp)
|
||
(set! j (Int.dec j))))
|
||
a))
|
||
|
||
(doc index-of "gets the index of element `e` in an array and wraps it on a `Just`.
|
||
|
||
If the element is not found, returns `Nothing`")
|
||
(defn index-of [a e]
|
||
(let-do [idx (Maybe.Nothing)]
|
||
(for [i 0 (length a)]
|
||
(when (= (nth a i) e)
|
||
(do
|
||
(set! idx (Maybe.Just i))
|
||
(break))))
|
||
idx))
|
||
|
||
(doc element-count "counts the occurrences of element `e` in an array.")
|
||
(defn element-count [a e]
|
||
(let-do [c 0]
|
||
(for [i 0 (length a)]
|
||
(when (= e (nth a i)) (set! c (Int.inc c))))
|
||
c))
|
||
|
||
(doc predicate-count "counts the number of elements satisfying the predicate function `pred` in an array.")
|
||
(defn predicate-count [a pred]
|
||
(let-do [c 0]
|
||
(for [i 0 (length a)]
|
||
(when (~pred (nth a i))
|
||
(set! c (Int.inc c))))
|
||
c))
|
||
|
||
(doc aupdate "transmutes (i.e. updates) the element at index `i` of an array `a` using the function `f`.")
|
||
(defn aupdate [a i f]
|
||
(let [new-value (~f (nth &a i))]
|
||
(aset a i new-value)))
|
||
|
||
(doc aupdate! "transmutes (i.e. updates) the element at index `i` of an array `a` using the function `f` in place.")
|
||
(defn aupdate! [a i f]
|
||
(aset! a i (~f (nth a i))))
|
||
|
||
(doc swap "swaps the indices `i` and `j` of an array `a`.")
|
||
(defn swap [a i j]
|
||
(let [x @(nth &a i)
|
||
y @(nth &a j)]
|
||
(aset (aset a i y) j x)))
|
||
|
||
(doc swap! "swaps the indices `i` and `j` of an array `a` in place.")
|
||
(defn swap! [a i j]
|
||
(let-do [x @(nth a i)
|
||
y @(nth a j)]
|
||
(aset! a i y)
|
||
(aset! a j x)))
|
||
|
||
; cannot use for, because we want also be able to go downwards
|
||
(doc range "creates an array from `start` to `end` with `step` between them (the elements must support `<`, `<=`, `>=`, and `to-int`).")
|
||
(defn range [start end step]
|
||
(let-do [x (allocate (Int.inc (Int.abs (to-int (/ (- end start) step)))))
|
||
e start
|
||
i 0
|
||
op (if (< start end) <= >=)]
|
||
(while (op e end)
|
||
(do
|
||
(aset! &x i e)
|
||
(set! i (Int.inc i))
|
||
(set! e (+ e step))))
|
||
x))
|
||
|
||
(doc repeat "repeats the function `f` `n` times and stores the results in an array.")
|
||
(defn repeat [n f]
|
||
(let-do [a (allocate n)]
|
||
(for [i 0 n] (aset-uninitialized! &a i (~f)))
|
||
a))
|
||
|
||
(doc repeat-indexed "repeats function `f` `n` times and stores the results in an array.
|
||
|
||
This is similar to [`repeat`](#repeat), but the function `f` will be supplied with the index of the element.")
|
||
(defn repeat-indexed [n f]
|
||
(let-do [a (allocate n)]
|
||
(for [i 0 n] (aset-uninitialized! &a i (f i)))
|
||
a))
|
||
|
||
(doc replicate "repeats element `e` `n` times and stores the results in an array.")
|
||
(defn replicate [n e]
|
||
(let-do [a (allocate n)]
|
||
(for [i 0 n] (aset-uninitialized! &a i @e))
|
||
a))
|
||
|
||
(doc copy-map "maps over an array `a` using the function `f`.
|
||
|
||
This function copies the array. If you don’t want that, use [`endo-map`](#endo-map).")
|
||
(defn copy-map [f a]
|
||
(let-do [na (allocate (length a))]
|
||
(for [i 0 (length a)]
|
||
(aset-uninitialized! &na i (~f (nth a i))))
|
||
na))
|
||
|
||
(doc zip "maps over two arrays using a function `f` that takes two arguments. It will produces a new array with the length of the shorter input.
|
||
|
||
The trailing elements of the longer array will be discarded.")
|
||
(defn zip [f a b]
|
||
(let-do [l (Int.min (length a) (length b))
|
||
na (allocate l)]
|
||
(for [i 0 l]
|
||
(aset-uninitialized! &na i (~f (nth a i) (nth b i))))
|
||
na))
|
||
|
||
(doc sum-length "returns the sum of lengths from a nested array `xs`.")
|
||
(defn sum-length [xs]
|
||
(let-do [sum 0
|
||
lxs (Array.length xs)]
|
||
(for [i 0 lxs]
|
||
(set! sum (+ sum (Array.length (Array.nth xs i)))))
|
||
sum))
|
||
|
||
(doc zero "returns the empty array.")
|
||
(defn zero [] [])
|
||
|
||
(doc concat "returns a new array which is the concatenation of the provided nested array `xs`.")
|
||
(defn concat [xs]
|
||
;; This is using a StringBuilder pattern to only perform one allocation and
|
||
;; to only copy each of the incoming Array(s) once.
|
||
;; This currently performs wasted Array.length calls, as we call it for each
|
||
;; Array once here and once in sum-length.
|
||
(let-do [j 0
|
||
lxs (Array.length xs)
|
||
result (Array.allocate (sum-length xs))]
|
||
(for [i 0 lxs]
|
||
(let-do [arr (Array.nth xs i)
|
||
len (Array.length arr)]
|
||
(for [k 0 len]
|
||
(aset-uninitialized! &result (+ j k) @(Array.nth arr k)))
|
||
(set! j (+ j len))))
|
||
result))
|
||
|
||
(doc enumerated "creates a new array of `Pair`s where the first position is the index and the second position is the element from the original array `xs`.")
|
||
(defn enumerated [xs]
|
||
(zip &Pair.init-from-refs
|
||
&(range 0 (length xs) 1) ;; Inefficient, creates a temporary array.
|
||
xs))
|
||
|
||
(doc remove "removes all occurrences of the element `el` in the array `arr`, in place.")
|
||
(defn remove [el arr]
|
||
(endo-filter &(fn [x] (not (= el x)))
|
||
arr))
|
||
|
||
(doc remove-nth "removes element at index `idx` from the array `arr`.")
|
||
(defn remove-nth [i arr]
|
||
(do
|
||
;;(assert (<= 0 i))
|
||
;;(assert (< i (Array.length &arr)))
|
||
(for [j i (Int.dec (Array.length &arr))]
|
||
(aset! &arr j @(nth &arr (inc j))))
|
||
(pop-back arr)))
|
||
|
||
(doc copy-filter "filters the elements in an array.
|
||
|
||
It will create a copy. If you want to avoid that, consider using [`endo-filter`](#endo-filter) instead.")
|
||
(defn copy-filter [f a] (endo-filter f @a))
|
||
|
||
(doc contains? "checks wether an element exists in the array.")
|
||
(defn contains? [arr el]
|
||
(let-do [result false]
|
||
(for [i 0 (Array.length arr)]
|
||
(when (= el (Array.nth arr i))
|
||
(do
|
||
(set! result true)
|
||
(break))))
|
||
result))
|
||
)
|