在符号上链接 & to_proc
Chaining & to_proc on symbol
Rubyist 众所周知 &
会在符号上调用 to_proc
,所以
[:a, :b, :c].map(&:to_s)
等同于
[:a, :b, :c].map { |e| e.to_s } # => ["a", "b", "c"]
假设我想在 to_s
之后立即调用另一个方法,这两个实现将起作用:
[:a, :b, :c].map { |e| e.to_s.upcase }
[:a, :b, :c].map(&:to_s).map(&:upcase)
我的问题是,有没有办法在一个参数中链接 &
Symbol#to_proc
调用?类似于:
[:a, :b, :c].map(&:to_s:upcase)
谢谢!
无法使用符号链接来触发。
但是,您可以将一个方法修补到您正在映射的 class 上,这将同时执行这两项操作,然后调用它。
class Symbol
def to_upcase_str
self.to_s.upcase
end
end
[:a, :b, :c].map(&:to_upcase_str)
如果你只做:
%i[a b c].map { |e| e.to_s.upcase }
然后就使用该块并继续处理更重要的事情。如果您确实在执行一系列 Enumerable 调用并发现这些块在视觉上过于嘈杂:
%i[a b c].map { |e| e.to_s.upcase }.some_chain_of_enumerable_calls...
然后您可以将您的逻辑放入 lambda 以帮助清理外观:
to_s_upcase = lambda { |e| e.to_s.upcase }
%i[a b c].map(&to_s_upcase).some_chain_of_enumerable_calls...
或者把它扔到一个方法里说:
%i[a b c].map(&method(:to_s_upcase)).some_chain_of_enumerable_calls...
无论哪种方式,您都为您的一点点逻辑命名(这几乎是 &:symbol
为您所做的一切)以使代码更易读和更容易理解。在 to_s.upcase
的特定情况下,这有点毫无意义,但当块变大时,这些方法非常有用。
你需要提前定义一些方法,但是这样会有通用性。你可以这样做:
class Symbol
def * other
->x{x.send(self).send(other)}
end
end
[:a, :b, :c].map(&:to_s * :upcase)
[:a, :b, :c].map(&:to_s * :capitalize)
...
我选择*
作为功能组合的方法。
如果你认为你可能会使用第三个符号,你可以这样定义:
class Proc
def * other
->x{call(x).send(other)}
end
end
所以只是为了好玩(并证明如果付出一点努力,在 ruby 中几乎任何事情都是可能的)我们可以在 Symbol
上定义一个方法(我们称它为Symbol#chain
) 提供此功能以及更多
class Symbol
def proc_chain(*args)
args.inject(self.to_proc) do |memo,meth|
meth, *passable_args = [meth].flatten
passable_block = passable_args.pop if passable_args.last.is_a?(Proc)
Proc.new do |obj|
memo.call(obj).__send__(meth,*passable_args,&passable_block)
end
end
end
alias_method :chain, :proc_chain
end
然后可以这样调用
[:a, :b, :c].map(&:to_s.chain(:upcase))
#=> ["A","B","C"]
# Or with Arguments & blocks
[1,2,3,4,5].map(&:itself.chain([:to_s,2],:chars,[:map,->(e){ "#{e}!!!!"}]))
#=> => [["1!!!!"], ["1!!!!", "0!!!!"], ["1!!!!", "1!!!!"],
# ["1!!!!","0!!!!", "0!!!!"], ["1!!!!", "0!!!!", "1!!!!"]]
甚至可以单独使用
p = :to_s.chain([:split,'.'])
p.call(123.45)
#=> ["123","45"]
# Or even
[123.45,76.75].map(&p)
#=> => [["123", "45"], ["76", "75"]]
虽然我们在玩语法,但用 to_proc
方法对数组进行猴子修补怎么样?
class Array
def to_proc
return :itself.to_proc if empty?
->(obj) { drop(1).to_proc.call(first.to_proc.call(obj)) }
end
end
["foo", "bar", "baz"].map(&[:capitalize, :swapcase, :chars, ->a{ a.join("-") }])
# => ["f-O-O", "b-A-R", "b-A-Z"]
在 repl.it 上查看:https://repl.it/JS4B/1
我很惊讶没有人提到 Proc#<<
和 Proc#>>
:
[:a, :b, :c].map(&(:to_s.to_proc << :upcase.to_proc))
# => ["A", "B", "C"]
[:a, :b, :c].map(&(:upcase.to_proc >> :to_s.to_proc))
# => ["A", "B", "C"]
Rubyist 众所周知 &
会在符号上调用 to_proc
,所以
[:a, :b, :c].map(&:to_s)
等同于
[:a, :b, :c].map { |e| e.to_s } # => ["a", "b", "c"]
假设我想在 to_s
之后立即调用另一个方法,这两个实现将起作用:
[:a, :b, :c].map { |e| e.to_s.upcase }
[:a, :b, :c].map(&:to_s).map(&:upcase)
我的问题是,有没有办法在一个参数中链接 &
Symbol#to_proc
调用?类似于:
[:a, :b, :c].map(&:to_s:upcase)
谢谢!
无法使用符号链接来触发。
但是,您可以将一个方法修补到您正在映射的 class 上,这将同时执行这两项操作,然后调用它。
class Symbol
def to_upcase_str
self.to_s.upcase
end
end
[:a, :b, :c].map(&:to_upcase_str)
如果你只做:
%i[a b c].map { |e| e.to_s.upcase }
然后就使用该块并继续处理更重要的事情。如果您确实在执行一系列 Enumerable 调用并发现这些块在视觉上过于嘈杂:
%i[a b c].map { |e| e.to_s.upcase }.some_chain_of_enumerable_calls...
然后您可以将您的逻辑放入 lambda 以帮助清理外观:
to_s_upcase = lambda { |e| e.to_s.upcase }
%i[a b c].map(&to_s_upcase).some_chain_of_enumerable_calls...
或者把它扔到一个方法里说:
%i[a b c].map(&method(:to_s_upcase)).some_chain_of_enumerable_calls...
无论哪种方式,您都为您的一点点逻辑命名(这几乎是 &:symbol
为您所做的一切)以使代码更易读和更容易理解。在 to_s.upcase
的特定情况下,这有点毫无意义,但当块变大时,这些方法非常有用。
你需要提前定义一些方法,但是这样会有通用性。你可以这样做:
class Symbol
def * other
->x{x.send(self).send(other)}
end
end
[:a, :b, :c].map(&:to_s * :upcase)
[:a, :b, :c].map(&:to_s * :capitalize)
...
我选择*
作为功能组合的方法。
如果你认为你可能会使用第三个符号,你可以这样定义:
class Proc
def * other
->x{call(x).send(other)}
end
end
所以只是为了好玩(并证明如果付出一点努力,在 ruby 中几乎任何事情都是可能的)我们可以在 Symbol
上定义一个方法(我们称它为Symbol#chain
) 提供此功能以及更多
class Symbol
def proc_chain(*args)
args.inject(self.to_proc) do |memo,meth|
meth, *passable_args = [meth].flatten
passable_block = passable_args.pop if passable_args.last.is_a?(Proc)
Proc.new do |obj|
memo.call(obj).__send__(meth,*passable_args,&passable_block)
end
end
end
alias_method :chain, :proc_chain
end
然后可以这样调用
[:a, :b, :c].map(&:to_s.chain(:upcase))
#=> ["A","B","C"]
# Or with Arguments & blocks
[1,2,3,4,5].map(&:itself.chain([:to_s,2],:chars,[:map,->(e){ "#{e}!!!!"}]))
#=> => [["1!!!!"], ["1!!!!", "0!!!!"], ["1!!!!", "1!!!!"],
# ["1!!!!","0!!!!", "0!!!!"], ["1!!!!", "0!!!!", "1!!!!"]]
甚至可以单独使用
p = :to_s.chain([:split,'.'])
p.call(123.45)
#=> ["123","45"]
# Or even
[123.45,76.75].map(&p)
#=> => [["123", "45"], ["76", "75"]]
虽然我们在玩语法,但用 to_proc
方法对数组进行猴子修补怎么样?
class Array
def to_proc
return :itself.to_proc if empty?
->(obj) { drop(1).to_proc.call(first.to_proc.call(obj)) }
end
end
["foo", "bar", "baz"].map(&[:capitalize, :swapcase, :chars, ->a{ a.join("-") }])
# => ["f-O-O", "b-A-R", "b-A-Z"]
在 repl.it 上查看:https://repl.it/JS4B/1
我很惊讶没有人提到 Proc#<<
和 Proc#>>
:
[:a, :b, :c].map(&(:to_s.to_proc << :upcase.to_proc))
# => ["A", "B", "C"]
[:a, :b, :c].map(&(:upcase.to_proc >> :to_s.to_proc))
# => ["A", "B", "C"]