4.60. kink/iter/IFUN

Provides operations for ifuns = iterator functions. An ifun represents the pointer to a specific position of a finite or infinite stream of elements.

An ifun is a 2ary fun, which takes parameters $proc and $fin. The ifun must tail-call $proc if it has an element, or $fin if it has no element.

The $proc is a 2ary fun, which takes parameters Head and $tail. The ifun passes its first element as an argument for Head, and passes the *next* ifun, which points to elements after Head, as an argument for $tail.

The $fin is a thunk, which is tail-called when the ifun has no element.

In the next program, make_vec_ifun makes an ifun which iterates over the elements of Vec.

:make_vec_ifun <- {(:Vec)
  {(:proc :fin)
    if(! Vec.empty?
      { :Head = Vec.get(0)
        :tail = make_vec_ifun(Vec.drop_front(1))
        proc(Head $tail)
      }
      { fin }
    )
  }
}

Let's make an ifun using make_vec_ifun.

:nums_ifun = make_vec_ifun([100 200 300])

For example, you can iterate over the elements of $ifun_x using IFUN.each.

IFUN.each(
  $nums_ifun
  {(:N) dump(N) }
)
# Output:
#   100
#   200
#   300

Or you can make another ifun mapping the elements by a transforming fun.

:doubled_ifun = IFUN.map($nums_ifun {(:N) N * 2 })
IFUN.each(
  $doubled_ifun
  {(:N) dump(N) }
)
# Output:
#   200
#   400
#   600

Or you can accumulate the elements.

:Sum = IFUN.fold($nums_ifun 0 {(:X :Y) X + Y })
dump(Sum) # => 600

4.60.1. IFUN.each($ifun $on_elem)

Calls $on_elem for each elements of $ifun, then return the last result of $on_elem.

If $ifun has no elements, it returns nada.

4.60.2. IFUN.map($ifun $transform)

Returns an ifun whose nth element is transform(nth-element-of-$ifun).

4.60.3. IFUN.concat_map($ifun $transform_to_ifun)

Returns an ifun which contains elements of ifuns which are results of transform_to_ifun(element-of-$ifun).

4.60.4. IFUN.filter($ifun $include?)

Filters elements of $ifun by the predicate $include?.

The result ifun only contains elements of $ifun which satisfy $include?. The order of elements are not changed.

4.60.5. IFUN.count($ifun $counted?)

Returns the number of elements of $ifun, which sattisfy $counted?.

4.60.6. IFUN.fold($ifun Init $combine)

Accumulates elements of $ifun seeded by Init.

If the elements of $ifun is E1, E2, ,,, En_1, En, this fun returns combine(combine(,,,combine(combine(Init E1) E2),,, En_1) En).

If $ifun is empty, this fun returns Init.

4.60.7. IFUN.reduce($ifun $combine)

Accumulates elements of $ifun mutually, then returns a maybe list.

If $ifun has elements E1, E2, E3, ,,, En_1, En, this fun returns [combine(combine(,,,combine(combine(E1 E2) E3),,, En_1) En)].

If $ifun has a single element E, this fun returns [E].

If $ifun is empty, this fun returns [].

4.60.8. IFUN.scan($ifun Init $combine)

Returns an ifun of accumulated vals, seeded by Init.

If $ifun points to finite n elements E(1), E(2), ,,, E(n), the result ifun points to finite n+1 elements R(0), R(1), R(2), ,,, R(n).

If $ifun points to infinite elements E(1), E(2), ,,, , the result ifun points to infinite elements R(0), R(1), R(2), ,,, .

In both cases, R(i) is given as below:

For i=0, R(0) = Init.

For i>=1, R(i) = combine(R(i - 1) E(i)).

4.60.9. IFUN.scan_inside($ifun $combine)

Returns an ifun of vals accumulated inside $ifun.

If $ifun points to no elements, the result ifun points to no elements.

If $ifun points to finite n (n >= 1) elements E(1), E(2), ,,, E(n), the result ifun points to finite n elements R(1), R(2), ,,, R(n).

If $ifun points to infinite elements E(1), E(2), ,,, , the result ifun points to infinite elements R(1), R(2), ,,, .

In the latter two cases, R(i) is given as below:

For i=1, R(1) = E(1).

For i>=2, R(i) = combine(R(i - 1) E(i)).

4.60.10. IFUN.take_front($ifun N)

Returns an ifun containing the first N elements of $ifun, or all the elements if $ifun has less than N elements.

4.60.11. IFUN.drop_front($ifun N)

Returns an ifun omitting the first N elements of $ifun, or an empty ifun if $ifun has less than N elements.

4.60.12. IFUN.take_while($ifun $include?)

