无限可数

时间:2014-04-10 20:56:01

标签: ruby hash generator enumerator

我在运行时使用可用的键构建哈希(因此事先不知道对象的大小)。我希望所有这些值都是类ContestStanding的新实例,但不是完全相同的实例。我用

实现了这个目标
h = Hash.new {|h,k| h[k] = ContestStanding.new}
@my_keys.map {|k| h[k]}
h #=> {1=>#<ContestStanding...>, 2=>#<ContestStanding...>, ...}

我想知道我是否可以使用Enums或Lambdas这样做,如下所示。注意:我已经确认这不起作用。这只是我的思考过程

Hash[@my_keys.zip(-> { ContestStanding.new })]

在这里,问题是我的Lambda不可枚举。 Ruby中有无限生成器吗?

修改

我最初真的被Enumerable#each_with_object方法绊倒了。在块参数中没有看到kh的顺序。以为我疯了!至于你建议的实现,当我在IRB中运行时,这就是我得到的

my_keys = [1,2,3]
my_keys.each_with_object({}) {|k,h| h[k] = 'a'}
#=> {1=>"a", 2=>"a", 3=>"a"}
# The above is what I want to get out of the implementation
Hash[my_keys.zip(Array.new(my_keys.size, Hash.new {|h,k| h[k] = 'a'}))]
#=> {1=>{}, 2=>{}, 3=>{}}

我不是在寻找哈希哈希。这似乎是实现的回归。我想要回来{1=>'a', 2=>'a', 3=>'a'}。有什么想法?

2 个答案:

答案 0 :(得分:3)

当我挖掘Enumerator文档和a similar SO question时,我开始构建自定义枚举。我在Enum块中寻找嵌套的无限循环。最终的代码出现在这个

Hash[@my_keys.zip(Enumerator.new {|y| loop { y << ContestStanding.new}})]

这非常接近我想要的东西!

答案 1 :(得分:1)

布拉德,

以下是两种生成哈希的方法。我将使用以下作为示例:

class ContestStanding
  def checkit
    puts "hi"
  end
end

my_keys = [1,2,3]

使用Enumerable#each_with_object

h = my_keys.each_with_object({}) { |k,h| h[k] = ContestStanding.new }
  #=> {1=>#<ContestStanding:0x000001010efdd8>,
  #    2=>#<ContestStanding:0x000001010efdb0>,
  #    3=>#<ContestStanding:0x000001010efd88>}
h[1].checkit #=> "hi"

each_with_object创建并清空由块参数h引用的数组。传递给块的第一个值(并分配给块参数k)是my_keys.first => 1,所以

h[1] = ContestStanding.new

哈希的其他元素也是类似的。

使用Array.zip

Hash[my_keys.zip(Array.new(my_keys.size) {ContestStanding.new})]
  #=> {1=>#<ContestStanding:0x0000010280f720>,
  #    2=>#<ContestStanding:0x0000010280f6f8>,
  #    3=>#<ContestStanding:0x0000010280f6d0>}

或者,对于Ruby v2.0 +

my_keys.zip(Array.new(my_keys.size) {ContestStanding.new}).to_h
  #=> {1=>#<ContestStanding:0x0000010184bd48>,
  #    2=>#<ContestStanding:0x0000010184bd20>,
  #    3=>#<ContestStanding:0x0000010184bcf8>}

此处执行以下步骤:

a = Array.new(my_keys.size) {ContestStanding.new}
  #=> [#<ContestStanding:0x0000010185b248>,
  #    #<ContestStanding:0x0000010185b220>,
  #    #<ContestStanding:0x0000010185b1f8>]
b = my_keys.zip(a)
  #=> [[1, #<ContestStanding:0x0000010185b248>],
  #    [2, #<ContestStanding:0x0000010185b220>],
  #    [3, #<ContestStanding:0x0000010185b1f8>]]
b.to_h
  #=> {1=>#<ContestStanding:0x0000010185b248>,
  #    2=>#<ContestStanding:0x0000010185b220>,
  #    3=>#<ContestStanding:0x0000010185b1f8>}

您的解决方案

我发现你的解决方案很有趣。这是解释其工作原理的一种方式:

enum = Enumerator.new { |y| loop { y << ContestStanding.new } }
  #=> #<Enumerator: #<Enumerator::Generator:0x000001011a9530>:each>
a1 = my_keys.size.times.with_object([]) { |k,a| a << enum.next }
  #=> [#<ContestStanding:0x000001018820a0>,
  #    #<ContestStanding:0x00000101882028>,
  #    #<ContestStanding:0x00000101881fb0>
a2 = my_keys.zip(a1)
  #=> [[1, #<ContestStanding:0x000001018820a0>],
  #    [2, #<ContestStanding:0x00000101882028>],
  #    [3, #<ContestStanding:0x00000101881fb0>]]
Hash[a2]
  #=> {1=>#<ContestStanding:0x000001018820a0>,
  #    2=>#<ContestStanding:0x00000101882028>,
  #    3=>#<ContestStanding:0x00000101881fb0>}