Ruby的Enumerable
是否提供了更好的方法来执行以下操作?
output = things
.find { |thing| thing.expensive_transform.meets_condition? }
.expensive_transform
Enumerable#find
非常适合在枚举中查找元素,但返回原始元素,而不是块的返回值,因此完成的任何工作都将丢失。
当然有很难实现这个目标......
def constly_find(things)
output = nil
things.each do |thing|
expensive_thing = thing.expensive_transform
if expensive_thing.meets_condition?
output = expensive_thing
break
end
end
output
end
这是我正在尝试重构
的替代方法def costly_find(things)
things.each do |thing|
expensive_thing = thing.expensive_transform
return expensive_thing if expensive_thing.meets_condition?
end
nil
end
each.lazy.map.find
def costly_find(things)
things
.each
.lazy
.map(&:expensive_transform)
.find(&:meets_condition?)
end
还有更好的东西吗?
答案 0 :(得分:2)
当然有很难实现这个目标......
如果您的手术很便宜,那么您只需使用:
collection.map(&:operation).find(&:condition?)
根据需要使红宝石只调用operation
"" (如文档所述),您可以简单地添加lazy
:
collection.lazy.map(&:operation).find(&:condition?)
我认为这根本就不丑 - 恰恰相反 - 它看起来很优雅。
应用于您的代码:
def costly_find(things)
things.lazy.map(&:expensive_transform).find(&:meets_condition?)
end
答案 1 :(得分:1)
我倾向于创建一个生成值thing.expensive_transform
的枚举器,然后在find
的块中使meets_condition?
成为find
的接收器。首先,我喜欢读取的方式。
<强>代码强>
def costly_find(things)
Enumerator.new { |y| things.each { |thing| y << thing.expensive_transform } }.
find(&:meets_condition?)
end
示例强>
class Thing
attr_reader :value
def initialize(value)
@value = value
end
def expensive_transform
self.class.new(value*2)
end
def meets_condition?
value == 12
end
end
things = [1,3,6,4].map { |n| Thing.new(n) }
#=> [#<Thing:0x00000001e90b78 @value=1>, #<Thing:0x00000001e90b28 @value=3>,
# #<Thing:0x00000001e90ad8 @value=6>, #<Thing:0x00000001e90ab0 @value=4>]
costly_find(things)
#=> #<Thing:0x00000001e8a3b8 @value=12>
在示例中,我假设expensive_things
和things
是同一个类的实例,但如果不是这种情况,则需要以明显的方式修改代码。
答案 2 :(得分:0)
我不认为有一个明显的最佳通用解决方案&#34;为您的问题,这也很容易使用。您有两个涉及的过程(expensive_transform
和meets_condition?
),如果没有转换元素满足条件,您还需要 - 如果这是一个要使用的库方法 - 作为第三个参数返回的值。在这种情况下,您返回nil,但在一般解决方案中,expensive_transform
也可能产生nil,并且只有调用者知道哪个唯一值表示条件未满足。
因此,Enumerable中的可能解决方案将具有签名
class Enumerable
def find_transformed(default_return_value, transform_proc, condition_proc)
...
end
end
或类似的东西,所以这也不是特别优雅。
如果您同意将两个过程的语义合并为一个,则可以使用单个块执行此操作:您只有一个过程,它会计算转换后的值并对其进行测试。如果测试成功,则返回转换后的值,如果失败,则返回默认值:
class Enumerable
def find_by(default_value, &block)
result = default_value
each do |element|
result = block.call(element)
break if result != default_value
end
end
result
end
你会在你的情况下使用它:
my_collection.find_by(nil) do |el|
transformed_value = expensive_transform(el)
meets_condition?(transformed_value) ? transformed_value : nil
end
我不确定这是否真的直观易用......