在这段代码中,逗号的作用是什么?

时间:2019-04-07 17:27:58

标签: ruby

我正在尝试使用method.each_with_object理解此代码。 我不明白逗号(units[name]之后)在代码块中的工作方式。

更清楚地,它的功能是什么? 为什么会有一个逗号将units[name]seconds=...分开并且没有写成units[name, seconds=seconds....]

这是一段代码:

units = durations.each_with_object({}) do |(name, duration), units|
    units[name], seconds = seconds.divmod(duration)
end.reject { |k, v| v.zero? }.map(&singularize)

2 个答案:

答案 0 :(得分:4)

在Ruby中,您可以按照以下方式a, b = [1, 2]“解构”数组并将其绑定到多个变量,其中1将绑定到a,而2 b。举个例子:

[1] pry(main)> a, b = [1, 2]
=> [1, 2]
[2] pry(main)> a
=> 1
[3] pry(main)> b
=> 2

知道上面的代码与

相同
units = durations.each_with_object({}) do |(name, duration), units|
    result = seconds.divmod(duration)
    units[name] = result.first
    seconds = result.last
    result
end.reject { |k, v| v.zero? }.map(&singularize)

要反思您的问题,units[name, seconds=seconds....]不是有效的Ruby。哈希的[]方法只需要一个参数。您可以定义自己的可以接受更多内容的类。

# The following was tested on ruby 2.6

class A
  def [](name, surname)
    puts "#{name} #{surname}"
  end

  def []=(name, surname, value)
    puts "#{name} #{surname} = #{value}"
  end
end

a = A.new
a['foo', 'bar'] # prints 'foo bar'
a['foo', 'bar'] = 'baz' # prints 'foo bar = bar'

因此,您可以定义自己的Hash实现来实现您所描述的功能,但是默认情况下不会。

答案 1 :(得分:0)

看来.map(&singularize)与这个问题并没有真正的联系,所以我已经放弃了。

样本数据

让我们分解一下此代码段。为此,我们需要一些数据。我猜测durations可能看起来像下面的样子,但是实际的结构可能并不重要。

seconds = 1630
durations = [["Bob", 3600], ["Melba", 60]] 

鲍勃耗时数小时;梅尔巴,几分钟之内。

枚举器

我们首先定义一个枚举数。

enum = durations.each_with_object({})
  #=> #<Enumerator: {"Bob"=>3600, "Melba"=>60}:each_with_object({})>

enum生成的第一个值

第一个元素由enum生成并传递给块,并为块变量分配值。

(name, duration), units = enum.next
  #=> [["Bob", 3600], {}] 
name
  #=> "Bob" 
duration
  #=> 3600 
units
  #=> {} 

数组分解

分解enum.next的过程称为array decomposition。该链接值得仔细阅读。实际上,整个文件写得很好并且内容丰富。请注意|(name, duration), units|中的括号与数组duration中的括号如何对应。另请参见Enumerator#next

区块计算

现在可以执行块计算了。

units[name], seconds = seconds.divmod(duration)
  #=> units["Bob"], seconds = 1630.divmod(3600)
  #=> units["Bob"], seconds = [0, 1600]
units
  #=> {"Bob"=>0} 
seconds
  #=> 1630 

再一次,我们使用了数组分解。由于duration等于Bob每小时的秒数(3600),units["Bob"]设置为等于他“消耗”的小时数,而seconds现在等于剩余的剩余秒数。由于后者不变(1630),我们推断Bob只能消耗数小时的时间,因此他消耗了零秒。

enum

生成的第二个值

在该块通知enum准备好生成另一个元素之后,我们得到以下内容。

(name, duration), units = enum.next
  #=> [["Melba", 60], {"Bob"=>1}] 
name
  #=> "Melba" 
duration
  #=> 60 
units
  #=> {"Bob"=>0} 

请注意,units已更新。继续

units[name], seconds = seconds.divmod(duration)
  #=> units["Melba"], seconds = 1630.divmod(60)
  #=> units["Melba"], seconds = [27, 10]
units
  #=> {"Bob"=>0, "Melba"=>27}
seconds
  #=> 10

我们看到梅尔巴(Melba)消耗了27分钟1630分钟,而剩下10秒。

该区块要求从enum的下一个元素

该块现在可以用于来自enum的另一个元素。

(name, duration), units = enum.next    
   #=> #StopIteration (iteration reached an end)

enum引发了一个StopInteration异常,因为它没有其他要提供的元素。这将导致该块返回units{"Bob"=>0, "Melba"=>27})的值。

删除不消耗时间的人

最后,我们摆脱了所有消耗零秒的人。

{"Bob"=>0, "Melba"=>27}.reject { |k, v| v.zero? }
  #=> {"Melba"=>27}

更复杂的数组分解示例

这里有两个。

arr = [[1, {a:2}, [3, [4, 5..6]]], 7]

(a, b, (c, (d, e))), f  = arr
a #=> 1 
b #=> {:a=>2} 
c #=> 3 
d #=> 4 
e #=> 5..6 
f #=> 7

[arr].each do |(a,b,(c,(d,e))),f|
  puts a
  puts b
  puts c
  puts d
  puts e
  puts f
end
1
{:a=>2}
3
4
5..6
7

再次,将括号的位置与arr中括号的位置进行比较。

使用数组分解非常有用,但似乎未得到充分利用。