【问题标题】:Reimplementing Enumerable Map method in Ruby在 Ruby 中重新实现 Enumerable Map 方法
【发布时间】:2013-05-07 15:26:37
【问题描述】:

我正在为一家红宝石店的实习面试做准备。我期待的工作问题之一是重新实现可枚举的方法。

我现在正在尝试实现 map,但我无法弄清楚如何实现没有给出块的情况。

class Array
    def mapp()
      out = []
      if block_given?
        self.each { |e| out << yield(e) }
      else
        <-- what goes here? -->
      end
   out
   end
 end

使用我当前的实现。如果我跑:

[1,2,3,4,5,6].mapp{|each| each+1} #returns => [2,3,4,5,6,7]

但是,我不确定如何获取未传入块的情况:

[1,2,3,4].mapp("cat") # should return => ["cat", "cat", "cat", "cat"]

如果有人能指出我正确的方向。我真的很感激。我尝试查看源代码,但它似乎做的事情与我习惯的完全不同。

static VALUE
enum_flat_map(VALUE obj)
{
VALUE ary;

RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, flat_map_i, ary);

return ary;
}

【问题讨论】:

  • 那是 C 源代码。这对您在 Ruby 中重新实现该方法没有多大帮助。另外,为什么.mapp("cat") 会返回["cat", "cat", "cat"]?那没有意义。内置的.map 方法抛出异常。
  • 此外,这是 flat_map 的代码,而不是 map。老实说,我认为现在使用 Ruby 做工作还为时过早。
  • 也许你可以看看 Rubinius 的来源,例如github.com/rubinius/rubinius/blob/master/kernel/bootstrap/…
  • 忽略 C 代码。除非您申请了非常高级的职位,否则雇主可能只是希望您了解yield。不过,无阻塞实现可能会给你加分!

标签: ruby-on-rails ruby map


【解决方案1】:

我想[1,2,3,4].mapp("cat") 是指[1,2,3,4].mapp{"cat"}

也就是说,没有块的地图返回一个枚举器:

 [1,2,3,4].map
 => #<Enumerator: [1, 2, 3, 4]:map>

这与to_enum的输出相同

[1,2,3,4].to_enum
 => #<Enumerator: [1, 2, 3, 4]:each> 

所以在你的代码中,你只想调用 to_enum:

class Array
    def mapp()
        out = []
        if block_given?
            self.each { |e| out << yield(e) }
        else
            out = to_enum :mapp
        end
        out
    end
end

【讨论】:

  • 谢谢!我觉得自己很蠢,但这回答了我的问题。
  • @hhoang 很高兴我能帮到你
  • @JörgWMittag 你有什么建议?
  • 返回mapp 的枚举器而不是each,就像其他答案一样。
  • @JörgWMittag 谢谢,已修复。
【解决方案2】:
return to_enum :mapp unless block_given?    

应该足够了。

查看完全用 Ruby 实现的 Rubinius 实现的 map 的实现:

https://github.com/rubinius/rubinius/blob/master/kernel/bootstrap/array19.rb

# -*- encoding: us-ascii -*-

class Array
  # Creates a new Array from the return values of passing
  # each element in self to the supplied block.
  def map
    return to_enum :map unless block_given?
    out = Array.new size

    i = @start
    total = i + @total
    tuple = @tuple

    out_tuple = out.tuple

    j = 0
    while i < total
      out_tuple[j] = yield tuple.at(i)
      i += 1
      j += 1
    end

    out
  end
end

【讨论】:

    【解决方案3】:

    rubinius 有一个尽可能用 ruby​​ 编写的 ruby​​ 实现。您可以查看他们的 enumerable.#collect

    代码

    有趣的是区别

    【讨论】:

      【解决方案4】:
      class Array
        def map!
          return to_enum :map! unless block_given?
          self.each_with_index { |e, index| self[index] = yield(e) }
        end
      end
      

      【讨论】:

        【解决方案5】:

        查看Object#to_enum的文档

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-12-14
          • 1970-01-01
          • 2012-09-05
          • 1970-01-01
          • 2013-03-18
          • 2020-02-14
          • 2014-01-22
          • 2011-07-11
          相关资源
          最近更新 更多