Skip to content

Latest commit

 

History

History
966 lines (866 loc) · 20.1 KB

lazy.md

File metadata and controls

966 lines (866 loc) · 20.1 KB

lazy.elv

  1. testing-status
  2. make-iterator
  3. is-iterator
  4. init
  5. to-iter
  6. cycle
  7. iterate
  8. nums
  9. repeatedly
  10. repeat
  11. prepend
  12. take
  13. drop
  14. rest
  15. reductions
  16. each
  17. map
  18. map-indexed
  19. keep
  20. filter
  21. remove
  22. interleave
  23. interpose
  24. unique
  25. take-while
  26. drop-while
  27. partition
  28. partition-all
  29. take-nth
  30. drop-last
  31. butlast
  32. keep-indexed
  33. blast
  34. first
  35. second
  36. nth
  37. some
  38. first-pred
  39. every

testing-status

126 tests passed out of 126

100% of tests are passing

This module allows you to express infinite sequences. Typically you start by providing input to a generator, then pipe them into any number of iterators, and finally pipe that to a consumer.

Iterator structure


make-iterator

Iterators have five zero-arity functions:

  • init: performs any initialization steps.

  • step: advances iteration to the next value.

  • curr: outputs the next value.

  • done: returns a boolean. true if the iterator has been exhusted

inf-iterator & nest-iterator are convenience wrappers around make-iterator.

make-iterator
▶ [&curr=<closure 0xc0008a4900> &step=<closure 0xc0008a49c0> &done=<closure 0xc0008a4a80> &init=<closure 0xc0008a4840>]
make-iterator &init={ } &curr={ } &step={ } &done={ }[&curr=<closure 0xc000adc0c0> &step=<closure 0xc000adc180> &done=<closure 0xc000adc240> &init=<closure 0xc000adc000>]

is-iterator

Simple predicate for iterators. Runs done to be sure it returns a bool.

All of the iterators satisfy this predicate.

