# clj
`clj` is a collection of functions to work with lazy iterators in Python.
It’s a module for those times when you did too much Clojure and came back to Python wondering where are all these
`distinct`, `drop-while`, `cycle`, `first`, etc.
The library is oriented toward laziness and performance. Functions are implemented with as little overhead as possible.
## Install
pip install clj
Since 0.2.0, only Python 3.7+ is supported.
## Usage
The library is considered stable and is used in production.
### Example
```clojure
;; Clojure
(->> coll (map inc) (filter even?) distinct count println)
; which expands to:
(println (count (distinct (filter even? (map inc coll)))))
```
```python
# Python
from clj import count, distinct, inc, is_even
print(count(distinct(filter(is_even, map(inc, coll)))))
```
Note that `count()` works on both sequences in generators; in the latter case it doesn’t load everything in memory like
e.g. `len(list(g))` would do.
## Core Ideas
* Lazy by default. All functions should work on arbitrary iterators and return generators.
* This is Python. We keep Python’s semantics instead of trying to reproduce Clojure in Python (e.g. `0` and `[]` are
logically true in Clojure but false in Python; `None` is not equivalent to an empty collection).
* Don’t Reinvent the Wheel. We don’t reimplement built-in functions unless they miss something, like `range` that can’t
be called without argument to yield an infinite sequence.
## Support
The general naming scheme is: use underscores instead of hyphens; start the function with `is_` if its Clojure
counterparts ends with a `?`.
### Sequences
We aim to implement all Clojure functions that operate on sequences
(see [the list here][seqs]). They all work on iterables and return generators by default (Python’s closest equivalent of
lazy seqs). We don’t support transducers.
[seqs]: http://clojure.org/reference/sequences
| Clojure | `clj` | Comment |
|-------------------|:----------------|---------------------------------------------------------------------------------------------------------------------|
| `distinct` | `distinct` | |
| `filter` | `filter` | Alias to Python’s built-in `filter`. |
| `remove` | `remove` | |
| `keep` | `keep` | |
| `keep-indexed` | `keep_indexed` | |
| `cons` | `cons` | |
| `concat` | `concat` | Equivalent to `itertools.chain`. |
| `lazy-cat` | - | Use Python’s `itertools.chain`. |
| `mapcat` | `mapcat` | |
| `cycle` | `cycle` | |
| `interleave` | `interleave` | |
| `interpose` | `interpose` | |
| `rest` | `rest` | |
| `next` | - | Use `rest`. |
| `fnext` | - | Use `second`. |
| `nnext` | - | Use `rest(rest(…))` |
| `drop` | `drop` | |
| `drop-while` | `drop_while` | Equivalent to `itertools.dropwhile`. |
| `nthnext` | - | Use `drop`. |
| `take` | `take` | |
| `take-nth` | `take_nth` | |
| `take-while` | `take_while` | Equivalent to `itertools.takewhile`. |
| `butlast` | `butlast` | |
| `drop-last` | `drop_last` | |
| `flatten` | `flatten` | |
| `reverse` | `reverse` | |
| `sort` | - | Use Python’s built-in `sort`. |
| `sort-by` | - | Use `sort(…, key=your_function)`. |
| `shuffle` | `shuffle` | |
| `split-at` | `split_at` | |
| `split-with` | `split_with` | |
| `partition` | `partition` | `(partition n step pad coll)` becomes `partition(coll, n, step, pad)`. Only the case `step=n` is supported for now. |
| `partition-all` | | |
| `partition-by` | `partition_by` | |
| `map` | `map` | Alias to Python’s built-in `map`. |
| `pmap` | - | |
| `replace` | `replace` | |
| `reductions` | `reductions` | `(reductions f i c)` becomes `reductions(f, c, i)`. |
| `map-indexed` | `map_indexed` | |
| `seque` | - | |
| `first` | `first` | `None` is not a valid parameter. |
| `ffirst` | `ffirst` | `None` is not a valid parameter. |
| `nfirst` | `nfirst` | |
| `second` | `second` | |
| `nth` | `nth` | |
| `last` | `last` | |
| `rand-nth` | - | Use Python’s `random.choice`. |
| `zipmap` | `zipmap` | |
| `into` | - | |
| `reduce` | - | Use Python’s `functools.reduce`. |
| `set` | - | Use Python’s `set`. |
| `vec` | - | Use Python’s `list`. |
| `into-array` | - | Use Python’s `list`. |
| `to-array-2d` | - | |
| `frequencies` | - | Use Python’s `collections.Counter`. |
| `group-by` | `group_by` | |
| `apply` | - | Use the `f(*args)` construct. |
| `not-empty` | - | |
| `some` | `some` | |
| `seq?` | `is_seq` | |
| `every?` | `every` | |
| `not-every?` | `not_every` | |
| `not-any?` | `not_any` | |
| `empty?` | - | |
| `empty` | `empty` | |
| `doseq` | - | Use `for … in`. |
| `dorun` | `dorun` | |
| `doall` | - | Use Python’s `list`. |
| `realized?` | - | |
| `seq` | - | Use Python’s `list`. |
| `vals` | - | Use Python’s `dict.values`. |
| `keys` | - | Use Python’s `dict.keys`. |
| `rseq` | - | |
| `subseq` | | |
| `rsubseq` | | |
| `repeatedly` | `repeatedly` | |
| `iterate` | `iterate` | |
| `repeat` | `repeat` | `(repeat n x)` becomes `repeat(x, n)`. Equivalent to `itertools.repeat`. |
| `range` | `range` | Prefer Python’s `range` for everything but infinite generators. |
| `line-seq` | - | Loop over an `io.BufferedReader`. |
| `resultset-seq` | - | |
| `re-seq` | - | Use Python’s `re.finditer`. |
| `tree-seq` | `tree_seq` | |
| `file-seq` | - | Use Python’s `os.walk`. |
| `xml-seq` | - | |
| `iterator-seq` | - | |
| `enumeration-seq` | - | |
| `hash-map` | - | Use Python’s `dict`. |
| `array-map` | - | Use Python’s `dict`. |
| `sorted-map` | - | Use `collections.OrderedDict`. |
| `sorted-map-by` | | |
| `hash-set` | - | Use Python’s `set`. |
| `set` | - | Use Python’s `set`. |
| `sorted-set` | | |
| `sorted-set-by` | | |
| `dedupe` | `dedupe` | |
We also implemented `count`, which uses Python’s `len` when possible and fallbacks on a `for` loop for other cases.
### Functions
We also provide miscellaneous functions as well as functions that work on functions.
| Clojure | `clj` | Comment |
|-------------------|:----------------|----------------------------------|
| `identity` | `identity` | |
| `partial` | - | Use Python’s `functools.partial` |
| `comp` | `comp` | |
| `complement` | `complement` | |
| `constantly` | `constantly` | |
| `juxt` | `juxt` | |
| `distinct?` | `is_distinct` | |
| Clojure | `clj` | Comment |
|-------------------|:----------------|----------------------------------|
| `inc` | `inc` | |
| `dec` | `dec` | |