在创建对象时将哈希作为初始值传递

我正在使用实例方法创建一个Temperature类,将温度从f转换为c,反之亦然。 测试用例是:

 Temperature.new(:f => 32).in_celsius #=> 0 

我认为传递:f => 32会给出转换。 我不知道如何从散列中提取值32 :f => 32 。 我想提取它,以便我可以将该数字转换为f或c。

以下是我的答案代码。

 class Temperature def initialize(f = 0, c = 0) @f = f @c = c end def in_fahrenheit c = @c * 1.8 c += 32 @f = c.floor @f end def in_celsius f = @f - 32 f /= 1.8 @c = f.floor @c end def fahrenheit @f end def celsius @c end end 

我不熟悉创建对象时作为初始值传递的哈希。 我很感激任何建议。

您的initialize方法正在查找两个参数,但您的输入只包含一个哈希值。 以下是处理包含华氏度的哈希的方法:

  def initialize(temp) @f = temp[:f] end 

如果你想处理F或C中的输入,我会把它作为练习。

如果我是你,我想我会这样做:

 class Temperature def initialize(input) puts "in #{self.class}.#{__method__}, input.class.name is: #{input.class.name}" @f, @c = input[:f], input[:c] end def in_fahrenheit return @f if @f c = @c * 1.8 c += 32 @f = c.floor @f end def in_celsius return @c if @c f = @f - 32 f /= 1.8 @c = f.floor @c end def fahrenheit return @f if @f in_fahrenheit end def celsius return @c if @c in_celsius end end 

正如马克·托马斯指出的那样, initialize正在接收Hash

 > t = Temperature.new(f: 32) in Temperature.initialize, input.class.name is: Hash => # 

BTW, f: 32比现代更现代:f => 32 ,我相信。

我在一些return声明中添加了。 这样,你可以这样做:

 > t = Temperature.new(f: 32) => # > t.fahrenheit => 32 > t.celsius => 0 > t.in_fahrenheit => 32 > t.in_celsius => 0 

和:

 > t = Temperature.new(c: 0) => # > t.fahrenheit => 32 > t.celsius => 0 > t.in_fahrenheit => 32 > t.in_celsius => 0 

正如马克所说,你可以这样做:

 t = Temperature.new(f: 32, c: 32) 

但是,这会给你带来各种错误的结果。 所以,如果你想要防止这种情况,你可以这样做:

 class Temperature def initialize(input) puts "in #{self.class}.#{__method__}, input.class.name is:"<<" #{input.class.name}" @f, @c = input[:f], input[:c] raise "Only one input, please!" if @f and @c end def in_fahrenheit return @f if @f c = @c * 1.8 c += 32 @f = c.floor @f end def in_celsius return @c if @c f = @f - 32 f /= 1.8 @c = f.floor @c end def fahrenheit return @f if @f in_fahrenheit end def celsius return @c if @c in_celsius end end 

哪个会给你这样的东西:

 > t = Temperature.new(f: 32, c: 0) in Temperature.initialize, input.class.name is: Hash RuntimeError: Only one input, please! 

你错误地混合了不同的参数样式。

位置参数

通过调用Temperature.new(:f => 32)判断,你似乎认为def initialize(f = 0, c = 0)提供了哈希或关键字参数。 它没有。 这只是两个位置参数(默认值,所以它们是可选的)。 让我们打印出我们看到的结果:

  def initialize(f = 0, c = 0) puts "f is #{f.inspect}" puts "c is #{c.inspect}" end 

你的来电:

 Temperature.new(:f => 32) f is {:f=>32} c is 0 

显然,这不是你想要的,以及为什么你以后会收到错误。 以下是您必须如何调用它:

 Temperature.new(32) f is 32 c is 0 Temperature.new(0, 32) f is 0 c is 32 

顺便说一下, nil会比0更好,因为0是一个完全有效的温度。 并且提供一些f值以提供c值是很糟糕的。

哈希论证

上面我们看到Ruby为你创建了一个哈希,然后它变成了方法得到的参数。 所以你可以改变方法签名来反映它,即取一个哈希,然后从中提取值:

  def initialize(options) f = options[:f] c = options[:c] puts "f is #{f.inspect}" puts "c is #{c.inspect}" end 

现在您可以像使用它一样使用它:

 Temperature.new(:f => 32) f is 32 c is nil Temperature.new(:c => 32) f is nil c is 32 

请注意,这样您默认为nil 。 您还可以使用这种更好的语法:

 Temperature.new(f: 32) f is 32 c is nil Temperature.new(c: 32) f is nil c is 32 

关键字参数

差不多五年前, Ruby 2问世,给我们带来了关键字参数 。 现在,Ruby不仅可以为您创建哈希,而且还可以让它为您分开 。 像这样:

  def initialize(f: nil, c: nil) puts "f is #{f.inspect}" puts "c is #{c.inspect}" end 

您仍然可以像以前一样调用它:

 Temperature.new(f: 32) f is 32 c is nil Temperature.new(c: 32) f is nil c is 32 

initialize(f: nil, c: nil)看起来比initialize(options)更有意义吗? 它甚至看起来非常像现在的电话。 而且您不需要额外的代码来从哈希中提取值。 此外,如果您错误输入参数名称,现在会收到错误:

 Temperature.new(x: 32) ArgumentError: unknown keyword: x 

单个哈希参数解决方案只需将fc设置为nil并愉快地继续前进,就好像没有问题一样。

你甚至可以通过显式散列和火箭表示法来调用它,尽管我只是为了进一步理解而展示它:

 Temperature.new({:f => 32}) f is 32 c is nil