在 Ruby 中使用 String#to_proc 实现 to_s(2)
Implement to_s(2) with String#to_proc in Ruby
我正在学习一元运算符,&
。
关于在方法调用的参数中使用 &
有一些很好的问题。通常格式类似于 some_obj.some_method(&:symbol)
:
- What is the functionality of “&: ” operator in ruby?
- What does map(&:name) mean in Ruby?
似乎主要思想是当一元运算符放在符号前面时,ruby 调用 :symbol
上的 to_proc
方法。因为 Symbol#to_proc
存在 "everything works".
我仍然对一切如何运作感到困惑。
如果我想实现一个 "to_proc sort of functionality with a string" 怎么办?我把它放在引号中是因为我什至不确定如何谈论我正在尝试做的事情。
但我们的目标是编写一个 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
returns 的过程?
就写这个
[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])
尽管所有这些看起来更像是学术练习。
您的方法的问题在于,当参数始终作为字符串传递时,无法推断出参数的类型。
顺便回答一下你的问题:
How do the [2, 4, 6, 8] values get into the proc that String#to_proc returns?
这里是some_arg
,这不是你必须定义的变量,而是调用proc时自动传递的参数。
下面是字符串补丁的重写和一些使用示例:
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
问题是在调用 to_s('2')
时,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,而不仅仅是字符串。
定义后,您可以轻松地将字符串换成符号:
class String
def call(*args, &blk)
to_sym.call(*args, &blk)
end
end
puts [1,2,3].map(&'+'.(1))
当你 运行 some_method(&some_obj)
, Ruby 首先调用 some_obj.to_proc
来获得一个过程,然后它 "converts" 那个过程到一个块并通过该块到 some_method
。所以参数如何进入 proc 取决于 some_method
如何将参数传递给块。
例如,正如您定义的 String#to_proc
,其中 returns 一个 proc{|arg| ...}
(一个带有一个参数的过程),并调用 [...].map(&'to_s 2')
、Ruby将其解释为
[...].map(&('to_s 2'.to_proc))
也就是
[...].map(&proc{|arg| ... })
最后
[...].map {|arg| ... }
重构代码
您可以自由选择 proc 块变量的名称。所以它可能是 yet_another_arg
、some_arg
或 something_else
。在这种情况下,您传递给 to_proc
的对象实际上是您要从 self
.
receive the proc call, so you could call it receiver
. The method and param are in the String, so you get them with 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"]
请注意,此方法已被定制为接受一个方法名称和一个整数参数。它在一般情况下不起作用。
另一种可能
警告
eval is evil
您已收到警告
这适用于您想要的确切语法,它也适用于范围更广的方法和参数:
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]]
不过,它也带来了安全问题。能力越大责任越大!
我正在学习一元运算符,&
。
关于在方法调用的参数中使用 &
有一些很好的问题。通常格式类似于 some_obj.some_method(&:symbol)
:
- What is the functionality of “&: ” operator in ruby?
- What does map(&:name) mean in Ruby?
似乎主要思想是当一元运算符放在符号前面时,ruby 调用 :symbol
上的 to_proc
方法。因为 Symbol#to_proc
存在 "everything works".
我仍然对一切如何运作感到困惑。
如果我想实现一个 "to_proc sort of functionality with a string" 怎么办?我把它放在引号中是因为我什至不确定如何谈论我正在尝试做的事情。
但我们的目标是编写一个 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
returns 的过程?
就写这个
[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])
尽管所有这些看起来更像是学术练习。
您的方法的问题在于,当参数始终作为字符串传递时,无法推断出参数的类型。
顺便回答一下你的问题:
How do the [2, 4, 6, 8] values get into the proc that String#to_proc returns?
这里是some_arg
,这不是你必须定义的变量,而是调用proc时自动传递的参数。
下面是字符串补丁的重写和一些使用示例:
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
问题是在调用 to_s('2')
时,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,而不仅仅是字符串。
定义后,您可以轻松地将字符串换成符号:
class String
def call(*args, &blk)
to_sym.call(*args, &blk)
end
end
puts [1,2,3].map(&'+'.(1))
当你 运行 some_method(&some_obj)
, Ruby 首先调用 some_obj.to_proc
来获得一个过程,然后它 "converts" 那个过程到一个块并通过该块到 some_method
。所以参数如何进入 proc 取决于 some_method
如何将参数传递给块。
例如,正如您定义的 String#to_proc
,其中 returns 一个 proc{|arg| ...}
(一个带有一个参数的过程),并调用 [...].map(&'to_s 2')
、Ruby将其解释为
[...].map(&('to_s 2'.to_proc))
也就是
[...].map(&proc{|arg| ... })
最后
[...].map {|arg| ... }
重构代码
您可以自由选择 proc 块变量的名称。所以它可能是 yet_another_arg
、some_arg
或 something_else
。在这种情况下,您传递给 to_proc
的对象实际上是您要从 self
.
receiver
. The method and param are in the String, so you get them with 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"]
请注意,此方法已被定制为接受一个方法名称和一个整数参数。它在一般情况下不起作用。
另一种可能
警告
eval is evil
您已收到警告
这适用于您想要的确切语法,它也适用于范围更广的方法和参数:
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]]
不过,它也带来了安全问题。能力越大责任越大!