【问题标题】:In Ruby, how do you write a simple method that can be used with &:symbol?在 Ruby 中,如何编写一个可以与 &:symbol 一起使用的简单方法?
【发布时间】:2022-07-24 08:16:02
【问题描述】:

这个article 涉及问题但没有给出解决方案。

这开始于我想编写一个方法并可选地传递一个可以为 null 或 ???? 的参数(proclambdamethodblock、???)。让我们暂时称它为block,因为block 有效。 block 接受一个必需的参数。该方法及其调用的示例如下:

#!/usr/bin/env ruby

def foo(&proc)
  puts "before"
  if proc
    yield "passed to proc"
  end
  puts "after"
end

def add_message(s)
  puts "from add_message #{s}"
end

foo { |s| add_message(s) }
foo

输出是:

before
from add_message passed to proc
after
before
after

太好了。但是,我想做的是能够像这样调用foofoo(&:add_message)。但我不能。更改上面的第 15 行我得到:

before
./temp.rb:11:in `add_message': wrong number of arguments (given 0, expected 1) (ArgumentError)
    from ./temp.rb:6:in `foo'
    from ./temp.rb:15:in `<main>'

而且,正如上面的文章所提到的,arity 现在是 -2。那么,我该如何编写一个像add_message 这样可以与&amp;:add_message 一起使用的简单方法。或者!!!就像 99.99% 的情况一样,请让我在正确的轨道上执行此操作。

【问题讨论】:

    标签: ruby proc


    【解决方案1】:

    问题是Symbol#to_proc 没有创建正确调用add_message 方法的proc。

    # `yield` will pass its arguments to proc
    >> :add_message.to_proc.call('passed to proc')
    # => ArgumentError
    

    这会调用'passed to proc'.add_message,因为我们的方法是在Object 中定义的,它在String 上调用时可以工作,但是它缺少必需的参数。

    解决方案是制作一个可以接受与add_message 方法相同的参数并将它们传递给该方法的proc。我们可以使用 Object#method 返回 Method 对象,该对象实现了自己的 to_proc 并具有与方法相同的数量。

    >> method(:add_message).to_proc.arity
    => 1
    
    >> method(:add_message).to_proc.call('passed to proc')
    from add_message passed to proc
    
    >> foo(&method(:add_message))
    before
    from add_message passed to proc
    after
    

    【讨论】:

    • “问题在于 arity”——这就是 Ruby 所抱怨的,但我认为更大的问题是接收者、消息和参数之间的不匹配。 foo(&amp;method(:add_message)) 导致add_message("passed to proc")foo(&amp;:add_message) 导致"passed to proc".add_message
    【解决方案2】:

    来自the Ruby docs

    其他对象到procs的转换

    任何实现to_proc 方法的对象都可以通过&amp; 运算符转换为proc,因此可以被迭代器使用。

    class Greeter
      def initialize(greeting)
        @greeting = greeting
      end
    
      def to_proc
        proc {|name| "#{@greeting}, #{name}!" }
      end
    end
    
    hi = Greeter.new("Hi")
    hey = Greeter.new("Hey")
    ["Bob", "Jane"].map(&hi)    #=> ["Hi, Bob!", "Hi, Jane!"]
    ["Bob", "Jane"].map(&hey)   #=> ["Hey, Bob!", "Hey, Jane!"]
    

    在 Ruby 核心类中,此方法由 SymbolMethodHash 实现。

    因此,当您传递带有一元&符号的参数时,to_proc 会被调用。 &amp;:“语法”实际上是 &amp; 在符号文字上被调用,即 &amp;(:foobar),而 Symbol.to_proc 具有将符号转换为对其第一个参数的方法调用的行为,即这两者大致等价(模命名参数转发)

    :foobar.to_proc
    proc { |x, *args| x.foobar(*args) }
    

    Ruby 的Method 类型也实现了to_proc,因此如果您有一个名为foobar 的独立方法(在模块上,例如Example),那么您可以调用Example.method(:foobar) 并获得&amp; -兼容的对象。如果你有一个“顶级”方法,那么它可能是 being defined on the main object 并且在没有明确接收者的情况下调用 method 将起作用。

    引用中提到的另一种类型是哈希,它可以变成一个函数,将它们的键映射到它们的值(如果不存在匹配的键,则返回 nil)。当然,您始终可以在自己的类上实现一个名为 to_proc 的方法,它的工作原理与任何内置类型一样好。

    【讨论】:

    • 如果方法属于当前的self,则在没有接收器的情况下调用method 也有效,例如当从一个实例方法中调用method 来引用另一个实例方法(同一个对象)时。
    【解决方案3】:
    class Integer
        def set
            return self + 1
        end
    end
    
    p [1,2,3,4,5,6].map(&:set)
    

    我认为当你可以使用 &amp;: 语法时,已经为上面的类定义了一个方法

    【讨论】:

    • 是的。 &amp;:foo 表示 { |x| x.foo(args) }。我正在做一些完全不同的事情——{ |x| foo(x) }
    猜你喜欢
    • 1970-01-01
    • 2012-08-19
    • 2010-10-22
    • 1970-01-01
    • 2012-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多