Skip to content

Commit

Permalink
More collection functions (#439)
Browse files Browse the repository at this point in the history
* Collection functions

* zipmap

* Dedupe

* More goodies

* Clean up the interface hierarchy a bit

* Reversible vector stuff

* Fix MyPy warning

* More interface cleaning
  • Loading branch information
chrisrink10 committed Sep 8, 2019
1 parent 1d23cbc commit e7b40eb
Show file tree
Hide file tree
Showing 4 changed files with 416 additions and 13 deletions.
218 changes: 209 additions & 9 deletions src/basilisp/core.lpy
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,16 @@
[coll i]
(basilisp.lang.runtime/nthrest coll i))

(defn nfirst
"Return the result of calling (next (first v))."
[v]
(next (first v)))

(defn nnext
"Return the result of calling (next (next v))."
[v]
(next (next v)))

(defn last
"Return the last item in a seq, or nil if the seq is empty."
[s]
Expand Down Expand Up @@ -952,11 +962,23 @@
[x]
(instance? python/complex x))

(defn counted?
"Return true if x can be counted in constant time."
[x]
(instance? basilisp.lang.interfaces/ICounted x))

(defn decimal?
"Return true if x is a Decimal."
[x]
(instance? decimal/Decimal x))

(defn empty?
"Return true if coll is empty (as by '(not (seq coll))).
Typically, you should prefer the idiom (seq coll) to (not (empty? coll))."
[coll]
(not (seq coll)))

(defn even?
"Return true if x is even."
[x]
Expand Down Expand Up @@ -1107,11 +1129,21 @@
[x]
(or (integer? x) (float? x)))

(defn reversible?
"Return true if x implements IReversible."
[x]
(instance? basilisp.lang.interfaces/IReversible x))

(defn seqable?
"Return true if an ISeq can be produced from x."
[x]
(instance? basilisp.lang.interfaces/ISeqable x))

(defn sequential?
"Return true if x implements ISequential."
[x]
(instance? basilisp.lang.interfaces/ISequential x))

(defn simple-keyword?
"Return true if x is a keyword with no namespace."
[x]
Expand Down Expand Up @@ -1275,6 +1307,92 @@
[x n]
(bit-and (bit-shift-right x n) 1))

;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Collection Functions ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;

(defn bounded-count
[n coll]
(if (counted? coll)
(count coll)
(let [counter (fn counter
[coll cur]
(cond
(>= cur n) n
(not (seq coll)) cur
:else (recur (rest coll) (inc cur))))]
(counter coll 0))))

(defn empty
"Return an empty collection of the same interface type as coll, or nil."
[coll]
(when (coll? coll)
(.empty coll)))

(defn not-empty
"Return coll when coll is not empty, otherwise return nil."
[coll]
(when (seq coll)
coll))

(defn peek
"For a list or a queue, return the first element.
For a vector, return the last element (more efficiently than by last).
For empty collections, returns nil."
[coll]
(.peek coll))

(defn pop
"For a list or a queue, return a new list without the first element.
For a vector, return a new vector without the last element.
If coll is empty, throw an exception."
[coll]
(.pop coll))

