【问题标题】:Ruby - providing multiple lambda or proc arguments to a function?Ruby - 为函数提供多个 lambda 或 proc 参数?
【发布时间】:2021-11-04 23:17:46
【问题描述】:

我已经通过forwardable 模块在Hash 上实现了一个基本扩展,在以下源代码中(此处为公共域)

module OptionsConstants
  THIS = lambda { |a| a.itself }
end


require('forwardable')

class AssocHash

  extend(Forwardable)
  include(Enumerable)
  def_delegators(:@table,
                 :[], :delete, :each, :keys, :values,
                 :length, :empty?,
                 :include?, :has_key?, :key?, :member?
                )

  def self.table_default(whence)
    return lambda { |hash, key| raise("Found no key #{key} in #{whence.class} #{whence.object_id}") }
  end

  def initialize(&keytest)
    @table = Hash.new(&self.class.table_default(self))
    @keytest = ( keytest || OptionsConstants::THIS )
  end
  

  def add(obj, overwrite = false)
    usekey=key(obj)
    if @table.member?(usekey) 
      if (obj == @table[usekey])
        return obj
      elsif ! overwrite
        raise("An object is already registered for key #{usekey} in #{self.class} #{self}")
      end
    end
    @table[usekey]=obj
    return obj
  end

  def get(key)
    return @table[key]
  end

  def key(obj)
    @keytest.call(obj)
  end

end

我仍在研究 Ruby 语言的语法和语义。我想我已经在documentation for the Proc class in Ruby 2.6 中阅读了一些关于如何在方法参数列表中处理& 的内容。但是,我不确定我是否完全清楚 &procarg 语法如何运作。也许它不像 C 中的指针?

为了实现 AsssocHash 类,我希望能够将两个 lambda 对象传递给 initialize 方法,理想情况下第二个是可选的,这样第二个 lambda 对象(或通用 proc)将是然后用于为封装的哈希@table 提供一个“默认”过程。它需要作为proc 提供给Hash 初始化程序。随后,除了在AssocHash 中直接使用的@keytest proc 之外,还可以从任何调用API 提供default proc。

在本地为此开发 API 时,似乎在使用 &procarg 语法时可能存在一些限制?

  • 不能在方法签名中使用两个&procarg
  • 不能在方法签名中的 &procarg 之后使用任何 arg?
  • 没有在参数列表中提供常规的必需参数?

目前,我可能只能猜测在将任何对象/lambda/proc 传递给另一个方法时如何使用&。虽然在当前实现中将& 添加到表达式中以为Hash 初始化程序提供默认过程可能已经“刚刚工作”,但坦率地说,我不确定为什么需要它,或者如何它会影响 Hash 初始化程序接收的内容。这似乎与如何在此代码中向顶级初始化程序提供任何两个 proc 表达式的问题相切?

我不确定它是否是最准确的术语,将其称为&procarg。尽管我自己对 Ruby 语法的这一方面的理解有限,但将 @keytest 的值提供给 AssocHash 的初始化程序的参数似乎需要 & 语法,这样 @keytest 就可以是稍后在表达式中使用,@keytest.call

有没有办法为初始化方法提供第二个过程,这样就可以用作Hash 初始化方法的默认过程?

我将在此处尝试对语法进行更多迭代。恐怕周围似乎没有很多文档,关于 Ruby 方法签名中的这个 & 限定符。不过,我确信它已在源代码中详细记录。

更新

也许& 可能不需要将 lambda 或 proc 传递给方法?

我相信下面的代码可能有助于解决这个问题

module TestConstants
  NOKEY = lambda { |h,k| return "No key: #{k}" }
end

class Test
  def mkhash(fn = TestConstants::NOKEY) 
    Hash.new(&fn)
  end
end

随后,在 irb 下:

irb(main)> t = Test.new
=> #<Test:0x0000557c6fe2e460>

irb(main)> h = t.mkhash
=> {}

irb(main)> h[:a]
=> "No key: a"

irb(main)> h = t.mkhash(lambda { |h,k| return "Not found: #{k}" })
=> {}

irb(main)> h[:b]
=> "Not found: b"

如果可以将 lambda 或 proc 传递给方法而不用 &amp; 表示参数,那么早期的 AssocHash 代码可能会更新每个 lambda 值将如何传递给方法.

也许这与每个 lambda 将如何存储在任何实例变量中和/或传递给 Hash 构造函数的方式无关。

至少,它可能有助于解决如何修改该单一课程的问题。这个&amp; 限定符肯定是一个有趣的语法,imo。

【问题讨论】:

    标签: ruby lambda syntax


    【解决方案1】:

    Ruby 中的方法可能不会超过一个块。但是,您可以将任意数量的块绑定为 proc(或 lambda)并将它们作为常规参数传递。你没有得到很好的块糖语法 (do...end),但它工作得很好。

    在 Ruby 中,块(通过{|...| }do ... end 语法传递给方法的匿名函数)是“一切都是对象”规则的极少数例外之一。 Ruby 特别考虑到将回调或匿名函数传递给方法的情况并不少见。您使用块语法传递它,然后可以通过将 &amp;block 参数添加为 arg 列表中的最后一个参数来声明您希望访问块 作为 Proc,该参数采用您传递的匿名函数并将其绑定到存储在变量block 中的 Proc 实例。这使您可以调用它(通过调用block.call)或将其作为普通对象实例(如block)或作为一个块(如&amp;block)传递。您也可以使用 yield 隐式调用它,而无需将其绑定到 Proc。

    def debug(obj = nil, &block)
      p [:object, obj]
      p [:block, block]
    end
    
    def pass_block_as_proc(&foo)
      puts "Passing as foo"
      debug(foo)
      puts "Passing as &foo"
      debug(&foo)
    end
    
    pass_block_as_proc {}
    

    输出:

    Passing as foo
    [:object, #<Proc:0x000055ee3a724a38>]
    [:block, nil]
    Passing as &foo
    [:object, nil]
    [:block, #<Proc:0x000055ee3a724a38>]
    

    所以,如果我们想向一个方法传递多个回调,我们不能依赖匿名块糖。相反,我们必须将它们作为绑定的 Proc 实例传递:

    def multiple_procs(value, proc1, proc2)
      proc2.call(proc1.call(value))
    end
    
    puts multiple_procs "foo",
          ->(s) { s.upcase },
          ->(s) { s + s }
    
    # => FOOFOO
    

    This link 是一些额外的有用阅读材料,可帮助您了解块和 procs 之间的区别。

    【讨论】:

    • imo 这是 Ruby 设计中一个非常有趣的领域。我很欣赏全面的解释。不要偏离当前的上下文,也许这些语言特性中的任何一个都可能在 Ruby 中的多线程细节中找到相关性?
    • 这里对多线程没有特别的影响,没有。真正相关的是封闭的东西;与任何其他代码一样,围绕共享数据结构的闭包需要执行锁定以访问这些共享资源。
    猜你喜欢
    • 2022-01-17
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-27
    相关资源
    最近更新 更多