range 10 | to-iter
▶ [&curr=<closure 0xc000862540> &step=<closure 0xc000862600> &done=<closure 0xc0008626c0> &init=<closure 0xc000862480>]
cycle a b c
▶ [&curr=<closure 0xc000a558c0> &step=<closure 0xc000a55980> &done=<closure 0xc000a55a40> &init=<closure 0xc000a55800>]
iterate $base:inc~ (num 0)[&curr=<closure 0xc000becb40> &step=<closure 0xc000becc00> &done=<closure 0xc000beccc0> &init=<closure 0xc000beca80>]
nums
▶ [&curr=<closure 0xc000b68240> &step=<closure 0xc000b68300> &done=<closure 0xc000b68600> &init=<closure 0xc000b68180>]
repeatedly { randint 100 }[&curr=<closure 0xc000b689c0> &step=<closure 0xc0008a4d80> &done=<closure 0xc000b68a80> &init=<closure 0xc0008a4c00>]
repeat (randint 100)[&curr=<closure 0xc000832480> &step=<closure 0xc0008a4d80> &done=<closure 0xc000832540> &init=<closure 0xc0008a4c00>]
to-iter d e f | prepend [a b c][&curr=<closure 0xc000b69200> &step=<closure 0xc000b692c0> &done=<closure 0xc000b69440> &init=<closure 0xc000b69380>]
range 10 | to-iter | take 5
▶ [&curr=<closure 0xc000b452c0> &step=<closure 0xc000b45440> &done=<closure 0xc000b45a40> &init=<closure 0xc000b458c0>]
cycle a b c | reductions $base:append~ [][&curr=<closure 0xc000448cc0> &step=<closure 0xc000448fc0> &done=<closure 0xc000449380> &init=<closure 0xc000449080>]
use str; nums &start=(num 65) | each $str:from-codepoints~[&curr=<closure 0xc000448180> &step=<closure 0xc000448480> &done=<closure 0xc000449c80> &init=<closure 0xc0004495c0>]
nums | keep {|n| if (base:is-even $n) { put $n }}[&curr=<closure 0xc00056e840> &step=<closure 0xc00056e900> &done=<closure 0xc00056ea80> &init=<closure 0xc00056e9c0>]
nums | filter $base:is-even~[&curr=<closure 0xc000bd4f00> &step=<closure 0xc000bd5080> &done=<closure 0xc000bd5200> &init=<closure 0xc000bd5140>]
nums | remove $base:is-even~[&curr=<closure 0xc00056f8c0> &step=<closure 0xc00056f980> &done=<closure 0xc00056fb00> &init=<closure 0xc00056fa40>]
map $'+~' (to-iter (range 10)) (to-iter (range 10))[&curr=<closure 0xc0007fe600> &step=<closure 0xc0007fe840> &done=<closure 0xc0007feb40> &init=<closure 0xc0007fe3c0>]
nums &start=10 &step=10 | map-indexed $'*~'[&curr=<closure 0xc0000e1080> &step=<closure 0xc0000e1140> &done=<closure 0xc0000e1200> &init=<closure 0xc0000e0fc0>]
range 10 | to-iter | drop 5
▶ [&curr=<closure 0xc000a55500> &step=<closure 0xc000a555c0> &done=<closure 0xc000a55b00> &init=<closure 0xc000a55740>]
interleave (to-iter a b c) (to-iter 1 2 3)[&curr=<closure 0xc0008623c0> &step=<closure 0xc000862780> &done=<closure 0xc000862840> &init=<closure 0xc000862300>]
interpose , (range 10 | to-iter )[&curr=<closure 0xc000bec000> &step=<closure 0xc000bec0c0> &done=<closure 0xc000bec240> &init=<closure 0xc000bec180>]
unique (to-iter a b b c c c a a a a d)[&curr=<closure 0xc0000e1c80> &step=<closure 0xc0000e1d40> &done=<closure 0xc0000e1ec0> &init=<closure 0xc0000e1e00>]
unique (to-iter a b b c c c a a a a d) &count=$true[&curr=<closure 0xc000bed080> &step=<closure 0xc000bed140> &done=<closure 0xc000bed200> &init=<closure 0xc000becfc0>]
nums | take-while {|n| < $n 5}[&curr=<closure 0xc001199740> &step=<closure 0xc001199800> &done=<closure 0xc000aea240> &init=<closure 0xc001199a40>]
nums | drop-while {|n| < $n 5}[&curr=<closure 0xc000b45e00> &step=<closure 0xc000b45ec0> &done=<closure 0xc000263440> &init=<closure 0xc000263380>]
nums &stop=12 | partition 3
▶ [&curr=<closure 0xc000af8840> &step=<closure 0xc000af8a80> &done=<closure 0xc000af8b40> &init=<closure 0xc000af86c0>]
nums &stop=13 | partition-all 3
▶ [&curr=<closure 0xc000add080> &step=<closure 0xc000add140> &done=<closure 0xc000add200> &init=<closure 0xc000adcfc0>]
nums &stop=50 | take-nth 5
▶ [&curr=<closure 0xc000e85a40> &step=<closure 0xc000c8cf00> &done=<closure 0xc000c8db00> &init=<closure 0xc000c8da40>]
nums &stop=10 | drop-last 5
▶ [&curr=<closure 0xc000c8dec0> &step=<closure 0xc000bd4480> &done=<closure 0xc000bd4780> &init=<closure 0xc000bd4600>]
nums &stop=5 | butlast
▶ [&curr=<closure 0xc000739d40> &step=<closure 0xc0003aa6c0> &done=<closure 0xc0003aab40> &init=<closure 0xc0003aa780>]
to-iter a b c d e f g | keep-indexed {|i x| put [$i $x]} &pred=(fun:comp $base:first~ $base:is-odd~)[&curr=<closure 0xc000b13440> &step=<closure 0xc000b135c0> &done=<closure 0xc000b13bc0> &init=<closure 0xc000b138c0>]

init

