如何修复 Ruby 中的"String to Integer"错误



我需要由各种费率来确定运输成本。

在过去的5个小时里,我尝试了各种各样的东西。如果我应该使用.to_s.to_f,我已经尝试过了,但做得不正确。

if (weight < 2)
rate = 0.10
elsif ((weight >= 2) or (weight < 10))
rate = 0.20
elsif ((weight >= 10) or (weight < 40))
rate = 0.30
elsif ((weight >= 40) or (weight < 70))
rate = 0.50
elsif ((weight >= 70) or (weight < 100))
rate = 0.75
else (weight >= 100)
rate = 0.90
end
rate = rate.to_i
ship_cost = weight * price * rate
ship_cost = ship_cost.to_i

该结果应该显示应用费率后的运输成本。我一直在处理字符串到整数的错误。

问题是乘法中的一个或多个变量是字符串,这会导致您得到的TypeError错误,如:

'a' * 'b' #  '*': no implicit conversion of String into Integer (TypeError)

如果要抑制错误,可以手动将它们转换为整数或浮点值。这意味着如果字符串没有数字表示,它将返回0:

'asd'.to_i  # 0
'1'.to_i.   # 1
'-9.9'.to_i # -9
'-9.9'.to_f # -9.9

或者,您可以使用保存最小值和最大值的"dictionary"来处理rate赋值。weight可以返回X。创建一个从最小到最大的范围,并询问它是否包括您可以获得的权重值,分配其值:

dict = {
[-Float::INFINITY, 2]  => 0.10,
[2, 10]                => 0.20,
[10, 40]               => 0.30,
[40, 70]               => 0.50,
[70, 100]              => 0.75,
[100, Float::INFINITY] => 0.90
}
p dict.find { |(start, finish), _| (start...finish).include?(-42.12) }.last # 0.1
p dict.find { |(start, finish), _| (start...finish).include?(0) }.last      # 0.1
p dict.find { |(start, finish), _| (start...finish).include?(1) }.last      # 0.1
p dict.find { |(start, finish), _| (start...finish).include?(23) }.last     # 0.3
p dict.find { |(start, finish), _| (start...finish).include?(101) }.last    # 0.9

一个不那么冗长、更符合习惯的解决方案是使用一个包含范围的case语句:

def shipping_rate(weight)
case weight
when 0...2
0.10
when 2...10
0.20
when 10...40
0.30
when 40...70
0.50
when 70...100
0.75
when 100...Float::INFINITY
0.90
end
end

...声明一个范围将排除结束值。所以(40...70).cover?(70) == false。这使我们能够避免重叠问题。

require "minitest/autorun"
class TestShippingRate < Minitest::Test
def test_correct_rate
assert_equal 0.10, shipping_rate(1)
assert_equal 0.20, shipping_rate(3)
assert_equal 0.30, shipping_rate(39)
assert_equal 0.50, shipping_rate(40)
assert_equal 0.75, shipping_rate(70)
assert_equal 0.90, shipping_rate(101)
end
end
# Finished in 0.002255s, 443.3896 runs/s, 2660.3374 assertions/s.
# 1 runs, 6 assertions, 0 failures, 0 errors, 0 skips

如果你想使用Sebastian Palma建议的dict,你可以使用哈希和密钥范围来代替:

def shipping_rate(weight)
{
0...2 => 0.10,
2...10 => 0.20,
10...40 => 0.30,
40...70 => 0.50,
70...100 => 0.75,
100...Float::INFINITY => 0.90
}.find { |k, v| break v if k.cover? weight }
end

使用case更灵活一些,因为您可以添加else条件或处理字符串参数:

def shipping_rate(weight)
case weight
when 0...2
0.10
when 2...10
0.20
when 10...40
0.30
when 40...70
0.50
when 70...100
0.75
when 100...Float::INFINITY
0.90
# I'm not saying this is a good idea as the conversion should happen
# upstream. Its just an example of what you can do
when String
shipping_rate(weight.to_f) # recursion
else 
raise "Oh noes. This should not happen."
end
end