Returns an ifun of the longest initial prefix of $ifun, whose elements all satisfy $include?.

4.60.13. IFUN.drop_while($ifun $exclude?)

Returns an ifun after the longest initial prefix of $ifun, whose elements all satisfy $exclude?.

4.60.14. IFUN.all?($ifun $ok?)

Returns true if all the elements of $ifun satisfy $ok?. Returns false if at least one element of $ifun does not satisfy $ok?.

If $ifun is infinite, this fun is not guaranteed to return.

4.60.15. IFUN.any?($ifun $ok?)

Returns true if at least one element of $ifun satisfies $ok?. Returns false if all the elements of $ifun does not satisfy $ok?.

If $ifun is infinite, this fun is not guaranteed to return.

4.60.16. IFUN.chunk($ifun Chunk_size)

Returns an ifun of chunks with the Chunk_size.

For example, if $ifun contains E1, E2, E3, E4, E5, E6, E7, and Chunk_size is 2, this fun returns an ifun containing [E1 E2], [E3 E4] and [E5 E6]. The last E7 is ignored because there are not enough number of elements as a chunk.

Chunk_size must be an int num, and >= 1.

4.60.17. IFUN.concat($ifun_of_ifuns)

Returns an ifun which concatenates the ifuns contained in $ifun_of_ifuns. $ifun_of_ifuns should be an ifun whose elements are also ifuns.

4.60.18. IFUN.fetcher_thunk($ifun)

Makes a stateful thunk to fetch each element of the $ifun.

When the result thunk is called, it returns a list containing the current element and go to next, when there are remaining elements. Otherwise, it returns an empty list.

For example:

:ifun <- IFUN.of('foo' 'bar' 'baz')
:fetch <- IFUN.fetcher_thunk($ifun)
print_line(fetch.repr)  # => ["foo"]
print_line(fetch.repr)  # => ["bar"]
print_line(fetch.repr)  # => ["baz"]
print_line(fetch.repr)  # => []
print_line(fetch.repr)  # => []

4.60.19. IFUN.of(...Elems)

Returns an ifun containing the Elems.

4.60.20. IFUN.chain(...Ifuns)

`chain` makes an ifun concatenating the elements of each ifun of `Ifuns`.

Precondition:

• Each element of `Ifuns` must be an ifun.

The result ifun produces the elements of the first ifun in the same order, and then the elements of the second ifun in the same order, and so forth.

Example:

:IFUN.require_from('kink/iter/')

:Chained <- IFUN.chain(
  IFUN.of(1 2 3)
  IFUN.of(4 5 6)
  IFUN.of(7 8 9)
)
IFUN.each(Chained){(:N)
  stdout.print_line(N.repr)
} # => 1 2 3 4 5 6 7 8 9

4.60.21. IFUN.zip($ifun ...Other_ifuns)

Returns an ifun zpping the given ifuns.

For example, an ifun X has elements X1, X2, X3, ,,, , Y has elements Y1, Y2, Y3, ,,, , and Z has elements Z1, Z2, Z3, ,,, , zip(X Y Z) returns an ifun containing [X1 Y1 Z1], [X2 Y2 Z2], [X3 Y3 Z3], ,,, .

The size of the result ifun is the minimum of the size of the arg ifuns.

4.60.22. IFUN.cycle($make_ifun)

Returns an ifun which concatenates elements of ifuns created by $make_ifun thunk.

If $make_ifun makes an empty ifun, the result ifun is terminated there.

4.60.23. IFUN.lazy($make_ifun)

Returns an ifun, whose elements are equal to the ifun returned by $make_ifun.

4.60.24. IFUN.from_generator($body)

`from_generator` makes an ifun of elements yielded in $body.

$body must take an unary fun $yield as the arg, When $yield is called with vals in invocation of $body, those vals will be the elements of the result ifun.

For example:

:IFUN.require_from('kink/iter/')
:ifun <- IFUN.from_generator{(:yield)
  yield('foo')
  yield('bar')
  yield('baz')
}
IFUN.each($ifun){(:Str)
  stdout.print_line(Str)
}
# Output:
#   foo
#   bar
#   baz

A generator is stackfull. You can call $yield from another fun.

:IFUN.require_from('kink/iter/')
:ifun <- IFUN.from_generator{(:yield)
  yield('foo')
  bar($yield)
  yield('baz')
}
:bar <- {(:yield)
  yield('bar')
}
IFUN.each($ifun){(:Str)
  stdout.print_line(Str)
}
# Output:
#   foo
#   bar
#   baz

4.60.25. IFUN.from_each(Eacher)

`from_each` makes an ifun of elements which is enumerated in Eacher.each.