我正在尝试使用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)
答案 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
中括号的位置进行比较。
使用数组分解非常有用,但似乎未得到充分利用。