gpt4 book ai didi

ruby - 为什么在这个任务中使用 Ruby 的 Array#shift?

转载 作者:太空宇宙 更新时间:2023-11-03 17:06:56 24 4
gpt4 key购买 nike

我正在阅读这个关于 Rubymonk 的烦人教程,它要求我执行以下操作:

为我写三种方法——计算、加法和减法。测试应该全部通过。有问题就看提示!还有一点额外的提示:请记住,您可以使用 something.is_a?(Hash) 或 another_thing.is_a?(String) 来检查对象的类型。

我什至无法理解他们要我做什么,所以我决定看看解决方案,并努力对任务有一个体面的理解。

这是解决方案:

def add(*numbers)
numbers.inject(0) { |sum, number| sum + number }
end

def subtract(*numbers)
current_result = numbers.shift
numbers.inject(current_result) { |current_result, number| current_result - number }
end

def calculate(*arguments)
# if the last argument is a Hash, extract it
# otherwise create an empty Hash
options = arguments[-1].is_a?(Hash) ? arguments.pop : {}
options[:add] = true if options.empty?
return add(*arguments) if options[:add]
return subtract(*arguments) if options[:subtract]
end

很多东西我都不懂,但让我困惑的一件事是 shift 方法:current_result = numbers.shift。为什么会在那里?我的意思是,我理解它的作用,但它在这段特定的代码中的作用是什么?

顺便说一句,如果有人不厌其烦地为我破解这段代码,我将永远感激不尽。

该任务位于以下页面的底部:
https://rubymonk.com/learning/books/1-ruby-primer/chapters/19-ruby-methods/lessons/69-new-lesson#solution3899

最佳答案

添加(*数字)

让我们从调用开始:

def add(*numbers)
numbers.inject(0) { |sum, number| sum + number }
end

像这样:
add(1,2,3)    #=> 6

或像这样:
add(*[1,2,3]) #=> 6

两者是等价的。后者向您展示了运算符“splat”的作用。

这导致:
numbers      #=> [1,2,3]

所以 Ruby 将 Enumerable#inject (又名 reduce )发送到 numbers :
[1,2,3].inject(0) { |sum, number| sum + number }
inject 首先将“备忘录” sum 的参数初始化为 inject 的参数(如果像这里一样,它有一个),然后将“接收者”的第一个元素 [1,2,3] 传递给 block 并将其分配给 block 变量 number :
sum          #=> 0
number #=> 1

Ruby 然后计算:
sum + number #=> 0 + 1 => 1

这成为备忘录的新值(value)。接下来, inject2 传递到 block 中并计算:
sum          #=> 1
number #=> 2
sum + number #=> 3

所以(备忘录) sum 现在是 3

最后,
sum          #=> 3
number #=> 3
sum + number #=> 6

由于接收器的所有元素都已传递到 block 中,因此 inject 返回了备忘录的值:
sum          #=> 6

如果您检查 inject 的文档,您会发现如果该方法没有参数,Ruby 会将接收器的第一个元素(此处为 1)分配给备忘录( sum),然后从接收器的第二个元素开始如上所述继续( 2)。正如预期的那样,这会产生相同的答案:
def add(*numbers)
numbers.inject { |sum, number| sum + number }
end

add(1,2,3) #=> 6

那么为什么要包含参数零呢?通常我们会希望 add() (即 add(*[])) 返回零。我将留给您调查 inject 的两种形式中的每一种在此处发生的情况。您能得出什么结论?

正如@Stefan 在他的回答中指出的那样,您可以简单地这样做:
def add(*numbers)
numbers.inject :+
end

这就是你通常看到的写法。

但是,如果 numbers 可能是一个空数组,您需要为备忘录提供一个零初始值:
def add(*numbers)
numbers.inject 0, :+
end

add(*[]) #=> 0

减法(*数字)
def subtract(*numbers)
current_result = numbers.shift
numbers.inject(current_result) { |current_result, number|
current_result - number }
end

