Issue #14423 has been updated by zverok (Victor Shepelev).


> All those conditions are quite different.

Well, you can say that next page fetching "converges" up to "no more pages left" (which in some APIs represented by infinite repetition of the same page, while in other with an empty page), and job queue "converges" to empty queue.

But the more I think about it, the more I feel like just "infinite sequence" would be quite enough. You can make the "converger" or anything from the infinite enumerator, with `take_while` and other nice `Enumerable` stuff.

----------------------------------------
Feature #14423: Enumerator from single object
https://bugs.ruby-lang.org/issues/14423#change-70026

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
Sometimes (or rather often), there is a programming pattern of "start from one object, do something, look at the result, do the same, look at the result (and so on)". 

Examples:

* fetch page by URL, if pagination present, fetch next page;
* take 10 jobs from the queue, process them, exit when queue is empty;

Typically, those are represented by `while` or `loop` + `break` which somehow feels "not functional enough", and even "not Ruby enough", so much less expressive than `map` and other `Enumerable`/`Enumerator`-based cycles.

In some functional languages or libraries, there is function named `FixedPoint` or `converge`, whose meaning is "take an initial value, repeat the block provided on the result on prev computation, till it will not 'stable'". I believe this notion can be useful for Rubyists too.

Reference implementation (name is disputable!):

```ruby
class Object
  def converge(&block)
    Enumerator.new { |y|
      prev = self
      y << self
      loop do
        cur = block.call(prev)
        raise StopIteration if cur == prev
        y << cur
        prev = cur
      end
    }
  end
end
```

Examples of usage:

```ruby
# Functional kata: find the closest number to sqrt(2):
1.0.converge { |x| (x + 2 / x) / 2 }.to_a.last # => 1.414213562373095
Math.sqrt(2) # => 1.4142135623730951

# Next page situation:
get(url).converge { |page| page.next } 
# => returns [page, page.next, page.next.next, ...til the result is nil, or same page repeated]

# Job queue situation:
queue.top(10).converge { |jobs| 
  jobs.each(&:perform)
  queue.top(10) 
}
# => takes top 10 jobs, till queue is empty (`[]` is returned two successful times)

# Useful for non-converging situations, too:
2.converge { |x| x ** 2 }.take(4)
# => [2, 4, 16, 256]

# Idiomatic Fibonacci:
[0, 1].converge { |f0, f1| [f1, f0 + f1] }.take(10).map(&:first)
# => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
```

Reference to similar things:

* Clojure [iterate](https://clojuredocs.org/clojure.core/iterate) "Returns a lazy sequence of `x`, `(f x)`, `(f (f x))` etc." No converging, just infinite sequence... And maybe that is even more basic and idiomatic. The name is nice, too.
* WolframLang [FixedPoint](http://reference.wolfram.com/language/ref/FixedPoint.html)
* Ramda [converge](http://ramdajs.com/docs/#converge)
* Elixir [Stream#unfold](https://hexdocs.pm/elixir/Stream.html#unfold/2) (ends iteration when `nil` is returned)
* Scala [Iterator#iterate](https://www.scala-lang.org/api/current/scala/collection/Iterator$.html#iterate%5BT%5D%28start%3AT%29%28f%3AT%3D%3ET%29%3AIterator%5BT%5D) (just infinite sequence)

Possible call-seq:

* If converges: `Object#converge(&block)`, `Enumerator.converge(object, &block)`;
* If just an infinite sequence: `Object#iterate(&block)`, `Object#deduce(&block)` (as opposed to `reduce`), `Enumerator.iterate(object, &block)`, `Enumerator#enumerate(object, &block)`.

WDYT?..

PS: Can imagine somebody already proposed that, yet can't find nothing similar in the tracker for all keywords I've tried.




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>