2013-07-25 107 views
1

这是一个红宝石僧侣的练习,我无法围绕一个特定的概念包裹我的头。为什么变量=变量+1工作?

例如,"soup bowl" = "soup bowl" + 1将无效,那么为什么@dishes_needed[a] = (@dishes_needed[a] || 0) + 1在下面的代码中工作?是因为它们是变量而不是对象?如果是这样,为什么不代码a = (a||0)+1工作时,我最初设置a = "Soup"

class Dish 
end 

class Soup < Dish 
end 
class IceCream < Dish 
end 
class ChineseGreenBeans < Dish 
end 

class DeliveryTray 
    DISH_BOWL_MAPPING = { 
    Soup => "soup bowl", 
    IceCream => "ice cream bowl", 
    ChineseGreenBeans => "serving plate" 
    } 

    def initialize 
    @dishes_needed = {} 
    end 

    def add(dish) 
    a = DISH_BOWL_MAPPING[dish.class] 
    @dishes_needed[a] = (@dishes_needed[a] || 0) + 1 
    end 

    def dishes_needed 
     return "None." if @dishes_needed.empty? 

     @dishes_needed.map { |dish, count| "#{count} #{dish}"}.join(", ") 
    end 
end 

d = DeliveryTray.new 
d.add Soup.new; d.add Soup.new 
d.add IceCream.new 

puts d.dishes_needed # should be "2 soup bowl, 1 ice cream bowl" 
+1

等号的RHS评估为值,LHS评估为可变参考 – texasbruce

回答

3

让我们简化@dishes_needed一部分,所以你可以理解的核心概念。 @dishes_needed是一个散列,@dishes_needed[a] = (@dishes_needed[a] || 0) + 1向散列添加一个键值对。

这是查看代码更简单的方法。这里是DISH_BOWL_MAPPING哈希:

DISH_BOWL_MAPPING = { 
    Soup => "soup bowl", 
    IceCream => "ice cream bowl", 
    ChineseGreenBeans => "serving plate" 
    } 

DISH_BOWL_MAPPING哈希获取某一个元素:

>> DISH_BOWL_MAPPING[Soup] 
=> "soup bowl" 

@dishes_needed是一个空哈希:

>> @dishes_needed = {} 
=> {} 

如果a = Soup,然后在这里是怎么了有问题的代码行:

>> a = Soup 
=> Soup 
>> @dishes_needed[a] = (@dishes_needed[a] || 0) + 1 
=> 1 
>> @dishes_needed 
=> {Soup=>1} 

让我们分解这是混乱的等式的右边:

>> (@dishes_needed[a] || 0) + 1 
>> (@dishes_needed[Soup] || 0) + 1 
# @dishes_needed[Soup] is nil because Soup hasn't been added to the hash yet 
>> (nil || 0) + 1 
# nil || 0 evaluates to 0 because nil and false are falsey in Ruby 
>> (0) + 1 
>> 1 

随后打电话@dishes_needed[Soup]计算为1,现在的哈希已更新:

>> @dishes_needed[Soup] 
=> 1 

此说,键(汤)等于值加1(在这种情况下,该值尚未建立,所以它导致1)。

如果a = "Soup"那么a = (a||0)+1的计算结果为a = "Soup" + 1,您不能在Ruby中添加整数和字符串。如果将1转换为字符串,则表达式正确计算。

a = (a||0)+1.to_s 
1

在此代码:

a = DISH_BOWL_MAPPING[dish.class] 
@dishes_needed[a] = (@dishes_needed[a] || 0) + 1 

a要么将​​是从DISH_BOWL_MAPPING散列或nil的值之一的字符串,如果该键是不存在。如果是nil,那么@dishes_needed[a]也将为零,这使得(@dishes_needed[a] || 0)评估为0,然后您将1添加到。这里的关键概念是Hash#[]返回nil如果找不到密钥(除非您已将默认设置为别的,您不在这里)。显然,这里的净效应是,如果它已经存在,则增加@dishes_needed[a];如果它尚不存在,则将其设置为1

对比,与:

a = "Soup" 
a = (a||0)+1 

这里,a总是开始为"Soup",所以(a||0)总是"Soup",你不能添加到1

2

我们不能将事物分成红宝石变量和对象。这里的一切都是一个对象。

关于你的代码唯一需要理解的是+方法(是它的一个方法)将不会与字符串对象一起工作,因为你正在尝试这两种情况。它在这种情况下起作用,因为如果对象是NILL,则使用Fixnum对象0和+方法对Fixnum进行初始化。

希望我回答你。如果需要更多的澄清,请询问。