The init function means that iterators should "start over" from the beginning.

   var iter = (range 10 | to-iter)
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (cycle a b c)
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (iterate $base:inc~ (num 0))
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (nums)
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (repeatedly { put x })
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (repeat (randint 100))
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (to-iter d e f | prepend [a b c])
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (range 10 | to-iter | take 5)
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (cycle a b c | reductions $base:append~ [])
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   use str
   var iter = (nums &start=(num 65) | each $str:from-codepoints~)
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (nums | keep {|n| if (base:is-even $n) { put $n }})
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (nums | filter $base:is-even~)
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (nums | remove $base:is-even~)
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (map $'+~' (to-iter (range 10)) (to-iter (range 10)))
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (nums &start=10 &step=10 | map-indexed $'*~')
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (range 10 | to-iter | drop 5)
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (interleave (to-iter a b c) (to-iter 1 2 3))
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (interpose , (range 10 | to-iter ))
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (unique (to-iter a b b c c c a a a a d))
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (unique (to-iter a b b c c c a a a a d) &count=$true)
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (nums | take-while {|n| < $n 5})
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (nums | drop-while {|n| < $n 5})
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (nums &stop=12 | partition 3)
   eq (take 10 $iter | blast | fun:listify) (take 10 $iter | blast | fun:listify)
   
   var iter = (nums &stop=13 | partition-all 3)
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (nums &stop=50 | take-nth 5)
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (nums &stop=10 | drop-last 5)
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var iter = (nums &stop=5 | butlast)
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
   var pred = (fun:comp $base:first~ $base:is-odd~)
   var iter = (to-iter a b c d e f g | keep-indexed {|i x| put [$i $x]} &pred=$pred)
   eq (blast $iter | fun:listify) (blast $iter | fun:listify)
   
$true

Generators


to-iter

Simplest generator. Transforms an "array" to an iterator.

to-iter (range 10) | blast
range 10 | to-iter | blast
▶ 0
▶ 1
▶ 2
▶ 3
▶ 4
▶ 5
▶ 6
▶ 7
▶ 8
▶ 9

cycle

cycles an "array" infinitely.

cycle a b c | take 10 | blast
put a b c | cycle | take 10 | blast
▶ a
▶ b
▶ c
▶ a
▶ b
▶ c
▶ a
▶ b
▶ c
▶ a

iterate

Returns an "array" of n, f(n), f(f(n)), etc.

iterate $base:inc~ (num 0) | take 10 | blast
▶ 0
▶ 1
▶ 2
▶ 3
▶ 4
▶ 5
▶ 6
▶ 7
▶ 8
▶ 9

nums

With no options, starts counting up from 0.

nums | take 10 | blast
▶ 0
▶ 1
▶ 2
▶ 3
▶ 4
▶ 5
▶ 6
▶ 7
▶ 8
▶ 9

You can tell it to start at a specific value.

nums &start=10 | take 10 | blast
▶ 10
▶ 11
▶ 12
▶ 13
▶ 14
▶ 15
▶ 16
▶ 17
▶ 18
▶ 19

You can specify a step value.

nums &step=2 | take 5 | blast
▶ 0
▶ 2
▶ 4
▶ 6
▶ 8

It can be negative.

nums &step=-1 | take 10 | blast
▶ 0
▶ -1
▶ -2
▶ -3
▶ -4
▶ -5
▶ -6
▶ -7
▶ -8
▶ -9

Stop values can also be provided, although they offer little value over range.

nums &stop=10 | blast
▶ 0
▶ 1
▶ 2
▶ 3
▶ 4
▶ 5
▶ 6
▶ 7
▶ 8
▶ 9

nums returns nothing if the inputs make no sense.

nums &step=-1 &stop=10 | blast

MATCHES EXPECTATIONS: [nothing]


repeatedly

Takes a zero-arity function and calls it infinitely.

repeatedly { randint 100 } | take 5 | blast
▶ 64
▶ 58
▶ 63
▶ 32
▶ 96

repeat

Returns x infinitely

repeat x | take 5 | blast
▶ x
▶ x
▶ x
▶ x
▶ x

High-level iterators


prepend

Prepends a list to an iterator

to-iter d e f | prepend [a b c] | blast
▶ a
▶ b
▶ c
▶ d
▶ e
▶ f

take

Like builtin:take but for iterators.

cycle a b c | take 10 | blast
put a b c | cycle | take 10 | blast
▶ a
▶ b
▶ c
▶ a
▶ b
▶ c
▶ a
▶ b
▶ c
▶ a

Exceeding the length of a nested iterator is handled gracefully.

range 5 | to-iter | take 20 | blast
▶ 0
▶ 1
▶ 2
▶ 3
▶ 4

drop

Like builtin:drop but for iterators.

range 10 | to-iter | drop 5 | blast
▶ 5
▶ 6
▶ 7
▶ 8
▶ 9

