所以,我正在经历The Odin Project's个Ruby Path,在其中一个项目中,我们必须将Enum个函数中的一些重写为它们的基本功能.问题是,我想try 超越并重新创建尽可能接近原始函数的那些函数,这将我带到Enum#inject.

我使用以下代码(在Enumerable模块中)重新创建了它

def my_inject(initial = first, sym = nil)
  memo = initial

  enum = to_a

  if block_given?
    enum.my_each_with_index do |el, idx|
      next if memo == first && idx.zero?

      memo = yield memo, el
    end
  else
    block = sym.to_proc
    enum.my_each_with_index do |el, idx|
      next if memo == first && idx.zero?

      memo = block.call memo, el
    end
  end
  memo
end

(my_each_with_indexeach_with_index的自定义版本,应该是这样工作的)

这个版本是almost,工作正常.问题是只有当我只用Symbol作为参数来调用它时,就像在((5..10).my_inject(:+))中一样,因为它抛出了'my_inject': undefined method 'to_proc' for nil:NilClass (NoMethodError).

我猜测之所以会发生这种情况,是因为符号作为initial值传递,作为函数的第一个参数.

我想过要try 编写一系列判断(比如判断函数是否只有一个参数,并且该参数是一个符号),但我想知道是否有更简单、更简洁的方法来这样做.

(请记住,我学习代码不超过6个月,所以我在这方面是一个非常新手).

我很感谢你的帮助!

推荐答案

内置的inject非常多态,所以在try 从头开始实现它之前(不查看源代码) 我们需要探索它在不同情况下的表现.我跳过您已经知道的内容(例如,将第一个元素用作 初始值(如果未明确提供等),除此之外:

[1,2,3].inject(0, :+, :foo) #=> ArgumentError: wrong number of arguments (given 3, expected 0..2)
# Max. arity is strict

[1,2,3].inject(0, :+) { 1 } #=> 6
# If both symbol _and_ block are provided, the former dominates

[1,2,3].inject(:+) { |acc, x| acc } #=> :+
# With only 1 parameter and a block the former will be treated as an init value, not a proc.

[1,2,3].inject("+") #=> 6
[1,2,3].inject("+") { |acc, x| acc } #=> "+"
# Strings work too. This is important, because strings _don't respond to `to_proc`_, so we would need smth. else

[1,2,3].inject #=> LocalJumpError: no block given
# Ok, local jump error means that we try to yield in the cases where the 2nd parameter is not provided

[1,2,3].inject(nil) #=> TypeError: nil is not a symbol nor a string
# But if it is provided, we try to do with it something that quacks pretty much like `send`...

考虑到这些内容,我们可以编写如下内容

module Enum
  # Just for convenience, you don't need it if you implement your own `each`
  include Enumerable

  def my_inject(acc = nil, sym = nil)
    # With a single attribute and no block we assume that the init value is in fact nil
    # and what is provided should be "called" on elements somehow
    if acc && !sym && !block_given?
      sym, acc = acc, nil
    end
  
    each do |element|
      if !acc
        # If we are not initialized yet, we just assign an element to our accumulator
        # and proceed
        acc = element
      elsif sym
        # If the "symbol" was provided explicitly (or resolved as such in a single parameter case)
        # we try to call the appropriate method on the accumulator. 
        acc = acc.send(sym, element)
      else
        # Otherwise just try to yield
        acc = yield acc, element
      end
    end
    
    acc
  end
end

听我说,我们快到了:)让我们来看看它是如何嘎嘎作响的:

class Ary < Array
  include Enum
end

ary = Ary.new.push(*[1,2,3])

ary.my_inject #=> LocalJumpError: no block given
ary.my_inject(0) #=> TypeError: 0 is not a symbol nor a string
ary.my_inject("+") #=> 6
ary.my_inject(:+) #=> 6
ary.my_inject(0, :+) #=> 6
ary.my_inject(1, :+) #=> 7
ary.my_inject(1, :+) { 1 } #=> 7
ary.my_inject(:+) { |acc, x| acc } #=> :+

因此,情况大致相同.可能还有其他一些我的实现不能满足的边缘情况,但我把它们留给您:)

Ruby相关问答推荐

Rbenv说未安装已安装的版本

关键字if是什么对象类型/类?

如何在 Ruby 中验证来自多项 Select 提示的命令行输入?

如何在 OpenURI 中指定http 请求标头

这些 Ruby 命名空间约定之间有什么区别?

在 rake 任务中提问

我可以在 Ruby 中使用默认值创建一个数组吗?

Coffeescript 中等效的 Ruby .times

加载 RubyGems 插件时出错,openssl.bundle (LoadError)

hash['key'] 到 Ruby 中的 hash.key

Ruby 输出 Unicode 字符

Ruby - 查看端口是否打开

Broken pipe (Errno::EPIPE)

Ruby 运算符优先级表

在 jekyll 博客中支持标签的简单方法

如何设置方法测试中使用的私有实例变量?

在现有 Jekyll 安装中切换主题

如何测试数组中的所有项目是否相同?

Ruby:除非与如果不是

如何在 Rails 应用程序中使用 httparty 的基本身份验证?