与返回 Enumerable 的 Ruby Enumerable.collect 等效吗?

发布于 2024-08-23 00:23:30 字数 1133 浏览 7 评论 0 原文

在这段代码中,我创建了一个字符串“1”到“10000”的数组:

array_of_strings = (1..10000).collect {|i| String(i)}

Ruby Core API 是否提供了一种获取可枚举对象的方法,该对象允许我枚举同一列表,按需生成字符串值,而不是生成字符串数组?

这是一个进一步的示例,希望能够澄清我正在尝试做的事情:

def find_me_an_awesome_username
  awesome_names = (1..1000000).xform {|i| "hacker_" + String(i) }
  awesome_names.find {|n| not stackoverflow.userexists(n) }
end

其中 xform 是我正在寻找的方法。 Awesome_names 是一个 Enumerable,因此 xform 不会创建 100 万个元素的字符串数组,而只是根据需要生成并返回“hacker_[N]”形式的字符串。

顺便说一句,它在 C# 中可能如下所示:

var awesomeNames = from i in Range(1, 1000000) select "hacker_" + i;
var name = awesomeNames.First((n) => !stackoverflow.UserExists(n));

(一个解决方案)

这是 Enumerator 的扩展,添加了 xform 方法。它返回另一个枚举器,该枚举器迭代原始枚举器的值,并对其应用变换。

class Enumerator
  def xform(&block)
    Enumerator.new do |yielder|
      self.each do |val|
        yielder.yield block.call(val)
      end
    end
  end
end

# this prints out even numbers from 2 to 10:
(1..10).each.xform {|i| i*2}.each {|i| puts i}

In this code, I create an array of strings "1" to "10000":

array_of_strings = (1..10000).collect {|i| String(i)}

Does the Ruby Core API provide a way to get an enumerable object that lets me enumerate over the same list, generating the string values on demand, rather than generating an array of the strings?

Here's a further example which hopefully clarifies what I am trying to do:

def find_me_an_awesome_username
  awesome_names = (1..1000000).xform {|i| "hacker_" + String(i) }
  awesome_names.find {|n| not stackoverflow.userexists(n) }
end

Where xform is the method I am looking for.
awesome_names is an Enumerable, so xform isn't creating a 1 million element array of strings, but just generating and returning strings of the form "hacker_[N]" on demand.

By the way, here's what it might look like in C#:

var awesomeNames = from i in Range(1, 1000000) select "hacker_" + i;
var name = awesomeNames.First((n) => !stackoverflow.UserExists(n));

(One Solution)

Here is an extension to Enumerator that adds an xform method. It returns another enumerator which iterates over the values of the original enumerator, with a transform applied to it.

class Enumerator
  def xform(&block)
    Enumerator.new do |yielder|
      self.each do |val|
        yielder.yield block.call(val)
      end
    end
  end
end

# this prints out even numbers from 2 to 10:
(1..10).each.xform {|i| i*2}.each {|i| puts i}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

凯凯我们等你回来 2024-08-30 00:23:30

Ruby 2.0 引入了 Enumerable#lazy< /a> 允许链接 mapselect 等...,并且仅在最后使用 to_a 生成最终结果, first 等...您可以通过 require 'backports/2.0.0/enumerable/lazy' 在任何 Ruby 版本中使用它。

require 'backports/2.0.0/enumerable/lazy'
names = (1..Float::INFINITY).lazy.map{|i| "hacker_" + String(i) }
names.first # => 'hacker_1'

否则,您可以使用 Enumerator.new { with_a_block }。它是 Ruby 1.9 中的新增功能,因此如果您在 Ruby 1.8.x 中需要它,请require 'backports/1.9.1/enumerator/new'

根据您的示例,以下内容不会创建中间数组,而只会构造所需的字符串:

require 'backports/1.9.1/enumerator/new'

def find_me_an_awesome_username
  awesome_names = Enumerator.new do |y|
    (1..1000000).each {|i| y.yield "hacker_" + String(i) }
  end
  awesome_names.find {|n| not stackoverflow.userexists(n) }
