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


@inopinatus I updated the README with [reasoning about name](https://github.com/zverok/object_enumerate#upd-naming). Copying from there:

The reasons behind the name `#enumerate`:

* Core method names should be short and mnemonic, not long and descriptive. (Otherwise, we'd have `yield_each` instead of `each`, `yield_each_and_collect` instead of `map`, `parallel_each` instead of `zip` and don't even get me started on `reduce`). It is if you can't guess what core method _exactly_ does _just_ from the name, more important to have it easily remembered and associative.
* Code constructs using the name should be spellable in your head. I pronounce it "1: enumerate succeeding numbers", "last result: enumerate all next" and so on. Judge yourself.
* Concept is present in other languages and frequently named `iterate` (for example, Clojure and Scala). As we call our iterators `Enumerator`, it is logical to call the method `enumerate`.
* Once you got it, the name is hard to confuse with anything (the only other slightly similar name in core is `#to_enum`, but it is typically used in a very far context).

The only other reasonable option I can think about is `deduce`, as an antonym for `reduce` which makes the opposite thing. Elixir follows this way, calling the methods `fold` (for our `reduce`) and `unfold` (for method proposed).


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

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
**UPD: Current proposal**

Introduce method `Object#enumerate` for producing infinite enumerator by applying block to result of previous call.

Reference implementation:

```ruby
class Object
  def enumerate(&block)
    Enumerator.new { |y|
      val = self
      y << val
      loop do
        val = block.call(val)
        y << val
      end
    }
  end
end
```

Possible usages:
```ruby
# Most idiomatic "infinite sequence" possible:
p 1.enumerate(&:succ).take(5)
# => [1, 2, 3, 4, 5]

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

# Enumerable pagination
page.enumerate { |page| Faraday.get(page.next) if page.next }.take_while { |p| !p.nil? }
```

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)

---

**Initial proposal**

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>