A library for lazy sequences heavily inspired by clojure seq's and stdlib
Goals
- Library for lazy handling of sequences of stuff.
- Common target for other libs that want to support lazy handling.
Design
A seq is a command which:
- when called (expanded) gives a two element list
[list value true]
for the next value or[list {} false]
when exhausted
- the order of the value boolean is choosen to make looping over a seq less verbose.
- can be cleaned up by renaming the command or exhausting the elements
This makes a coroutine a very natural choice to implement a sequence. They can be used to wrap other Tcl iterators such as an TclOO based ones to allow cleanup after the last successful call for a next element.
Because the command is expanded, subcommands such as {cmd next}
are also good choices to implement a seq.
The general pattern with coroutines as also used in the standard library is:
proc seqtypeimpl {arg1 .....} {
# init based on arg1 ......
# return the name
yield [info coroutine]
# yield elements
yield [list $elem true]
# cleanup
return {{} false}
}
proc seqtype {arg1 .....} {
return [::seq::createcoro seqtypeimpl arg1 .....]
}
# loop over a seq
while {[lassign [{*}$seq] val]} { # do something with $val }
It's good practice to use [::seq::seqname]
to create a proc name for the
seq to be created. This also allows easy monitoring of leaked seqs.
Function calls
Whenever a command accepts a function, either a prefix command or a lambda can be passed
The difference can be indicated by the first character. Lambdas will start with &
.
For example:
set l {&x {puts $x}}
Defines a one argument lambda. If a prefix command is passed the command will be expanded and the arguments appended.
Standard Library
first seq
Return the first element from seq
.
seqname
Helper to create a unique seq command name.
The commands names are created in the ::seq::seqs
namespace.
createcoro cmd args
Helper to create a seq command with coroutine backed by impl
. Pass {*}$args
to the
coroutine command.
Uses [seqname]
to generate the seq command name
listseq list
Create a sequence from a list.
lineseq chan
Create a sequence of lines retrieved with [gets]
from chan
.
do f seq
Execute the one argument function f
for every seq
element
map f seq
return a new seq with f
applied to every element of seq
.
take count seq
take the first count
elements from seq
and return them as a list
takeseq count seq
take the first count
elements from seq
and return them as a new seq
dup count seq
duplicate the first count
elements and return the new seq
iterate f x
create an infinite sequence of {x [f x] [f [f x]] ....}
iterate-until pred f x
create an sequence of {x [f x] [f [f x]] ....} stop when pred becomes true the element where pred is true is not included in the sequence
range start end
create a sequence with items start
to end
(inclusive)
into type arg seq
realize the seq into an eager tcl string.
- type = list -> append all elements to the list and return the result
- type = listvar -> append all elements to the list in listvar
cons item seq
prepend item
to the seq
and return the new seq
peek seq
get the first element of seq without removing it NB: this obviously will realize the first element
drop seq
drop the first element of seq
.
filter pred seq
Return a new seq with all elements from seq
for which the predicate pred
is true.
pred
is handled as decribed in the Function Calls section.
reduce start f seq
Reduce the seq with f
on start