Dropping more than the nested iterator is handled gracefully.

range 10 | to-iter | drop 20 | blast

MATCHES EXPECTATIONS: [nothing]


rest

Drops the first element from the iterator.

range 10 | to-iter | drop 5 | rest | blast
▶ 6
▶ 7
▶ 8
▶ 9

reductions

Like fun:reductions, but works with iterators.

cycle a b c | reductions $base:append~ [] | take 5 | blast
▶ [][a][a b][a b c][a b c a]

each

Like builtin:each, but works with iterators.

use str; nums &start=(num 65) | each $str:from-codepoints~ | take 3 | blast
▶ A
▶ B
▶ C

map

Like each, but works with multiple iterators.

map $'+~' (to-iter (range 10)) (to-iter (range 10)) | take 5 | blast
▶ 0
▶ 2
▶ 4
▶ 6
▶ 8

Can work like each, but you should avoid this because it is less performant.

use str; nums &start=(num 65) | map $str:from-codepoints~ | take 3 | blast
▶ A
▶ B
▶ C

map-indexed

Returns a sequence of (f index element).

nums &start=10 &step=10 | map-indexed $'*~' | take 5 | blast
▶ 0
▶ 20
▶ 60
▶ 120
▶ 200

keep

Returns result of (f x) when it's non-nil & non-empty.

Notice how these two results are different depending on where you place the take.

nums | take 10 | keep {|n| if (base:is-even $n) { put $n }} | blast
▶ 0
▶ 2
▶ 4
▶ 6
▶ 8
nums | keep {|n| if (base:is-even $n) { put $n }} | take 10 | blast
▶ 0
▶ 2
▶ 4
▶ 6
▶ 8
▶ 10
▶ 12
▶ 14
▶ 16
▶ 18

filter

Returns x when (f x) is non-empty & truthy.

nums | filter $base:is-even~ | take 5 | blast
▶ 0
▶ 2
▶ 4
▶ 6
▶ 8

remove

Returns x when (complement (f x)) is non-empty & truthy.

nums | remove $base:is-even~ | take 5 | blast
▶ 1
▶ 3
▶ 5
▶ 7
▶ 9

interleave

Returns a sequence of the first item in each iterator, then the second, etc.

interleave (to-iter a b c) (to-iter 1 2 3) | blast
▶ a
▶ 1
▶ b
▶ 2
▶ c
▶ 3

Understands when to stop short.

interleave (to-iter a b) (to-iter 1 2 3) | blast
interleave (to-iter a b c) (to-iter 1 2) | blast
▶ a
▶ 1
▶ b
▶ 2

interpose

Returns the elements from the nested iterator, interposed with sep.

interpose , (to-iter a b c) | blast
▶ a
▶ ,
▶ b
▶ ,
▶ c

Needs to elements from iter in order to interpose sep.

interpose , (to-iter a) | blast
▶ a

unique

Like uniq but for iterators.

unique (to-iter a b b c c c a a a a) | blast
▶ a
▶ b
▶ c
▶ a
unique (to-iter a b b c c c a a a a d) | blast
▶ a
▶ b
▶ c
▶ a
▶ d
unique (to-iter a b b c c c a a a a) &count=$true | blast
▶ [a (num 1)][b (num 2)][c (num 3)][a (num 4)]
unique (to-iter a b b c c c a a a a d) &count=$true | blast
▶ [a (num 1)][b (num 2)][c (num 3)][a (num 4)][d (num 1)]

Corner-case test

unique (to-iter a) | blast
▶ a
unique (to-iter a) &count=$true | blast
▶ [a (num 1)]

take-while

Returns elements so long as (f x) returns $true.

nums | take-while {|n| < $n 5} | blast
▶ 0
▶ 1
▶ 2
▶ 3
▶ 4

drop-while

Drops elements until (f x) returns false.

nums | drop-while {|n| < $n 5} | take 5 | blast
▶ 5
▶ 6
▶ 7
▶ 8
▶ 9

partition

partitions an iterator into lists of size n.

nums &stop=12 | partition 3 | blast
▶ [(num 0) (num 1) (num 2)][(num 3) (num 4) (num 5)][(num 6) (num 7) (num 8)][(num 9) (num 10) (num 11)]

