在Ruby中使用Enumerator
非常简单:
a = [1, 2, 3]
enumerator = a.map
enumerator.each(&:succ) # => [2, 3, 4]
但我可以使用嵌套集合做类似的事情吗?
a = [[1, 2, 3], [4, 5, 6]]
a.map(&:map) # => [#<Enumerator: [1, 2, 3]:map>, #<Enumerator: [4, 5, 6]:map>]
但是现在如何获得[[2, 3, 4], [5, 6, 7]]
?
这总是可以通过一个块完成:
a = [[1, 2, 3], [4, 5, 6]]
a.map { |array| array.map(&:succ) } # => [[2, 3, 4], [5, 6, 7]]
但是我想知道是否有一种避免使用块的方法,部分是因为我觉得必须键入|array| array
很烦人,部分因为我很想找到一种方法来做
理想情况下,感觉就像这个伪代码:
a.map.map(&:succ)
# perhaps also something like this
a.map(&:map).apply(&:succ)
答案 0 :(得分:4)
据我所知,根据您的要求,没有具体的实施方式。
您可以创建一个递归函数来处理它,例如:
def map_succ(a)
a.map {|arr| arr.is_a?(Array) ? map_succ(arr) : arr.succ}
end
然后无论数组的嵌套程度如何都会有效(如果元素没有响应#succ
,这将会失败)。
如果你真的想要,你可以使用monkey_patch数组(无需推荐)
#note if the element does not respond to `#succ` I have nullified it here
class Array
def map_succ
map do |a|
if a.is_a?(Array)
a.map_succ
elsif a.respond_to?(:succ)
a.succ
#uncomment the lines below to return the original object in the event it does not respond to `#succ`
#else
#a
end
end
end
end
示例强>
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9, [2, 3, 4]], {"test"=>"hash"}, "F"]
a.map_succ
#=> [[2, 3, 4], [5, 6, 7], [8, 9, 10, [3, 4, 5]], nil, "G"]
nil
是因为Hash
没有#succ
方法。
更新
基于此SO Post,可以支持类似的语法,但请注意递归仍然是您最好的选择,这样您就可以支持任何深度而不是明确的深度。
#taken straight from @UriAgassi's from post above
class Symbol
def with(*args, &block)
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
end
end
然后
a = [[1,2,3],[4,5,6]]
a.map(&:map.with(&:succ))
#=> [[2, 3, 4], [5, 6, 7]]
a << [7,8,[9,10]]
#=> [[2, 3, 4], [5, 6, 7],[7,8,[9,10]]]
a.map(&:map.with(&:succ))
#=> NoMethodError: undefined method `succ' for [9, 10]:Array
答案 1 :(得分:4)
我知道这样做的唯一方法是执行以下操作:
a = [[1, 2, 3], [4, 5, 6]]
a.map { |b| b.map(&:succ) } # => [[2, 3, 4], [5, 6, 7]]
主要是因为Array#map
/ Enumerable#map
和Symbol#to_proc
的组合,您无法将第二个变量传递给#map
为其生成的块,从而传递另一个变量到内部#map
:
a.map(1) { |b, c| c } # c => 1, but this doesn't work :(
所以你有来使用块语法; Symbol#to_proc
实际上返回一个带有任意数量参数的proc(您可以通过执行:succ.to_proc.arity
来测试它,返回-1
)。第一个参数用作接收器,接下来的几个参数用作方法的参数 - 这在[1, 2, 3].inject(&:+)
中得到证明。但是,
:map.to_proc.call([[1, 2, 3], [4, 5, 6]], &:size) #=> [3, 3]
如何? :map.to_proc
创建了这个:
:map.to_proc # => proc { |receiver, *args, &block| receiver.send(:map, *args, &block) }
然后使用数组数组作为参数调用它,使用此块:
:size.to_proc # => proc { |receiver, *args, &block| receiver.send(:size, *args, &block) }
这会导致.map { |receiver| receiver.size }
被有效调用。
这一切都导致了这一点 - 因为#map
没有额外的参数,并将它们作为参数传递给块,你必须使用一个块。