这类似于方法 add ,但略有不同。我们需要备忘录的第一个值(这里是 current_result )作为接收器的第一个元素。我们有两种方法可以做到这一点。

第一种方式是这样的:
def subtract(*numbers)
numbers[1..-1].inject(numbers.first) { |current_result, number|
current_result - number }
end

numbers = [6,2,3]
subtract(*numbers) #=> 1


first_number = numbers.first   #=> 6
all_but_first = numbers[1..-1] #=> [2,3]

然后:
numbers[1..-1].inject(numbers.first) { ... }

是:
all_but_first.inject(first_number) { ... }
#=> [2,3].inject(6) { ... }

相反,作者选择写:
first_number = numbers.shift   #=> 6
numbers #=> [2,3]

numbers.inject(first_number) { ... }
#=> [2,3].inject(6) { ... }

这可能有点漂亮,但选择权在你。

第二种方法是使用 inject 不带参数:
def subtract(*numbers)
numbers.inject { |current_result, number| current_result - number }
end

numbers = [6,2,3]
subtract(*numbers) #=> 1

您可以通过查看 inject 的文档来了解其工作原理。

此外,类似于 :add ,您可以编写:
def subtract(*numbers)
numbers.inject :-
end

最后, subtract 要求 numbers 至少有一个元素,所以我们可以这样写:
def subtract(*numbers)
raise ArgumentError, "argument cannot be an empty array" if numbers.empty?
numbers.inject :-
end

计算(*参数)

我们看到 calculate 期望以下列方式之一被调用:
calculate(6,2,3,{ :add=>true })      #=> 11 
calculate(6,2,3,{ :add=>7 }) #=> 11
calculate(6,2,3,{ :subtract=>true }) #=> 1
calculate(6,2,3,{ :subtract=>7 }) #=> 1
calculate(6,2,3) #=> 11

如果散列的键 :add 具有“真实”值(除 falsenil 之外的任何值,我们将添加;如果散列具有具有“真实”值的键 :subtract (除 falsenil 之外的任何值,我们将如果最后一个元素不是散列( calculate(6,2,3)),则假定为 add

笔记:
calculate(6,2,3,{ :add=>false })     #=> nil
calculate(6,2,3,{ :subtract=>nil }) #=> nil

让我们这样写方法:
def calculate(*arguments)
options =
if arguments.last.is_a?(Hash) # or if arguments.last.class==Hash
arguments.pop
else
{}
end

if (options.empty? || options[:add])
add *arguments
elsif options[:subtract]
subtract *arguments
else
nil
end
end

calculate(6,2,3,{ :add=>true }) #=> 11
calculate(6,2,3,{ :add=>7 }) #=> 11
calculate(6,2,3,{ :subtract=>true }) #=> 1
calculate(6,2,3,{ :subtract=>7 }) #=> 1
calculate(6,2,3) #=> 11
calculate(6,2,3,{ :add=>false }) #=> nil
calculate(6,2,3,{ :subtract=>nil }) #=> nil

请注意,不需要 return 关键字(在原始代码中也不需要)。使用散列来表示要执行的操作类型似乎很奇怪。调用该方法会更有意义:
calculate(6,2,3,:add)                #=> 11 
calculate(6,2,3) #=> 11
calculate(6,2,3,:subtract) #=> 1

我们可以如下实现:
def calculate(*arguments)
operation =
case arguments.last
when :add
arguments.pop
:add
when :subtract
arguments.pop
:subtract
else
:add
end

case operation
when :add
add *arguments
else
subtract *arguments
end
end

更好的:
def calculate(*arguments, op=:add)
case op
when :subtract
subtract *arguments
else
add *arguments
end
end

calculate(6,2,3,:add) #=> 11
calculate(6,2,3) #=> 11
calculate(6,2,3,:subtract) #=> 1

我对你提出的“永远感激不尽”感到不知所措,但如果你能在几分钟内感谢我的努力,那就足够了。

关于ruby - 为什么在这个任务中使用 Ruby 的 Array#shift?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31660079/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com