talc/docs/src/lang/iterators.md
2024-12-25 12:53:56 -05:00

142 lines
3.2 KiB
Markdown

# Iterators
Iterators are one of the most powerful features of the Talc language and
standard library. An iterator is represented with a zero-argument function.
Every time the function is called it returns the next item in the sequence,
or the special value `:end_iteration` to signal that no more items are
available.
An iterator can be created simply by defining such a function:
```talc
>> n = 0
0
>> fn counting() do
if n == 3 then
return :end_iteration
end
n += 1
n
end
<function counting(0) @5661c434f760>
>> counting()
1
>> counting()
2
>> counting()
3
>> counting()
:end_iteration
>> counting()
:end_iteration
```
Much like lists and ranges, iterators can be used in a `for` loop. In fact,
lists and ranges are converted to iterators automatically by the `for` loop.
This can be done manually using the built-in `iter` function:
```talc
>> lst = [2, 3, 5]
[2, 3, 5]
>> lst_iter = iter(lst)
<native function iter[list](0) @5661c4352ef0>
>> lst_iter()
2
>> lst_iter()
3
>> lst_iter()
5
>> lst_iter()
:end_iteration
```
An iterator can be converted to a list using the `list` function:
```talc
>> lst = [2, 3, 5]
[2, 3, 5]
>> list(iter(lst))
[2, 3, 5]
```
## Iterator functions
The standard library includes many functions to manipulate iterators. Some
essential ones are listed here. Examples will show both conventional call
syntax as well as a version using a pipeline.
All standard library functions will take the iterator as the final argument.
### Map
`map` creates a new iterator from an existing one by passing each element
through a provided function.
```talc
>> list(map(\x -> x^2, [2, 3, 5]))
[4, 9, 25]
>> [2, 3, 5] | map(\x -> x^2) | list
[4, 9, 25]
```
### Filter
`filter` creates a new iterator only including values that, when passed to the
provided function, return a truthy value.
```talc
>> list(filter(\x -> x % 3 != 0, [1, 2, 3, 4, 5, 6]))
[1, 2, 4, 5]
>> [1, 2, 3, 4, 5, 6] | filter(\x -> x % 3 != 0) | list
[1, 2, 4, 5]
```
### Fold
`fold` consumes an iterator, applying a two-argument function repeatedly to its
values.
```talc
>> fold(\x,y -> x*y, [1, 2, 3, 4, 5, 6])
720
>> [1, 2, 3, 4, 5, 6] | fold(\x,y -> x*y)
720
```
`foldi` does the same but allows an initial value to be specified.
```talc
>> foldi(11, \x,y -> x/y, [1, 2, 3, 4, 5, 6])
720
>> [1,2,3,4,5,6] | foldi(11, \x,y -> x/y)
720
```
`sum` and `prod` consume an iterator and return the sum or product of its
elements, respectively.
### Forever and Take
`forever` creates an iterator that returns the given value every time it is
called - an endless iterator. It is useful in combination with `take`, which
creates an iterator that returns only the first *n* elements of its argument.
```talc
>> list(take(10, forever(42)))
[42, 42, 42, 42, 42, 42, 42, 42, 42, 42]
>> forever(42) | take(10) | list
[42, 42, 42, 42, 42, 42, 42, 42, 42, 42]
>> forever(42) | list -- bad idea!
```
### Enumerate
`enumerate` creates an iterator of two-element arrays, the first is the index
(starting from `0`) and the second is the item from the original iterator.
```
>> list(enumerate(["a", "b", "c"]))
[[0, "a"], [1, "b"], [2, "c"]]
>> ["a", "b", "c"] | enumerate | list
[[0, "a"], [1, "b"], [2, "c"]]
```