Issue #14423 has been updated by zverok (Victor Shepelev).
This one can be closed (`Enumerator#produce` was implemented in 2.7)
----------------------------------------
Feature #14423: Enumerator from single object
https://bugs.ruby-lang.org/issues/14423#change-82732
* 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:[email protected]?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.