import type { Eq } from "@principia/prelude/Eq"; import * as Ord from "@principia/prelude/Ord"; import { toNumber } from "@principia/prelude/Ordering"; import type { Either } from "../Either"; import type { Predicate, Refinement } from "../Function"; import type { NonEmptyArray } from "../NonEmptyArray"; import type { Option } from "../Option"; import { isSome, none, some } from "../Option"; import { empty } from "./constructors"; import { reduce_ } from "./foldable"; import { isEmpty, isNonEmpty, isOutOfBound_ } from "./guards"; import { chain_ } from "./monad"; /* * ------------------------------------------- * Combinators * ------------------------------------------- */ export const append_ = (xs: ReadonlyArray, ys: ReadonlyArray): ReadonlyArray => { const lenx = xs.length; if (lenx === 0) { return ys; } const leny = ys.length; if (leny === 0) { return xs; } const r = Array(lenx + leny); for (let i = 0; i < lenx; i++) { r[i] = xs[i]; } for (let i = 0; i < leny; i++) { r[i + lenx] = ys[i]; } return r; }; export const append = (ys: ReadonlyArray) => (xs: ReadonlyArray): ReadonlyArray => append_(xs, ys); export const lookup_ = (i: number, as: ReadonlyArray): Option => (isOutOfBound_(i, as) ? none() : some(as[i])); export const lookup = (i: number) => (as: ReadonlyArray): Option => lookup_(i, as); export const scanl = (b: B, f: (b: B, a: A) => B) => (as: ReadonlyArray): ReadonlyArray => { const l = as.length; const r: Array = new Array(l + 1); r[0] = b; for (let i = 0; i < l; i++) { r[i + 1] = f(r[i], as[i]); } return r; }; export const scanr = (b: B, f: (a: A, b: B) => B) => (as: ReadonlyArray): ReadonlyArray => { const l = as.length; const r: Array = new Array(l + 1); r[l] = b; for (let i = l - 1; i >= 0; i--) { r[i] = f(as[i], r[i + 1]); } return r; }; export const cons_ = (head: A, tail: ReadonlyArray): NonEmptyArray => { const len = tail.length; const r = Array(len + 1); for (let i = 0; i < len; i++) { r[i + 1] = tail[i]; } r[0] = head; return (r as unknown) as NonEmptyArray; }; export const cons = (tail: ReadonlyArray) => (head: A): NonEmptyArray => cons_(head, tail); export const snoc_ = (init: ReadonlyArray, end: A): NonEmptyArray => { const len = init.length; const r = Array(len + 1); for (let i = 0; i < len; i++) { r[i] = init[i]; } r[len] = end; return (r as unknown) as NonEmptyArray; }; export const snoc = (end: A) => (init: ReadonlyArray): NonEmptyArray => snoc_(init, end); export const head = (as: ReadonlyArray): Option => (isEmpty(as) ? none() : some(as[0])); export const tail = (as: ReadonlyArray): Option> => (isEmpty(as) ? none() : some(as.slice(1))); export const last = (as: ReadonlyArray): Option => lookup_(as.length - 1, as); export const init = (as: ReadonlyArray): Option> => { const len = as.length; return len === 0 ? none() : some(as.slice(0, len - 1)); }; export const takel = (n: number) => (as: ReadonlyArray): ReadonlyArray => as.slice(0, n); export const taker = (n: number) => (as: ReadonlyArray): ReadonlyArray => isEmpty(as) ? empty() : as.slice(-n); const spanIndexUncurry = (as: ReadonlyArray, predicate: Predicate): number => { const l = as.length; let i = 0; for (; i < l; i++) { if (!predicate(as[i])) { break; } } return i; }; export const takelWhile: { (refinement: Refinement): (as: ReadonlyArray) => ReadonlyArray; (predicate: Predicate): (as: ReadonlyArray) => ReadonlyArray; } = (predicate: Predicate) => (as: ReadonlyArray) => { const i = spanIndexUncurry(as, predicate); const init = Array(i); for (let j = 0; j < i; j++) { init[j] = as[j]; } return init; }; export interface Spanned { readonly init: ReadonlyArray; readonly rest: ReadonlyArray; } export const spanl: { (refinement: Refinement): (as: ReadonlyArray) => Spanned; (predicate: Predicate): (as: ReadonlyArray) => Spanned; } = (predicate: Predicate) => (as: ReadonlyArray): Spanned => { const i = spanIndexUncurry(as, predicate); const init = Array(i); for (let j = 0; j < i; j++) { init[j] = as[j]; } const l = as.length; const rest = Array(l - i); for (let j = i; j < l; j++) { rest[j - i] = as[j]; } return { init, rest }; }; export const dropl = (n: number) => (as: ReadonlyArray): ReadonlyArray => as.slice(n, as.length); export const dropr = (n: number) => (as: ReadonlyArray): ReadonlyArray => as.slice(0, as.length - n); export const droprWhile = (predicate: Predicate) => (as: ReadonlyArray): ReadonlyArray => { const i = spanIndexUncurry(as, predicate); const l = as.length; const rest = Array(l - i); for (let j = i; j < l; j++) { rest[j - i] = as[j]; } return rest; }; export const findr: { (refinement: Refinement): (as: ReadonlyArray) => Option; (predicate: Predicate): (as: ReadonlyArray) => Option; } = (predicate: Predicate) => (as: ReadonlyArray): Option => { const len = as.length; for (let i = 0; i < len; i++) { if (predicate(as[i])) { return some(as[i]); } } return none(); }; export const findrMap = (f: (a: A) => Option) => (as: ReadonlyArray): Option => { const len = as.length; for (let i = 0; i < len; i++) { const v = f(as[i]); if (isSome(v)) { return v; } } return none(); }; export const findl: { (refinement: Refinement): (as: ReadonlyArray) => Option; (predicate: Predicate): (as: ReadonlyArray) => Option; } = (predicate: Predicate) => (as: ReadonlyArray): Option => { const len = as.length; for (let i = len - 1; i >= 0; i--) { if (predicate(as[i])) { return some(as[i]); } } return none(); }; export const findlMap = (f: (a: A) => Option) => (as: ReadonlyArray): Option => { const len = as.length; for (let i = len - 1; i >= 0; i--) { const v = f(as[i]); if (isSome(v)) { return v; } } return none(); }; export const findlIndex_ = (as: ReadonlyArray, predicate: Predicate): Option => { const len = as.length; for (let i = 0; i < len; i++) { if (predicate(as[i])) { return some(i); } } return none(); }; export const findlIndex = (predicate: Predicate) => (as: ReadonlyArray): Option => findlIndex_(as, predicate); export const findrIndex = (predicate: Predicate) => (as: ReadonlyArray): Option => { const len = as.length; for (let i = len - 1; i >= 0; i--) { if (predicate(as[i])) { return some(i); } } return none(); }; export const unsafeInsertAt = (i: number, a: A, as: ReadonlyArray): ReadonlyArray => { const xs = Array.from(as); xs.splice(i, 0, a); return xs; }; export const unsafeUpdateAt = (i: number, a: A, as: ReadonlyArray): ReadonlyArray => { if (as[i] === a) { return as; } else { const xs = Array.from(as); xs[i] = a; return xs; } }; export const unsafeDeleteAt = (i: number, as: ReadonlyArray): ReadonlyArray => { const xs = Array.from(as); xs.splice(i, 1); return xs; }; export const insertAt_ = (as: ReadonlyArray, i: number, a: A): Option> => isOutOfBound_(i, as) ? none() : some(unsafeInsertAt(i, a, as)); export const insertAt = (i: number, a: A) => (as: ReadonlyArray): Option> => insertAt_(as, i, a); export const updateAt_ = (as: ReadonlyArray, i: number, a: A): Option> => isOutOfBound_(i, as) ? none() : some(unsafeUpdateAt(i, a, as)); export const updateAt = (i: number, a: A) => (as: ReadonlyArray): Option> => updateAt_(as, i, a); export const deleteAt_ = (as: ReadonlyArray, i: number): Option> => isOutOfBound_(i, as) ? none() : some(unsafeDeleteAt(i, as)); export const deleteAt = (i: number) => (as: ReadonlyArray): Option> => deleteAt_(as, i); /** * Apply a function to the element at the specified index, creating a new array, or returning `None` if the index is out * of bounds * * @since 1.0.0 */ export const modifyAt_ = (as: ReadonlyArray, i: number, f: (a: A) => A): Option> => isOutOfBound_(i, as) ? none() : some(unsafeUpdateAt(i, f(as[i]), as)); /** * Apply a function to the element at the specified index, creating a new array, or returning `None` if the index is out * of bounds * * @since 1.0.0 */ export const modifyAt = (i: number, f: (a: A) => A) => (as: ReadonlyArray): Option> => modifyAt_(as, i, f); export const reverse = (as: ReadonlyArray): ReadonlyArray => (isEmpty(as) ? as : as.slice().reverse()); export const rights = (as: ReadonlyArray>): ReadonlyArray => { const rs: Array = []; for (let i = 0; i < as.length; i++) { const a = as[i]; if (a._tag === "Right") { rs.push(a.right); } } return rs; }; export const lefts = (as: ReadonlyArray>): ReadonlyArray => { const ls: Array = []; for (let i = 0; i < as.length; i++) { const a = as[i]; if (a._tag === "Left") { ls.push(a.left); } } return ls; }; export const sort = (O: Ord.Ord) => (as: ReadonlyArray): ReadonlyArray => isEmpty(as) ? empty() : as.slice().sort((a, b) => toNumber(O.compare(a)(b))); export const unzip = (as: ReadonlyArray): readonly [ReadonlyArray, ReadonlyArray] => { const fa: Array = []; const fb: Array = []; for (let i = 0; i < as.length; i++) { fa[i] = as[i][0]; fb[i] = as[i][1]; } return [fa, fb]; }; export const elem = (E: Eq) => (a: A) => (as: ReadonlyArray): boolean => { const predicate = (element: A) => E.equals(element)(a); let i = 0; const len = as.length; for (; i < len; i++) { if (predicate(as[i])) { return true; } } return false; }; export const uniq = (E: Eq) => (as: ReadonlyArray): ReadonlyArray => { const elemS = elem(E); const out: Array = []; const len = as.length; let i = 0; for (; i < len; i++) { const a = as[i]; if (!elemS(a)(out)) { out.push(a); } } return len === out.length ? as : out; }; export const sortBy = (ords: ReadonlyArray>) => (as: ReadonlyArray): ReadonlyArray => { const M = Ord.getMonoid(); return sort(reduce_(ords, M.nat, (b, a) => M.combine_(a, b)))(as); }; export const comprehension: { ( input: readonly [ReadonlyArray, ReadonlyArray, ReadonlyArray, ReadonlyArray], f: (a: A, b: B, c: C, d: D) => R, g?: (a: A, b: B, c: C, d: D) => boolean ): ReadonlyArray; ( input: readonly [ReadonlyArray, ReadonlyArray, ReadonlyArray], f: (a: A, b: B, c: C) => R, g?: (a: A, b: B, c: C) => boolean ): ReadonlyArray; ( input: readonly [ReadonlyArray, ReadonlyArray], f: (a: A, b: B) => R, g?: (a: A, b: B) => boolean ): ReadonlyArray; (input: readonly [ReadonlyArray], f: (a: A) => boolean, g?: (a: A) => R): ReadonlyArray; } = ( input: ReadonlyArray>, f: (...xs: ReadonlyArray) => R, g: (...xs: ReadonlyArray) => boolean = () => true ): ReadonlyArray => { const go = (scope: ReadonlyArray, input: ReadonlyArray>): ReadonlyArray => { if (input.length === 0) { return g(...scope) ? [f(...scope)] : empty(); } else { return chain_(input[0], (x) => go(snoc_(scope, x), input.slice(1))); } }; return go(empty(), input); }; export const union = (E: Eq) => (ys: ReadonlyArray) => (xs: ReadonlyArray): ReadonlyArray => { const elemE = elem(E); return append_( xs, ys.filter((a) => !elemE(a)(xs)) ); }; export const intersection = (E: Eq) => (ys: ReadonlyArray) => (xs: ReadonlyArray): ReadonlyArray => { const elemE = elem(E); return xs.filter((a) => elemE(a)(ys)); }; export const chop_ = ( as: ReadonlyArray, f: (as: NonEmptyArray) => readonly [B, ReadonlyArray] ): ReadonlyArray => { const result: Array = []; let cs: ReadonlyArray = as; while (isNonEmpty(cs)) { const [b, c] = f(cs); result.push(b); cs = c; } return result; }; export const chop = (f: (as: NonEmptyArray) => readonly [B, ReadonlyArray]) => ( as: ReadonlyArray ): ReadonlyArray => chop_(as, f); export const splitAt = (n: number) => (as: ReadonlyArray): readonly [ReadonlyArray, ReadonlyArray] => [ as.slice(0, n), as.slice(n) ]; export const chunksOf = (n: number) => (as: ReadonlyArray): ReadonlyArray> => as.length === 0 ? empty() : isOutOfBound_(n - 1, as) ? [as] : chop_(as, splitAt(n)); export const difference = (E: Eq) => (ys: ReadonlyArray) => (xs: ReadonlyArray): ReadonlyArray => { const elemE = elem(E); return xs.filter((a) => !elemE(a)(ys)); }; export const dropLeft_ = (as: ReadonlyArray, n: number): ReadonlyArray => as.slice(n, as.length); export const dropLeft = (n: number) => (as: ReadonlyArray): ReadonlyArray => dropLeft_(as, n); export const dropRight_ = (as: ReadonlyArray, n: number): ReadonlyArray => as.slice(0, as.length - n); export const dropRight = (n: number) => (as: ReadonlyArray): ReadonlyArray => dropRight_(as, n); export const dropLeftWhile_ = (as: ReadonlyArray, predicate: Predicate): ReadonlyArray => { const i = spanIndexUncurry(as, predicate); const l = as.length; const rest = Array(l - i); for (let j = i; j < l; j++) { rest[j - i] = as[j]; } return rest; }; export const dropLeftWhile = (predicate: Predicate) => (as: ReadonlyArray): ReadonlyArray => dropLeftWhile_(as, predicate);