Ruby 方法可以接受块或参数吗?
Can a Ruby method accept either a block OR an argument?
我正在学习 The Odin Project 的课程,现在我必须自己编写一个新的 #count
方法(使用另一个名称),它的行为与 Enumerable 模块中的普通方法一样。
有关计数的文档说明如下 (http://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-count):
count → int
count(item) → int
count { |obj| block } → int
Returns the number of items in enum
through enumeration. If an
argument is given, the number of items in enum
that are equal to item
are counted. If a block is given, it counts the number of elements
yielding a true value.
我想我可以将所有这些写成单独的方法,但我主要想知道一个方法定义是否可以将 count
的最后两个用途与 item
和块结合起来。当然,我想知道是否可以将这三个定义组合成一个定义,但我最感兴趣的是后两个。到目前为止,我似乎找不到可能的答案。
文档页面有这些示例:
ary = [1, 2, 4, 2]
ary.count #=> 4
ary.count(2) #=> 2
ary.count{ |x| x%2==0 } #=> 3
当然有可能。您所要做的就是检查是否给出了参数并检查是否给出了块。
def call_me(arg=nil)
puts "arg given" unless arg.nil?
puts "block given" if block_given?
end
call_me(1)
# => arg given
call_me { "foo" }
# => block given
call_me(1) { "foo" }
# => arg given
# block given
或:
def call_me(arg=nil, &block)
puts "arg given" unless arg.nil?
puts "block given" unless block.nil?
end
后者很有用,因为它将块转换为 Proc(名为 block
),然后您可以重复使用,如下所示。
您可以像这样实现自己的 count
方法:
module Enumerable
def my_count(*args, &block)
return size if args.empty? && block.nil?
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)" if args.size > 1
counter = block.nil? ? ->(i) { i == args[0] } : block
sum {|i| counter.call(i) ? 1 : 0 }
end
end
arr = [1,2,3,4,5]
p arr.my_count # => 5
p arr.my_count(2) # => 1
p arr.my_count(&:even?) # => 2
p arr.my_count(2, 3) # => ArgumentError: wrong number of arguments (given 2, expected 1)
在 repl.it 上查看:https://repl.it/@jrunning/YellowishPricklyPenguin-1
是的,可以通过将参数设为可选(无论如何块始终是可选的)并检查是否传递了位置参数或块参数来实现。
不过这有点乱。大多数 Ruby 实现解决了这个问题,通过实现具有对实现的私有内部的特权访问的有问题的方法,这使得检查参数是否被传递变得容易得多。例如。 JRuby 和 IronRuby 都有办法将 多个重载 Java / CLI 方法绑定到 单个 Ruby 方法基于参数的数量和类型,这使得可以将 count
的三个 "modes" 作为同一方法的三个简单重载来实现。这是 count
from IronRuby, and this is count
from JRuby.
的示例
Ruby,但是不支持重载,所以你必须手动实现它,这可能有点尴尬。像这样:
module Enumerable
def count(item = (item_not_given = true; nil))
item_given = !item_not_given
warn 'given block not used' if block_given? && item_given
return count(&item.method(:==)) if item_given
return inject(0) {|acc, el| if yield el then acc + 1 else acc end } if block_given?
count(&:itself)
end
end
如你所见,有点别扭。为什么我不简单地使用 nil
作为可选 item
参数的默认参数?好吧,因为 nil
是一个有效的参数,我无法区分没有参数传递的人和传递 nil
作为参数的人。
为了比较,这里是 count
is implemented in Rubinius:
def count(item = undefined)
seq = 0
if !undefined.equal?(item)
each do
element = Rubinius.single_block_arg
seq += 1 if item == element
end
elsif block_given?
each { |element| seq += 1 if yield(element) }
else
each { seq += 1 }
end
seq
end
在我(ab)使用可选参数的默认参数是任意 Ruby 表达式和 side-effects 的情况下,例如设置变量,Rubinius 使用特殊的 undefined
由 Rubinius 运行时提供的对象,equal?
仅对它自己。
感谢您的帮助!就在我来检查是否有任何答案之前,我想出了以下解决方案。它肯定可以改进,我会尝试缩短它一点,但我更喜欢先 post 它是我想出来的,它可能对像我这样的其他新手有帮助。在下面的代码中,我使用了与普通#each.
相同的#my_each 方法
def my_count(arg=nil)
sum = 0
if block_given? && arg == nil
self.my_each do |elem|
if yield(elem)
sum += 1
end
end
elsif !block_given? && arg != nil
self.my_each do |elem|
if arg == elem
sum += 1
end
end
else
self.my_each do |elem|
sum += 1
end
end
sum
end
我还发现这两个 link 很有帮助:
和 http://augustl.com/blog/2008/procs_blocks_and_anonymous_functions/(这提醒我一个方法可以产生一个块,即使它没有被定义为一个参数,例如 &block)。我看到 Jorg 在第一个 link 的讨论中也发表了评论。
我正在学习 The Odin Project 的课程,现在我必须自己编写一个新的 #count
方法(使用另一个名称),它的行为与 Enumerable 模块中的普通方法一样。
有关计数的文档说明如下 (http://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-count):
count → int
count(item) → int
count { |obj| block } → intReturns the number of items in
enum
through enumeration. If an argument is given, the number of items inenum
that are equal toitem
are counted. If a block is given, it counts the number of elements yielding a true value.
我想我可以将所有这些写成单独的方法,但我主要想知道一个方法定义是否可以将 count
的最后两个用途与 item
和块结合起来。当然,我想知道是否可以将这三个定义组合成一个定义,但我最感兴趣的是后两个。到目前为止,我似乎找不到可能的答案。
文档页面有这些示例:
ary = [1, 2, 4, 2]
ary.count #=> 4
ary.count(2) #=> 2
ary.count{ |x| x%2==0 } #=> 3
当然有可能。您所要做的就是检查是否给出了参数并检查是否给出了块。
def call_me(arg=nil)
puts "arg given" unless arg.nil?
puts "block given" if block_given?
end
call_me(1)
# => arg given
call_me { "foo" }
# => block given
call_me(1) { "foo" }
# => arg given
# block given
或:
def call_me(arg=nil, &block)
puts "arg given" unless arg.nil?
puts "block given" unless block.nil?
end
后者很有用,因为它将块转换为 Proc(名为 block
),然后您可以重复使用,如下所示。
您可以像这样实现自己的 count
方法:
module Enumerable
def my_count(*args, &block)
return size if args.empty? && block.nil?
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)" if args.size > 1
counter = block.nil? ? ->(i) { i == args[0] } : block
sum {|i| counter.call(i) ? 1 : 0 }
end
end
arr = [1,2,3,4,5]
p arr.my_count # => 5
p arr.my_count(2) # => 1
p arr.my_count(&:even?) # => 2
p arr.my_count(2, 3) # => ArgumentError: wrong number of arguments (given 2, expected 1)
在 repl.it 上查看:https://repl.it/@jrunning/YellowishPricklyPenguin-1
是的,可以通过将参数设为可选(无论如何块始终是可选的)并检查是否传递了位置参数或块参数来实现。
不过这有点乱。大多数 Ruby 实现解决了这个问题,通过实现具有对实现的私有内部的特权访问的有问题的方法,这使得检查参数是否被传递变得容易得多。例如。 JRuby 和 IronRuby 都有办法将 多个重载 Java / CLI 方法绑定到 单个 Ruby 方法基于参数的数量和类型,这使得可以将 count
的三个 "modes" 作为同一方法的三个简单重载来实现。这是 count
from IronRuby, and this is count
from JRuby.
Ruby,但是不支持重载,所以你必须手动实现它,这可能有点尴尬。像这样:
module Enumerable
def count(item = (item_not_given = true; nil))
item_given = !item_not_given
warn 'given block not used' if block_given? && item_given
return count(&item.method(:==)) if item_given
return inject(0) {|acc, el| if yield el then acc + 1 else acc end } if block_given?
count(&:itself)
end
end
如你所见,有点别扭。为什么我不简单地使用 nil
作为可选 item
参数的默认参数?好吧,因为 nil
是一个有效的参数,我无法区分没有参数传递的人和传递 nil
作为参数的人。
为了比较,这里是 count
is implemented in Rubinius:
def count(item = undefined) seq = 0 if !undefined.equal?(item) each do element = Rubinius.single_block_arg seq += 1 if item == element end elsif block_given? each { |element| seq += 1 if yield(element) } else each { seq += 1 } end seq end
在我(ab)使用可选参数的默认参数是任意 Ruby 表达式和 side-effects 的情况下,例如设置变量,Rubinius 使用特殊的 undefined
由 Rubinius 运行时提供的对象,equal?
仅对它自己。
感谢您的帮助!就在我来检查是否有任何答案之前,我想出了以下解决方案。它肯定可以改进,我会尝试缩短它一点,但我更喜欢先 post 它是我想出来的,它可能对像我这样的其他新手有帮助。在下面的代码中,我使用了与普通#each.
相同的#my_each 方法def my_count(arg=nil)
sum = 0
if block_given? && arg == nil
self.my_each do |elem|
if yield(elem)
sum += 1
end
end
elsif !block_given? && arg != nil
self.my_each do |elem|
if arg == elem
sum += 1
end
end
else
self.my_each do |elem|
sum += 1
end
end
sum
end
我还发现这两个 link 很有帮助: