这是修复代码的一种方法:
a = [435,276,434]
def product(a)
result = [] # Create an empty array that will become [60, 85, 48]
for e in a
final = 1
# Convert the integer e to a string (e.g., "435")
str = e.to_s
# Iterate over each char of the string (e.g., "4", "3" and "5")
str.each_char do |c|
# Convert the character 'c' to an integer (digit) then multiply final by that integer
final *= c.to_i
end
# Append the value of final to the result array
result << final # e.g., when result = [60], result << 85 => [60, 85]
end
result # => [60, 85, 48]
end
product(a) # => [60, 85, 48]
现在让我们看看如何改进它。首先,我们可以链接操作并避免使用临时变量str
。此外,您会发现 for 循环each
通常比for
(尤其是因为您可以使用带有 的块each
)更可取,所以我也会对其进行更改。当我这样做时,由于each_char
循环只包含一个语句,我将用括号而不是do/end
. 我们现在有:
def product(a)
result = [] # Create an empty array that will become [60, 85, 48]
a.each do |e|
final = 1
e.to_s.each_char {|c| final *= c.to_i}
result << final
end
result
end
当我看到这个时,我想我想将数组的每个元素转换a
为其他元素(其数字的乘积)。这表明使用Array
方法map!
(或其同义词,collect!
),而不是each
。由于a
是方法的参数product
,如果我使用a.map!
,那将改变a
调用方法中的值product
。这可能好也可能不好,但由于我返回的是一个计算值数组,所以可能不好,所以我将map!
申请a
. (它被称为“浅”副本,这在这里并不重要,但在其他情况下也可以。)我们现在有这个:
def product(a)
result = a.dup
result.map! do |e|
final = 1
e.to_s.each_char {|c| final *= c.to_i}
final
end
result
end
我们不需要 last result
,因为map!
返回result
(以及改变result
)。嘿,这也意味着我们可以使用just map
(没有区别)。此外,我们可以链接a.dup
并map
摆脱result
:
def product(a)
a.dup.map do |e|
final = 1
e.to_s.each_char {|c| final *= c.to_i}
final
end
end
伙计,我们正在用煤气做饭!接下来,每当您看到一个计算某物的乘积或总和的块时,请思考inject
(或其同义词,reduce
):
def product(a)
a.dup.map do |e|
e.to_s.each_char.inject(1) {|final, c| final * c.to_i}
end
end
假设a
< 0。怎么办?(@Leon 的回答让我想到了这种可能性。)否定的接收者对 没有意义each
,所以如果发生这种情况,让我们提出一个例外:
def product(a)
raise RuntimeError, "Receiver must be non-negative" if self < 0
a.dup.map do |e|
e.to_s.each_char.inject(1) {|final, c| final * c.to_i}
end
end
你可能想在这里停下来,但你可以map
用另一个 `inject:
def product(a)
raise RuntimeError, "Receiver must be non-negative" if self < 0
a.inject([]) {|result, e| result.concat.e.to_s.each_char.inject(1) {|final, c| final * c.to_i}}
end
在这里,inject 的参数是一个名为 的空数组result
。请注意,由于我们没有更改a
,我们不再需要dup
. (我看到@Kingston 得到了类似的答案。)如果你愿意,你可以这样写:
def product(a)
raise RuntimeError, "Receiver must be non-negative" if self < 0
a.inject([]) {|result, e| result << e.to_s.each_char.inject(1) {|final, c| final * c.to_i}; result}
end
但请注意需要那个讨厌; result
的结尾。
您可能会认为所有这些“改进”的最终结果太过分了,或者作者只是在炫耀。这就是我刚接触 Ruby 时的想法。然而,随着经验,你会发现它很自然,读起来像一个句子。它还使调试更容易:从左到右工作,您可以测试链的每个环节以确保其正常工作。