8

このコードでは、文字列「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は列挙可能であるためxform、文字列の100万要素の配列を作成するのではなく、オンデマンドで「hacker_[N]」形式の文字列を生成して返すだけです。

ちなみに、C#では次のようになります。

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

(1つの解決策)

これは、xformメソッドを追加するEnumeratorの拡張機能です。変換が適用された、元の列挙子の値を反復処理する別の列挙子を返します。

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}
4

4 に答える 4

6

Ruby 2.0が導入されましEnumerable#lazyた。これにより、、などをチェーンしmap、最後に、selectなどでのみ最終結果を生成できます。これは、を使用する任意のRubyバージョンで使用できます。to_afirstrequire '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'

それ以外の場合は、を使用できますEnumerator.new { with_a_block }。Ruby 1.9の新機能なのでrequire 'backports/1.9.1/enumerator/new'、Ruby1.8.xで必要な場合。

あなたの例のように、以下は中間配列を作成せず、必要な文字列のみを作成します。

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(つまり、Infinity)に置き換えることもできます。

コメントに答えるために、常に値を1対1でマッピングしている場合は、次のようになります。

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}"}
于 2010-02-24T18:29:21.733 に答える
1

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の本(DaveThomasによるProgrammingRuby)にもこれに関するセクションがあります。

于 2010-02-24T17:28:50.787 に答える
1
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"

次に行うことは、ブロックなしで呼び出されたときに列挙子を返すことです...

于 2010-02-24T17:46:29.403 に答える
0

リストにはそれぞれの方法があります。

(1..100000).each
于 2010-02-24T16:57:48.890 に答える