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

3.2 KiB

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:

>> 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:

>> 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:

>> 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.

>> 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.

>> 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.

>> 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.

>> 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.

>> 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"]]