我正在学习一元算子&
。
在方法调用的参数中使用&
有一些很好的问题。通常格式类似于some_obj.some_method(&:symbol)
:
&
only valid on method arguments 当一元运算符放在符号前面时,似乎主要思想是ruby调用to_proc
上的:symbol
方法。因为Symbol#to_proc
存在"一切正常"。
我仍然对一切如何运作感到困惑。
如果我想用字符串"实现" to_proc类功能,该怎么办?我把它放在引号中是因为我不确定如何谈论我想要做的事情。
但目标是编写一个String#to_proc
方法,以便以下方法起作用:
class String
def to_proc # some args?
Proc.new do
# some code?
end
end
end
p result = [2, 4, 6, 8].map(&'to_s 2')
#=> ["10", "100", "110", "1000"]
我就这样做了:
class String
def to_proc
Proc.new do |some_arg|
parts = self.split(/ /)
some_proc = parts.first.to_sym.to_proc
another_arg = parts.last.to_i
some_proc.call(some_arg, another_arg)
end
end
end
p result = [2, 4, 6, 8].map(&'to_s 2')
#=> ["10", "100", "110", "1000"]
我很困惑的主要部分是如何将参数输入String#to_proc
方法。好像是:
def to_proc
Proc.new do |some_arg| ...
end
应该是:
def to_proc some_arg
Proc.new do |yet_another_arg| ...
end
或类似的东西。 [2, 4, 6, 8]
值如何进入String#to_proc
返回的过程?
答案 0 :(得分:4)
请写下这个
[2, 4, 6, 8].map { |each| each.to_s(2) }
虽然我猜这不是你要找的......
以下是Symbol#to_proc
的实施方式。
class Symbol
def to_proc
proc { |each| each.send(self) }
end
end
如果需要,可以按如下方式在数组上定义to_proc
class Array
def to_proc
symbol, *args = self
proc { |each| each.send(symbol, *args) }
end
end
然后使用
[2, 4, 6, 8].map(&[:to_s, 2])
另一种选择是使用curry
。
虽然这不适用于绑定方法,因此您必须首先定义to_s
lambda函数。
to_s = lambda { |n, each| each.to_s(n) }
[2, 4, 6, 8].map(&to_s.curry[2])
虽然所有这些看起来更像是学术练习。
答案 1 :(得分:3)
当你运行some_method(&some_obj)
时,Ruby首先调用some_obj.to_proc
来获取一个proc,然后它"转换" proc到块并将该块传递给some_method
。那么参数如何进入proc取决于some_method
如何将参数传递给块。
例如,当您定义String#to_proc
,它返回proc{|arg| ...}
(带有一个参数的proc),并调用[...].map(&'to_s 2')
时,Ruby会将其解释为
[...].map(&('to_s 2'.to_proc))
是
[...].map(&proc{|arg| ... })
最后
[...].map {|arg| ... }
答案 2 :(得分:2)
你的方法的问题是,当它总是以字符串形式传递时,无法推断出参数的类型。
顺便说一句,要解决你的问题:
[2,4,6,8]值如何进入String#to_proc返回的proc?
这里它们是some_arg
,它不是你必须定义的变量,而是一个在调用proc时自动传递的参数。
这里是String补丁的重写和一些用法示例:
class String
def to_proc
fn, *args = split ' '
->(obj) { obj.send(fn.to_sym, *args) }
end
end
这适用于以下示例:
p result = [[1,2,3]].map(&"join -")
# => ['1-2-3']
但是失败了(你的例子):
p result = [2, 4, 6, 8].map(&'to_s 2')
# => TypeError
当2应该是整数而不是字符串时,问题是to_s('2')
被调用。除了可能的一些序列化之外,我想不出任何解决方法(尽管其中一个答案显示了eval
如何工作)。
既然这种方法的局限性很明显,那么值得将它与Symbol上更常用的补丁进行比较,以使参数传递给proc短号(这取自can-you-supply-arguments-to-the-mapmethod-syntax-in-ruby)
class Symbol
def call(*args, &block)
->(caller, *rest) { caller.send(self, *rest, *args, &block) }
end
end
a = [1,3,5,7,9]
a.map(&:+.(2))
# => [3, 5, 7, 9, 11]
这样你可以将任何类型的参数传递给proc,而不仅仅是字符串。
一旦定义了它,就可以轻松地换掉String for Symbol:
class String
def call(*args, &blk)
to_sym.call(*args, &blk)
end
end
puts [1,2,3].map(&'+'.(1))
答案 3 :(得分:1)
您可以自由选择proc块变量的名称。因此可以是yet_another_arg
,some_arg
或something_else
。在这种情况下,您传递给to_proc
的对象实际上是您想要receive proc调用的对象,因此您可以将其称为receiver
。方法和参数位于字符串中,因此您可以使用self
中的String#split
获取它们。
class String
def to_proc
proc do |receiver|
method_name, param = self.split
receiver.method(method_name.to_sym).call(param.to_i)
end
end
end
p result = [2, 4, 6, 8].map(&'to_s 2')
# => ["10", "100", "110", "1000"]
请注意,此方法已经过定制,可以接受一个方法名称和一个整数参数。它在一般情况下不起作用。
这适用于您想要的确切语法,它也适用于更广泛的方法和参数:
class String
def to_proc
proc { |x| eval "#{x.inspect}.#{self}" }
end
end
p [2, 4, 6, 8].map(&'to_s 2')
#=> ["10", "100", "110", "1000"]
p ["10", "100", "110", "1000"].map(&'to_i 2')
#=> [2, 4, 6, 8]
p [1, 2, 3, 4].map(&'odd?')
#=> [true, false, true, false]
p %w(a b c).map(&'*3')
#=> ["aaa", "bbb", "ccc"]
p [[1,2,3],[1,2],[1]].map(&'map(&"*2")')
#=> [[2, 4, 6], [2, 4], [2]]
但它也带来了安全问题。强大的力量带来了巨大的责任!