end

如果需要,您甚至可以将 100000 替换为 1.0/0 (即无穷大)。

为了回答你的评论,如果你总是将你的价值观一对一地映射,你可能会得到类似的东西:

module Enumerable
  def lazy_each
    Enumerator.new do |yielder|
      each do |value|
        yielder.yield(yield value)
      end
    end
  end
end

awesome_names = (1..100000).lazy_each{|i| "hacker_#{i}"}

Ruby 2.0 introduced Enumerable#lazy which allows one to chain map, select, etc..., and only generate the final results at the end with to_a, first, etc... You can use it in any Ruby version with require 'backports/2.0.0/enumerable/lazy'.

require 'backports/2.0.0/enumerable/lazy'
names = (1..Float::INFINITY).lazy.map{|i| "hacker_" + String(i) }
names.first # => 'hacker_1'

Otherwise, you can use Enumerator.new { with_a_block }. It's new in Ruby 1.9, so require 'backports/1.9.1/enumerator/new' if you need it in Ruby 1.8.x.

As per your example, the following will not create an intermediate array and will only construct the needed strings:

require 'backports/1.9.1/enumerator/new'

def find_me_an_awesome_username
  awesome_names = Enumerator.new do |y|
    (1..1000000).each {|i| y.yield "hacker_" + String(i) }
  end
  awesome_names.find {|n| not stackoverflow.userexists(n) }
end

You can even replace the 100000 by 1.0/0 (i.e. Infinity), if you want.

To answer your comment, if you are always mapping your values one to one, you could have something like:

module Enumerable
  def lazy_each
    Enumerator.new do |yielder|
      each do |value|
        yielder.yield(yield value)
      end
    end
  end
end

awesome_names = (1..100000).lazy_each{|i| "hacker_#{i}"}
执笏见 2024-08-30 00:23:30

听起来您想要一个 Enumerator 对象,但不完全是。

也就是说,Enumerator 对象是一个可用于按需调用 next 的对象(而不是执行整个循环的 each)。 (许多人使用内部迭代器与外部迭代器的语言:each 是内部的,而枚举器是外部的。您驱动它。)

枚举器可能看起来如下:

awesome_names = Enumerator.new do |y|
  number = 1
  loop do
    y.yield number
    number += 1
  end
end

puts awesome_names.next
puts awesome_names.next
puts awesome_names.next
puts awesome_names.next

这里有一个链接,可详细讨论如何使用你可以在 Ruby 中懒惰地使用枚举器: http://www.michaelharrison.ws/weblog/? p=163

Pickaxe 书中也有专门的章节(Dave Thomas 的《Programming Ruby》)。

It sounds like you want an Enumerator object, but not exactly.

That is, an Enumerator object is an object that you can use to call next on demand (rather than each which does the whole loop). (Many people use the language of internal versus external iterators: each is internal, and an Enumerator is external. You drive it.)

Here's how an enumerator might look:

awesome_names = Enumerator.new do |y|
  number = 1
  loop do
    y.yield number
    number += 1
  end
end

puts awesome_names.next
puts awesome_names.next
puts awesome_names.next
puts awesome_names.next

Here's a link, to more discussion of how you might use Enumerators lazily in Ruby: http://www.michaelharrison.ws/weblog/?p=163

There's also a section on this in the Pickaxe book (Programming Ruby by Dave Thomas).

莫相离 2024-08-30 00:23:30
class T < Range
  def each
    super { |i| yield String(i) }
  end
end

T.new(1,3).each { |s| p s }
$ ruby rsc.rb
"1"
"2"
"3"

接下来要做的事情是在没有块的情况下调用时返回一个枚举器......

class T < Range
  def each
    super { |i| yield String(i) }
  end
end

T.new(1,3).each { |s| p s }
$ ruby rsc.rb
"1"
"2"
"3"

The next thing to do is to return an Enumerator when called without a block...

樱娆 2024-08-30 00:23:30

列表有一个each方法:

(1..100000).each

lists have an each method:

(1..100000).each
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文