Ruby - 为函数提供多个 lambda 或 proc 参数?
Ruby - providing multiple lambda or proc arguments to a function?
我通过 forwardable
模块在 Hash
上实现了基本扩展,在以下源代码中(此处为 public 域)
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
class,我希望能够将两个 lambda 对象传递给 initialize
方法,理想情况下第二个是可选的,这样然后将使用第二个 lambda 对象(或一般过程)为封装的哈希 @table
提供 'default' 过程。它需要作为 proc
提供给 Hash
初始值设定项。随后,除了直接在 AssocHash
.
中使用的 @keytest
proc 之外,可以从任何调用 API 提供 default
proc
在为此工作时,在本地使用 API 时,似乎使用 &procarg
语法时可能会有一些限制?
- 不能在方法签名中使用两个
&procarg
?
- 不能在方法签名中的
&procarg
后使用任何参数?
- 在参数列表中没有提供常规的必需参数?
目前,我可能只能猜测将任何 object/lambda/proc 传递给另一个方法时如何使用 &
。虽然在当前实现中为 Hash
初始值设定项提供默认过程的表达式中添加 &
可能“刚刚起作用”,但坦率地说,我不确定为什么需要它在那里,或者它如何影响 Hash
初始化程序接收的内容。在这段代码中,这似乎与如何向顶级初始化器提供任意两个 proc 表达式的问题无关?
我不确定将其称为 &procarg
是否是最准确的术语。尽管我对 Ruby 语法这方面的理解有限,但 &
语法似乎是为 AssocHash
的初始化程序提供 @keytest
值的参数所必需的, 这样 @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 传递给一个方法而不用 &
表示它的参数,那么早期的 AssocHash
代码可能会更新为每个 lambda 值将如何传递给方法。
也许这与每个 lambda 将如何存储在传递给 Hash
构造函数的任何实例变量 and/or 中无关。
至少,它可能有助于解决如何修改该单曲的问题 class。这个 &
限定符肯定是一个有趣的语法,imo.
Ruby 中的方法可能不会超过一个块。但是,您可以将任意数量的块绑定为 proc(或 lambda)并将它们作为常规参数传递。你没有得到很好的块糖语法 (do...end
),但它工作得很好。
在 Ruby 中,块(通过 {|...| }
或 do ... end
语法传递给方法的匿名函数)是“一切皆对象”规则的极少数例外之一. Ruby 考虑到将回调函数或匿名函数传递给方法的情况并不少见。您使用块语法传递它,然后可以通过将 &block
参数添加为 arg 列表中的最后一个参数来声明您希望作为 Proc 访问块 ,这获取您传递的匿名函数并将其绑定到存储在变量 block
中的 Proc 实例。这使您可以调用它(通过调用 block.call
)或将其作为普通对象实例(如 block
)或作为块(如 &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 是一些额外的有用读物,有助于理解块和过程之间的区别。
我通过 forwardable
模块在 Hash
上实现了基本扩展,在以下源代码中(此处为 public 域)
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
class,我希望能够将两个 lambda 对象传递给 initialize
方法,理想情况下第二个是可选的,这样然后将使用第二个 lambda 对象(或一般过程)为封装的哈希 @table
提供 'default' 过程。它需要作为 proc
提供给 Hash
初始值设定项。随后,除了直接在 AssocHash
.
@keytest
proc 之外,可以从任何调用 API 提供 default
proc
在为此工作时,在本地使用 API 时,似乎使用 &procarg
语法时可能会有一些限制?
- 不能在方法签名中使用两个
&procarg
? - 不能在方法签名中的
&procarg
后使用任何参数? - 在参数列表中没有提供常规的必需参数?
目前,我可能只能猜测将任何 object/lambda/proc 传递给另一个方法时如何使用 &
。虽然在当前实现中为 Hash
初始值设定项提供默认过程的表达式中添加 &
可能“刚刚起作用”,但坦率地说,我不确定为什么需要它在那里,或者它如何影响 Hash
初始化程序接收的内容。在这段代码中,这似乎与如何向顶级初始化器提供任意两个 proc 表达式的问题无关?
我不确定将其称为 &procarg
是否是最准确的术语。尽管我对 Ruby 语法这方面的理解有限,但 &
语法似乎是为 AssocHash
的初始化程序提供 @keytest
值的参数所必需的, 这样 @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 传递给一个方法而不用 &
表示它的参数,那么早期的 AssocHash
代码可能会更新为每个 lambda 值将如何传递给方法。
也许这与每个 lambda 将如何存储在传递给 Hash
构造函数的任何实例变量 and/or 中无关。
至少,它可能有助于解决如何修改该单曲的问题 class。这个 &
限定符肯定是一个有趣的语法,imo.
Ruby 中的方法可能不会超过一个块。但是,您可以将任意数量的块绑定为 proc(或 lambda)并将它们作为常规参数传递。你没有得到很好的块糖语法 (do...end
),但它工作得很好。
在 Ruby 中,块(通过 {|...| }
或 do ... end
语法传递给方法的匿名函数)是“一切皆对象”规则的极少数例外之一. Ruby 考虑到将回调函数或匿名函数传递给方法的情况并不少见。您使用块语法传递它,然后可以通过将 &block
参数添加为 arg 列表中的最后一个参数来声明您希望作为 Proc 访问块 ,这获取您传递的匿名函数并将其绑定到存储在变量 block
中的 Proc 实例。这使您可以调用它(通过调用 block.call
)或将其作为普通对象实例(如 block
)或作为块(如 &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 是一些额外的有用读物,有助于理解块和过程之间的区别。