38

I find that Mathematica has the iterator data structure. We can build some iterators like this:

Constant iterator

GeneralUtilities`ConstantIterator[5]

Key value iterator

GeneralUtilities`KeyValueIterator[<|a -> x, b -> y, c -> z|>]

Range iterator

GeneralUtilities`RangeIterator[9]

We can find all iterator functions with

Names["GeneralUtilities`*Iterator*"]

But I don't know how to process the iterator, as Mathematica has no function like next on Python:

>>> string='FhC'
>>> it=iter(string)
>>> next(it)
'F'
>>> next(it)
'h'

Update

Michael E2's answer clear this problem a lot,But I hope the DelegateIterator, IteratorGraph, JoinMapIterator, SingletonIterator, StreamIterator, TerminatorIterator and ToIterator can be discussed still.

Anton Antonov
  • 37,787
  • 3
  • 100
  • 178
yode
  • 26,686
  • 4
  • 62
  • 167
  • Re update: Their code can be inspected and it's fairly easy to understand, since it's so short. – Michael E2 Jan 22 '17 at 21:08
  • @MichaelE2 It's seem this seven function little complicate.You mean you understand it all? – yode Jan 22 '17 at 21:18
  • I have no need to understand it all, but I can read, if ever I need them. But it seems they are under development (see comments to my answer), so I probably won't base code on them at this point. At least not code that I expect to use more than once. (Stuff in GeneralUtilities` sometimes changes.) – Michael E2 Jan 22 '17 at 21:25

1 Answers1

32

Such functions set up a one-time iterator, i.e. a GeneralUtilities`Iterator. Its functionality may be inspected with

? GeneralUtilities`Iterator

and so forth. Except for a ConstantIterator which always returns the same value (forever), they go sequentially through values until they return GeneralUtilities`IteratorExhausted.

The main (if internal) utility is GeneralUtilities`PackageScope`PullIterator:

foo = GeneralUtilities`KeyValueIterator[<|a -> x, b -> y, c -> z|>];

GeneralUtilities`PackageScope`PullIterator@foo
GeneralUtilities`PackageScope`PullIterator@foo
GeneralUtilities`PackageScope`PullIterator@foo
GeneralUtilities`PackageScope`PullIterator@foo
(*
  a -> x
  b -> y
  c -> z
  IteratorExhausted
*)

One can see from inspection with ?... that one can do such things as Map, Scan, Fold, Read, etc., over iterators:

foo = GeneralUtilities`KeyValueIterator[<|a -> x, b -> y, c -> z|>];
Map[f, foo]
(*  {f[a -> x], f[b -> y], f[c -> z]}  *)

Normal converts them to a list:

foo = GeneralUtilities`KeyValueIterator[<|a -> x, b -> y, c -> z|>];
Normal[foo]
(*  {a -> x, b -> y, c -> z}  *)

Other functionality can be inspected with ?.

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
Michael E2
  • 235,386
  • 17
  • 334
  • 747
  • 6
    +1. It would appear that in V11 Map, Scan, Fold and Select are all eager. This limits the usefulness of iterators (e.g. Map[10 #&, ConstantIterator[3]] is a bad idea). I guess we'll need to wait for Leonid's streams to hit prime time :) Incidentally, Read is the analog of Python's next. – WReach Jan 22 '17 at 05:50
  • @WReach
    1. Does it matter for Fold?
    2. I wish you had referenced your discussion at

    http://mathematica.stackexchange.com/questions/838/functional-style-using-lazy-lists

    – Alan Jan 22 '17 at 06:01
  • 1
    @Alan No, it does not matter for Fold (I was thinking FoldList). I suppose Scan is arguable as well since it is executed for side-effects. Neither would be a good idea on an infinite stream. – WReach Jan 22 '17 at 06:26
  • +1, good exposure of internal stuff :). There is a hope that at some point soon iterators will get a standardized treatment in WL. – Leonid Shifrin Jan 22 '17 at 14:03
  • 3
    @WReach Hopefully soon now. Have to say though, that LazyList / its iteration process is only coarse-grained lazy, and works in chunks, where operations within a single chunk are eager. This is done for performance, in the first place. The user can in principle control the chunk size, but setting it to 1 will lead to really large framework overhead. – Leonid Shifrin Jan 22 '17 at 14:05