(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 (unsafe-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 (unsafe-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 (unsafe-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 (unsafe-nth a i)) (do (set! res (Maybe.Just @(unsafe-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 (unsafe-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.unsafe-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.unsafe-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.unsafe-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.unsafe-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 (/= (unsafe-nth a i) (unsafe-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 (unsafe-nth xs 0) n (length xs)] (for [i 1 n] (let [x (unsafe-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 (unsafe-nth xs 0) n (length xs)] (for [i 1 n] (let [x (unsafe-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 @(unsafe-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 @(unsafe-nth &a i)] (aset! &a i @(unsafe-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 (= (unsafe-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 (unsafe-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 (unsafe-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 (unsafe-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 (unsafe-nth a i)))) (doc swap "swaps the indices `i` and `j` of an array `a`.") (defn swap [a i j] (let [x @(unsafe-nth &a i) y @(unsafe-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 @(unsafe-nth a i) y @(unsafe-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 (unsafe-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 (unsafe-nth a i) (unsafe-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.unsafe-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.unsafe-nth xs i) len (Array.length arr)] (for [k 0 len] (aset-uninitialized! &result (+ j k) @(Array.unsafe-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 nth "gets a reference to the `n`th element from an array `arr` wrapped on a `Maybe`. If the `index` is out of bounds, return `Maybe.Nothing`") (defn nth [arr index] (if (and (>= index 0) (< index (length arr))) (Maybe.Just @(unsafe-nth a i)) ; the copy will go away with lifetimes (Maybe.Nothing))) (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 @(unsafe-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.unsafe-nth arr i)) (do (set! result true) (break)))) result)) )