我正在尝试学习Ruby,我遇到了一个在线问题,我需要创建类似于此的东西:
add(1) # => 1
add(1).(2) # => 3
add(1).(2).(3) # => 6
等等。
我已经阅读过,最接近我的工作是使用以下代码:
def add(n)
Proc.new { |s| s + n }
end
在某种程度上有效。
我可以运行以下内容:
add(1) # => #<Proc:0x007fb8aac309f0@(pry):2>
# or
add(1).(2) # => 3
但是我尝试这样做的那一刻:
add(1).(2).(3)
我收到以下错误:
NoMethodError
:3的未定义方法call
:Integer
为什么我会收到此错误?如何更改此方法以便它接受尽可能多的新值,因为我将其添加为自身而不会给出错误?
答案 0 :(得分:3)
作为参考,此问题来自CodeWars "A Chain Adding Function"。
实现此目标的最简单方法是定义Integer#call
:
class Integer
def call(n)
self + n
end
end
def add(n)
n
end
puts add(1)
# 1
puts add(1).(2)
# 3
puts add(1).(2).(3)
# 6
返回的对象是纯Ruby整数,而不是代理对象。
请注意,修改这样一个重要的类可能不是一个好主意。可以使用refinements以避免破坏其他Ruby部分:
module ChainAdding
refine Integer do
def call(n)
self + n
end
end
end
def add(n)
n
end
using ChainAdding
puts add(1)
# 1
puts add(1).(2)
# 3
puts add(1).(2).(3)
# 6
如果你正在学习Ruby,那么这种元编程技巧并不是你应该首先关注的。很高兴知道我们可以做到这一点,但这并不意味着我们应该这样做。
答案 1 :(得分:2)
你不能完全按照自己的意愿去做。原因是你必须返回一些东西,并且根据你是否要继续使用链条,某些东西不能改变。
请注意,一些Rails对象会让您在REPL(irb,pry)中产生错觉,但它们实际上只是重新定义#inspect
,这是在所述REPL可视化时调用的。
相反,你可以做一些非常相似的事情,它有一个更好的界面IMO:
def add(starting_number)
sum = starting_number
accumulator = proc do |next_number|
if next_number
sum += next_number
accumulator
else
sum
end
end
end
add(1) # => #<Proc:0x007fb8ae20e860@(pry):4>
add(1).(2) # => #<Proc:0x007fb8ae4894f0@(pry):4>
add(1).() # => 1
add(1).(2).() # => 3
add(1).(2).(3).(4).() # => 10