我正在研究一个 ruby 模块,该模块与 2 维数组(从 csv 文件导入)中的第一个项目匹配,并返回第二个项目。这听起来非常简单,我能够让它工作,直到我尝试匹配不在数组中的项目。发生这种情况时,出于某种原因,将返回整个数组。我能够操纵一个变通方法,涉及一个布尔变量"找到",但我想知道为什么这不能像写的那样工作。
require 'csv'
class Nutrition
@list = CSV.read("./lib/list.csv")
def self.carbs(name)
grams = @list.each do |item|
if item[0] == name
return item[1]
end
end
if grams == nil
grams = "error"
end
return grams
end
end
列表.csv文件如下:
onion,13.75,0,0
carrot,11.375,0,0
cauliflower,19.375,0,0
cabbage,20.125,0,0
sw pepper,20,0,0
leek,7.5,0,0
mushroom,16.375,0,0
celery,33.25,0,0
apple,6.37,0,0
sweet potato,4.875,0,0
broccoli,14.8,0,0
red mill museli,1.52,0,0
mixed nuts,0,0,0.65
B. Sprouts,11,0,0
eggplant,16.66,0,0
quinoa,4.7,0,0
brown rice,4.33,0,0
sesame seed,0,0,4.5
sesame oil,0,0,4.655
pork chop,0,3.84,28.57
chick breast,0,3.22,27.77
lean turkey,0,4,100
ham,0,4.76,26.66
我编辑了我的原始代码,如下所示:
require 'csv'
class Nutrition
include Enumerable
@list = CSV.read("./lib/list.csv")
def self.carbs(name)
result = @list.detect {|item| item|0| == name}
if result.nil?
result = "error"
end
result
end
end
现在,当我使用以下测试文件运行 rake 测试时:
require './lib/nutrition.rb'
require "test/unit"
require 'csv'
class TestNutrition < Test::Unit::TestCase
include Enumerable
def test_carbs()
assert_equal(Nutrition.carbs('onion'), "13.75")
assert_equal(Nutrition.carbs('ham'),'0')
assert_equal(Nutrition.carbs('sawdust'), 'error')
end
end
我最终收到以下错误消息:
语法错误,意外 == (语法错误) 结果 = @list.检测 {|项| 项|0| == 名称} ^
但是,当我运行以下文件时,一切似乎都正常,我似乎无法通过耙子测试:
require 'csv'
class Nutrition
include Enumerable
@list = CSV.read("./lib/list.csv")
def self.carbs(name)
result = @list.detect {|item| item[0] == name}
if result.nil?
result = "error"
end
result
end
end
result = Nutrition.carbs('sawdust')
puts result
你应该使用 Enumerable#find,而不是遍历每个数组。如果未找到该元素,则返回值将为 nil。否则,您将通过查找将其返回。
def self.carbs(name)
grams = @list.find {|item| item[0] == name}
if grams.nil?
grams = "error"
end
grams
end
找不到该项目,也不会nil
grams
。但是,如果达到该测试,则可以确定无论如何都未找到该项,因为如果找到某些内容,该函数将已经返回。因此,此时,您可以返回错误并忘记测试grams
。
这不能按编写方式工作的原因是您将grams
分配给在 @list
上调用 each
的返回值:grams = @list.each...
。 然后,您将在最后返回grams
:return grams
each
方法的返回值是原始数组,如文档中所述:http://ruby-doc.org/core-2.3.0/Array.html#method-i-each("返回数组本身。
在您的方法中,如果找到某个项目,则早期返回会阻止最后一行运行:return item[1]
正确缩进代码后,很容易理解为什么总是返回整个数组:
class Nutrition
def self.carbs(name)
grams = @list.each do |item|
if item[0] == name
return item[1]
end
end
if grams == nil
grams = "error"
end
return grams
end
end
如果name
从未匹配,那么self.carbs
执行的最后一个语句将是return grams
,这当然会返回整个数组。
编辑 另外,我建议对您的设计进行一些更改。
@
变量是实例变量(这里有一篇很好的SO帖子,解释了大多数Ruby变量之间的差异),因此在类(即静态)方法(self.carbs
)中使用@list
可能没有意义。您可以将carbs
定义为实例方法,也可以list
定义为类变量 ( @@list
)。
我使用前一个选项进行了一些重构,并将 each 循环替换为 find
:
class Nutrition
def initialize
@list = CSV.read("./lib/list.csv")
end
def carbs(name)
return "error" if @list.nil?
carb = (@list.find { |x| x[0] == name })
carb.nil? ? "error" : carb[1]
end
end
使用后一个选项:
class Nutrition
@@list = CSV.read("./lib/list.csv")
def self.carbs(name)
return "error" if @list.nil?
carb = (@@list.find { |x| x[0] == name })
carb.nil? ? "error" : carb[1]
end
end