(defn reverse
"Return a seq containing the elements in coll in reverse order. The
returned sequence is not lazy."
[coll]
(let [do-reverse (fn do-reverse
[in out]
(if (seq in)
(recur (rest in) (cons (first in) out))
out))]
(do-reverse coll '())))

(defn rseq
"Return a sequence of the elements of coll in reverse order in constant
time. Only Vectors support this operation."
[coll]
(.rseq coll))

(defn sequence
"Coerces coll to a possibly empty sequence."
[coll]
(if (seq? coll)
coll
(or (seq coll) '())))

(defn subvec
"Return a vector of elements consisting of the elements of v from the index 'start'
(inclusive) to index 'end' exclusive, or the end of the vector if no 'end' is
supplied."
([v start]
(subvec v start nil))
([v start end]
(when (> start (count v))
(throw (python/IndexError "Start index out of range")))
(when-not (nil? end)
(when (> end (count v))
(throw (python/IndexError "End index out of range")))
(when (> start end)
(throw (python/IndexError "Start index must be less than or equal to end index"))))
(operator/getitem v (python/slice start end))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Associative Functions ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand All @@ -1298,12 +1416,6 @@
[m & ks]
(apply (.-dissoc m) ks))

(defn empty
"Return an empty collection of the same interface type as coll, or nil."
[coll]
(when (coll? coll)
(.empty coll)))

(defn get
"Return the entry of m corresponding to k if it exists or nil/default otherwise."
([m k]
Expand Down Expand Up @@ -1399,9 +1511,9 @@
[m]
(seq (.values m)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Higher Order Functions ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Higher Order and Collection Functions ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defmacro lazy-seq
"Takes a body of expressions which will produce a seq or nil. When
Expand Down Expand Up @@ -1534,6 +1646,14 @@
(f assoc-coll k v))))
init))

(defn into
"Return a new collection created by adding all of the elements of from
to the existing to collection, as by conj."
([] [])
([to] to)
([to from]
(reduce conj to from)))

(defn comp
"Return a function which is the composition of all the functions
given as arguments. Note that, as in mathematical function composition,
Expand Down Expand Up @@ -1751,6 +1871,11 @@
([n coll]
(map (comp first vector) coll (drop n coll))))

(defn butlast
"Return all but the last element in a sequence in linear time."
[coll]
(drop-last coll))

(defn split-at
"Split a collection at the nth item. Returns a vector of
[(take n coll) (drop n coll)]."
Expand All @@ -1763,6 +1888,19 @@
[pred coll]
[(take-while pred coll) (drop-while pred coll)])

(defn frequencies
"Return a map whose keys are the elements of coll and whose values are
the counts for the number of times the key appears in coll."
[coll]
(if-not (seq coll)
{}
(reduce (fn [m v]
(if (contains? m v)
(update m v inc)
(assoc m v 1)))
{}
coll)))

(defn group-by
"Return a map whose keys are the result of calling f on each element
in coll and whose values are vectors of the values which produced the
Expand Down Expand Up @@ -1896,6 +2034,47 @@
run (cons elem (take-while #(= felem (f %)) (next coll)))]
(cons run (partition-by f (seq (drop (count run) coll))))))))

(defn distinct
"Return a lazy sequence of the elements of coll, removing duplicates."
[coll]
(let [coll-distinct (fn coll-distinct
[coll found]
(lazy-seq
(when (seq coll)
(let [e (first coll)]
(if-not (contains? found e)
(cons e (coll-distinct (rest coll) (conj found e)))
(coll-distinct (rest coll) found))))))]
(coll-distinct coll #{})))

(defn dedupe
"Return a lazy sequence of the elements of coll, removing consecutive duplicates."
[coll]
(let [coll-dedupe (fn coll-dedupe
[coll prev]
(lazy-seq
(when (seq coll)
(let [e (first coll)]
(if-not (= e prev)
(cons e (coll-dedupe (rest coll) e))
(coll-dedupe (rest coll) prev))))))]
(lazy-seq
(when-let [e (first coll)]
(cons e (coll-dedupe (rest coll) e))))))

(defn flatten
"Flatten any combination of nested sequences (such as lists or vectors)
into a single lazy sequence. Calling flattening on non-sequential values
returns an empty sequence."
[v]
(lazy-seq
(when (and (or (seq? v) (seqable? v)) (seq v))
(let [e (first v)
r (rest v)]
(if (or (seq? e) (seqable? e))
(concat (flatten e) (flatten r))
(cons e (flatten r)))))))

(defn min-key
"Return the arg for which (k arg) is the smallest number.
If multiple values return the same number, return the last."
Expand Down Expand Up @@ -1925,6 +2104,12 @@
([keyfn cmp coll]
(basilisp.lang.runtime/sort-by keyfn coll cmp)))

(defn zipmap
"Return a map with the keys mapped to their corresponding indexed value
in vals."
[keys vals]
(apply hash-map (interleave keys vals)))

(defn merge
"Merge maps together from left to right as by conj. If a duplicate key
appears in a map, the rightmost map's value for that key will be taken."
Expand Down Expand Up @@ -2488,6 +2673,21 @@
(operator/mod fmt arg)))
(operator/mod fmt (python/tuple args))))

(defn subs
"Return a substring of s from the index 'start' (inclusive) to index 'end'
exclusive, or the end of the string if no 'end' is supplied."
([s start]
(subs s start nil))
([s start end]
(when (> start (count s))
(throw (python/IndexError "Start index out of range")))
(when-not (nil? end)
(when (> end (count s))
(throw (python/IndexError "End index out of range")))
(when (> start end)
(throw (python/IndexError "Start index must be less than or equal to end index"))))
(operator/getitem s (python/slice start end))))

;;;;;;;;;;;;;;;;;;;;;;
;; Output Utilities ;;
;;;;;;;;;;;;;;;;;;;;;;
Expand Down
31 changes: 27 additions & 4 deletions src/basilisp/lang/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ def deref(
raise NotImplementedError()


class ICounted(ABC):
__slots__ = ()


# Making this interface Generic causes the __repr__ to differ between
# Python 3.6 and 3.7, which affects a few simple test assertions.
# Since there is little benefit to this type being Generic, I'm leaving
Expand Down Expand Up @@ -72,6 +76,14 @@ def with_meta(self, meta: "IPersistentMap") -> "IMeta":
ILispObject = _LispObject


class IReversible(Generic[T]):
__slots__ = ()

@abstractmethod
def rseq(self) -> "ISeq[T]":
raise NotImplementedError()


class ISeqable(Iterable[T]):
__slots__ = ()

Expand All @@ -80,6 +92,10 @@ def seq(self) -> "ISeq[T]":
raise NotImplementedError()


class ISequential(ABC):
__slots__ = ()


class ILookup(Generic[K, V], ABC):
__slots__ = ()

Expand Down Expand Up @@ -131,27 +147,34 @@ def pop(self) -> "IPersistentStack[T]":
raise NotImplementedError()


class IPersistentList(IPersistentStack[T]):
class IPersistentList(ISequential, IPersistentStack[T]):
__slots__ = ()


class IPersistentMap(IAssociative[K, V]):
class IPersistentMap(ICounted, IAssociative[K, V]):
__slots__ = ()

@abstractmethod
def dissoc(self, *ks: K) -> "IPersistentMap[K, V]":
raise NotImplementedError()


class IPersistentSet(AbstractSet[T], IPersistentCollection[T]):
class IPersistentSet(AbstractSet[T], ICounted, IPersistentCollection[T]):
__slots__ = ()

@abstractmethod
def disj(self, *elems: T) -> "IPersistentSet[T]":
raise NotImplementedError()


class IPersistentVector(Sequence[T], IAssociative[int, T], IPersistentStack[T]):
class IPersistentVector(
Sequence[T],
IAssociative[int, T],
ICounted,
IReversible[T],
ISequential,
IPersistentStack[T],
):
__slots__ = ()

@abstractmethod
Expand Down
Loading

0 comments on commit e7b40eb

Please sign in to comment.