Skip to content

Commit 1a35edc

Browse files
authored
More higher order functions (#433)
* Woops this got out of control * Fix macroexpand-1 and macroexpand by preventing symbol resolution * Fixes and fixes * Yes * Type checking * Allow nonexistent forms in macroexpand functions
1 parent 3f54ce2 commit 1a35edc

File tree

8 files changed

+410
-16
lines changed

8 files changed

+410
-16
lines changed

src/basilisp/core.lpy

Lines changed: 194 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,6 +1511,29 @@
15111511
(reduce-coll val coll)
15121512
(f val)))))
15131513

1514+
(defn reduce-kv
1515+
"Reduce an associative coll by f. f must be a function of 3
1516+
arguments: the initially supplied value (in later invocations, the
1517+
the return from previous invocations), the key of an entry of
1518+
coll, and the value of an entry of coll.
1519+
1520+
If coll has no elements, init is returned and f is not called.
1521+
1522+
reduce-kv may reduce vectors in addition to maps. Vectors have
1523+
non-negative integer keys."
1524+
[f init coll]
1525+
(if (seq coll)
1526+
(loop [in-coll coll
1527+
assoc-coll init]
1528+
(let [entry (first in-coll)
1529+
k (key entry)
1530+
v (val entry)
1531+
rem (rest in-coll)]
1532+
(if (seq rem)
1533+
(recur rem (f assoc-coll k v))
1534+
(f assoc-coll k v))))
1535+
init))
1536+
15141537
(defn comp
15151538
"Return a function which is the composition of all the functions
15161539
given as arguments. Note that, as in mathematical function composition,
@@ -1550,6 +1573,25 @@
15501573
(pred (first coll)) (recur pred (rest coll))
15511574
:else false))
15521575

1576+
(defn every-pred
1577+
"Return a predicate composed of all of the input predicates, which returns
1578+
true if all input predicates return true for a value, otherwise returns false.
1579+
The returned predicate returns false on the first failing predicate and will
1580+
not execute any remaining predicates."
1581+
([pred]
1582+
(fn [& args]
1583+
(every? pred args)))
1584+
([pred & preds]
1585+
(let [comp-pred (reduce (fn [f g]
1586+
(fn [v]
1587+
(if (and (f v) (g v))
1588+
true
1589+
false)))
1590+
pred
1591+
preds)]
1592+
(fn [& args]
1593+
(every? comp-pred args)))))
1594+
15531595
(def
15541596
^{:doc "Return true if not every element in coll satisfies pred."}
15551597
not-every?
@@ -1562,13 +1604,35 @@
15621604
(or (pred (first coll))
15631605
(recur pred (rest coll)))))
15641606

1607+
(defn some-fn
1608+
"Return a predicate composed of all of the input functions, which returns
1609+
the first truthy return value from one of the inputs, otherwise returns nil.
1610+
The returned predicate returns the first truthy value it encounters and will
1611+
not execute any remaining functions."
1612+
([f]
1613+
(fn [& args]
1614+
(some f args)))
1615+
([f & fs]
1616+
(let [comp-f (reduce (fn [f g]
1617+
(fn [v]
1618+
(or (f v) (g v))))
1619+
f
1620+
fs)]
1621+
(fn [& args]
1622+
(some comp-f args)))))
1623+
15651624
(def
15661625
^{:doc "Return true if no element in coll satisfies pred."}
15671626
not-any?
15681627
(comp not some))
15691628

15701629
(defn map
1571-
"Return a lazy sequence of (f elem) for elements in coll."
1630+
"Return a lazy sequence of (f elem) for elements in coll. More than one
1631+
collection may be supplied. If more than one collection is supplied, the
1632+
function f will be passed sequential elements from each collection on each
1633+
invocation and must be able to accept as many arguments as there are
1634+
collections. The sequence will terminate when at least one input collection
1635+
is exhausted."
15721636
([f coll]
15731637
(lazy-seq
15741638
(when (seq coll)
@@ -1579,6 +1643,14 @@
15791643
(cons (apply f (first coll) (map first colls))
15801644
(apply map f (rest coll) (map rest colls)))))))
15811645