Drops items which don't complete the specified list size.

nums &stop=14 | partition 3 | blast
▶ [(num 0) (num 1) (num 2)][(num 3) (num 4) (num 5)][(num 6) (num 7) (num 8)][(num 9) (num 10) (num 11)]

Specify &step=n to specify a "starting point" for each partition.

nums &stop=12 | partition 3 &step=5 | blast
▶ [(num 0) (num 1) (num 2)][(num 5) (num 6) (num 7)]

&step can be < than the partition size.

nums &stop=4 | partition 2 &step=1 | blast
▶ [(num 0) (num 1)][(num 1) (num 2)][(num 2) (num 3)]

When there are not enough items to fill the last partition, a pad can be supplied.

nums &stop=14 | partition 3 &pad=[a] | blast
▶ [(num 0) (num 1) (num 2)][(num 3) (num 4) (num 5)][(num 6) (num 7) (num 8)][(num 9) (num 10) (num 11)][(num 12) (num 13) a]

The size of the pad may exceed what is used.

nums &stop=13 | partition 3 &pad=[a b] | blast
▶ [(num 0) (num 1) (num 2)][(num 3) (num 4) (num 5)][(num 6) (num 7) (num 8)][(num 9) (num 10) (num 11)][(num 12) a b]

...or not.

nums &stop=13 | partition 3 &pad=[] | blast
▶ [(num 0) (num 1) (num 2)][(num 3) (num 4) (num 5)][(num 6) (num 7) (num 8)][(num 9) (num 10) (num 11)][(num 12)]

partition-all

Convenience function for partition which supplies &pad=[].

Use when you don't want everything in the resultset.

nums &stop=13 | partition-all 3 | blast
▶ [(num 0) (num 1) (num 2)][(num 3) (num 4) (num 5)][(num 6) (num 7) (num 8)][(num 9) (num 10) (num 11)][(num 12)]

take-nth

Returns the nth element from the given iterator.

nums &stop=50 | take-nth 5 | blast
▶ 0
▶ 5
▶ 10
▶ 15
▶ 20
▶ 25
▶ 30
▶ 35
▶ 40
▶ 45

drop-last

Drops the last n elements from an iterator.

nums &stop=10 | drop-last 5 | blast
▶ 0
▶ 1
▶ 2
▶ 3
▶ 4

butlast

Drops the last element from an iterator

nums &stop=5 | butlast | blast
▶ 0
▶ 1
▶ 2
▶ 3

keep-indexed

Returns all non-empty & non-nil results of (f index item).

to-iter a b c d e f g | keep-indexed {|i x| if (base:is-odd $i) { put $x } else { put $nil }} | blast
▶ b
▶ d
▶ f

And supply your own predicate.

to-iter a b c d e f g | keep-indexed {|i x| put [$i $x]} &pred=(fun:comp $base:first~ $base:is-odd~) | blast
▶ [(num 1) b][(num 3) d][(num 5) f]

consumers


blast

Simplest consumer. "Blasts" the iterator output to the terminal.

range 10 | to-iter | blast
▶ 0
▶ 1
▶ 2
▶ 3
▶ 4
▶ 5
▶ 6
▶ 7
▶ 8
▶ 9

first

Returns the first element from an iterator.

nums | first
▶ 0

second

Returns the second element from an iterator.

nums | second
▶ 1

nth

Returns the nth element from an iterator

nums | nth 25
▶ 24

some

Returns the first truthy value from (f x).

nums &stop=20 | some {|i| < $i 50}$true
nums &stop=20 | some {|i| > $i 50}$false
nums &stop=20 | some {|i| if (< $i 50) { put $i } }
▶ 0

Might return nothing, if nothing fits.

nums &stop=20 | some {|i| if (> $i 50) { put $i } }

MATCHES EXPECTATIONS: [nothing]


first-pred

Like filter but returns the first value.

nums &stop=20 | first-pred {|i| < $i 50}
▶ 0
nums | first-pred {|i| > $i 50}
▶ 51
nums &stop=20 | first-pred {|i| > $i 50}

MATCHES EXPECTATIONS: [nothing]


every

Returns $true if every element satisfies the predicate. $false otherwise.

nums &stop=20 | every {|i| < $i 50}$true
nums | every {|i| < $i 50}$false