1646+
(def ^{:doc "Return a vector of (f elem) for elements in coll. More than one
1647+
collection may be supplied. If more than one collection is supplied, the
1648+
function f will be passed sequential elements from each collection on each
1649+
invocation and must be able to accept as many arguments as there are
1650+
collections. The sequence will terminate when at least one input collection
1651+
is exhausted."}
1652+
mapv (comp vec map))
1653+
15821654
(defn map-indexed
15831655
"Return a lazy sequence of (f idx elem) for elements in coll. The
15841656
index starts at 0."
@@ -1592,14 +1664,18 @@
15921664
(apply concat (apply map f colls)))
15931665

15941666
(defn filter
1595-
"Return elements from coll where (pred elem) returns true."
1667+
"Return a lazy sequence of elements from coll where (pred elem) returns true."
15961668
[pred coll]
15971669
(lazy-seq
15981670
(when (seq coll)
15991671
(if (pred (first coll))
16001672
(cons (first coll) (filter pred (rest coll)))
16011673
(filter pred (rest coll))))))
16021674

1675+
(def ^{:doc "Return a lazy sequence of elements from coll where (pred elem)
1676+
returns true."}
1677+
filterv (comp vec filter))
1678+
16031679
(defn remove
16041680
"Return elements from coll where (pred elem) returns false."
16051681
[pred coll]
@@ -1609,6 +1685,31 @@
16091685
(cons (first coll) (remove pred (rest coll)))
16101686
(remove pred (rest coll))))))
16111687

1688+
(defn keep
1689+
"Return a lazy sequence of non-nil results of (f elem) for elements in coll."
1690+
[f coll]
1691+
(lazy-seq
1692+
(when (seq coll)
1693+
(let [elem (first coll)]
1694+
(if-not (nil? (f elem))
1695+
(cons elem (keep f (rest coll)))
1696+
(keep f (rest coll)))))))
1697+
1698+
(defn keep-indexed
1699+
"Return a lazy-sequence of non-nil results of (f index elem) for elements
1700+
in coll."
1701+
[f coll]
1702+
(let [keep-idx (fn keep-idx
1703+
[rng coll]
1704+
(lazy-seq
1705+
(when (seq coll)
1706+
(let [idx (first rng)
1707+
elem (first coll)]
1708+
(if-not (nil? (f idx elem))
1709+
(cons elem (keep-idx (rest rng) (rest coll)))
1710+
(keep-idx (rest rng) (rest coll)))))))]
1711+
(keep-idx (range) coll)))
1712+
16121713
(defn take
16131714
"Return the first n elements of coll."
16141715
[n coll]
@@ -1688,6 +1789,23 @@
16881789
(cons sep (interpose sep (rest coll))))
16891790
(cons (first coll) nil)))))
16901791

1792+
(defn interleave
1793+
"Return a lazy sequence consisting of the first element of coll, then the
1794+
first element of the following coll, etc. until the shortest input collection
1795+
is exhausted."
1796+
([] '())
1797+
([coll] (seq coll))
1798+
([coll & colls]
1799+
(let [coll-firsts (fn coll-firsts
1800+
[& colls]
1801+
(lazy-seq
1802+
(when (seq colls)
1803+
(cons (ffirst colls) (apply coll-firsts (rest colls))))))]
1804+
(lazy-seq
1805+
(when (and (seq coll) (every? seq colls))
1806+
(concat (apply coll-firsts coll colls)
1807+
(apply interleave (rest coll) (map rest colls))))))))
1808+
16911809
(defn cycle
16921810
"Cycle the items in coll infinitely."
16931811
[coll]
@@ -1734,13 +1852,16 @@
17341852
of step elements. If step is not given, steps of size n will be used
17351853
and there will be no overlap between partitions. If pad is given,
17361854
partition will pull elements from pad until the final sequence is
1737-
equal to size n."
1855+
equal to size n. If there are fewer than n leftover elements in coll,
1856+
they will not be returned as a partial partition."
17381857
([n coll]
17391858
(partition n n coll))
17401859
([n step coll]
17411860
(lazy-seq
17421861
(when (seq coll)
1743-
(cons (take n coll) (partition n step (drop step coll))))))
1862+
(let [s (take n coll)]
1863+
(when (= n (count s))
1864+
(cons s (partition n step (drop step coll))))))))
17441865
([n step pad coll]
17451866
(lazy-seq
17461867
(when (seq coll)
@@ -1751,6 +1872,19 @@
17511872
s)]
17521873
(cons s (partition n step pad (drop step coll))))))))
17531874

1875+
(defn partition-all
1876+
"Return a lazy sequence of partitions of coll of size n at offsets
1877+
of step elements. If step is not given, steps of size n will be used
1878+
and there will be no overlap between partitions. If there are leftover
1879+
elements from coll which do not fill a full partition, then a partial
1880+
partition will be returned, unlike partition."
1881+
([n coll]
1882+
(partition-all n n coll))
1883+
([n step coll]
1884+
(lazy-seq
1885+
(when (seq coll)
1886+
(cons (take n coll) (partition-all n step (drop step coll)))))))
1887+
17541888
(defn partition-by
17551889
"Return a lazy sequence of partitions, splitting coll each time f
17561890
returns a different value."
@@ -1762,6 +1896,35 @@
17621896
run (cons elem (take-while #(= felem (f %)) (next coll)))]
17631897
(cons run (partition-by f (seq (drop (count run) coll))))))))
17641898

1899+
(defn min-key
1900+
"Return the arg for which (k arg) is the smallest number.
1901+
If multiple values return the same number, return the last."
1902+
[k & args]
1903+
(reduce (fn [cur nxt]
1904+
(if (<= (k nxt) (k cur))
1905+
nxt
1906+
cur))
1907+
(first args)
1908+
(rest args)))
1909+
1910+
(defn max-key
1911+
"Return the arg for which (k arg) is the largest number.
1912+
If multiple values return the same number, return the last."
1913+
[k & args]
1914+
(reduce (fn [cur nxt]
1915+
(if (>= (k nxt) (k cur))
1916+
nxt
1917+
cur))
1918+
(first args)
1919+
(rest args)))
1920+
1921+
(defn sort-by
1922+
"Return a sorted sequence of the elements from coll."
1923+
([keyfn coll]
1924+
(basilisp.lang.runtime/sort-by keyfn coll))
1925+
([keyfn cmp coll]
1926+
(basilisp.lang.runtime/sort-by keyfn coll cmp)))
1927+
17651928
(defn merge
17661929
"Merge maps together from left to right as by conj. If a duplicate key
17671930
appears in a map, the rightmost map's value for that key will be taken."
@@ -1771,6 +1934,16 @@
17711934
{}
17721935
maps)))
17731936

1937+
(defn trampoline
1938+
"Trampoline f with starting arguments. If f returns an fn, call its return
1939+
value with no arguments, repeating that process until the return value is not
1940+
a fn."
1941+
[f & args]
1942+
(loop [ret (apply f args)]
1943+
(if (fn? ret)
1944+
(recur (ret))
1945+
ret)))
1946+
17741947
;;;;;;;;;;;;;;;;;;;;;;
17751948
;; Random Functions ;;
17761949
;;;;;;;;;;;;;;;;;;;;;;
@@ -3012,6 +3185,23 @@
30123185
;; Associative Functions ;;
30133186
;;;;;;;;;;;;;;;;;;;;;;;;;;;
30143187

3188+
(defn replace
3189+
"Replace elements of the vector/seq coll with matching elements from the
3190+
associative collection smap, if they exist."
3191+
[smap coll]
3192+
(if (vector? coll)
3193+
(reduce (fn [res v]
3194+
(if-let [newv (get smap v)]
3195+
(conj res newv)
3196+
res))
3197+
[]
3198+
coll)
3199+
(map (fn [v]
3200+
(if-let [newv (get smap v)]
3201+
newv
3202+
v))
3203+
coll)))
3204+
30153205
(defn select-keys
30163206
"Return a map with only the keys of m which are in ks."
30173207
[m ks]

src/basilisp/core/template.lpy

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
(ns basilisp.core.template)
2+
3+
(defmacro do-template
4+
"Given a template expression expr and bindings, produce a do expression with
5+
the repeated templated expressions replacing names in argv with elements from
6+
args.
7+
8+
For example:
9+
10+
(macroexpand '(do-template [x y] (= x y)
11+
1 (dec 2)
12+
2 (inc 1)))
13+
14+
produces
15+
16+
(do
17+
(= 1 (dec 2))
18+
(= 2 (inc 1)))"
19+
[argv expr & args]
20+
(let [n (count argv)
21+
arg-groups (partition n args)
22+
template-expr (fn [arg-group]
23+
(as-> arg-group $
24+
(interleave argv $)
25+
(apply hash-map $)
26+
(replace $ expr)))]
27+
`(do
28+
~@(map template-expr arg-groups))) )

0 commit comments

Comments
 (0)