[英]Can a Ruby method accept either a block OR an argument?
I'm doing the lessons on The Odin Project and now I have to write myself a new #count
method (with another name) that behaves like the normal one from the Enumerable module. 我正在Odin项目中上课,现在我必须自己编写一个新的#count
方法(带有另一个名字),其行为类似于Enumerable模块中的正常方法。
The documentation on count says the following ( http://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-count ): 有关计数的文档说明如下( http://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-count ):
count → int count→int
count(item) → int count(item)→int
count { |obj| count {| obj | block } → int block}→intReturns the number of items in
enum
through enumeration. 通过枚举返回enum
的项目数。 If an argument is given, the number of items inenum
that are equal toitem
are counted. 如果给出了参数,则enum
中与item
相等的item
数。 If a block is given, it counts the number of elements yielding a true value. 如果给出了一个块,它会计算产生真值的元素数量。
I think I can write all of these as separate methods, but I was mostly wondering if one method definition can combine the last two uses of count
- with item
and with the block. 我想我可以将所有这些作为单独的方法编写,但我主要想知道一个方法定义是否可以结合count
的最后两次使用 - 使用item
和block。 Naturally, I'm wondering if all three can be combined in one definition, but I'm mostly interested in the last two. 当然,我想知道这三个是否可以合并在一个定义中,但我最感兴趣的是最后两个。 So far I can't seem to find a possible answer. 到目前为止,我似乎找不到可能的答案。
The documentation page has these examples: 文档页面包含以下示例:
ary = [1, 2, 4, 2]
ary.count #=> 4
ary.count(2) #=> 2
ary.count{ |x| x%2==0 } #=> 3
Sure it's possible. 当然有可能。 All you have to do is check if an argument is given and also check if a block is given. 您所要做的就是检查是否给出了参数,并检查是否给出了一个块。
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
Or: 要么:
def call_me(arg=nil, &block)
puts "arg given" unless arg.nil?
puts "block given" unless block.nil?
end
The latter is useful because it converts the block to a Proc (named block
) that you can then reuse, as below. 后者很有用,因为它将块转换为可以重复使用的Proc(命名block
),如下所示。
You could implement your own count
method like this: 您可以像这样实现自己的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)
See it on repl.it: https://repl.it/@jrunning/YellowishPricklyPenguin-1 在repl.it上看到它: https ://repl.it/@jrunning/YellowishPricklyPenguin-1
Yes, it is possible to do this by making the parameters optional (blocks are always optional anyway) and checking whether a positional argument or a block argument was passed. 是的,可以通过使参数可选(块总是可选的)并检查是否传递位置参数或块参数来实现。
This is a bit messy, though. 不过,这有点乱。 Most Ruby implementations get around this, by implementing the methods in question with privileged access to the private internals of the implementation, which makes it much easier to check whether arguments were passed or not. 大多数Ruby实现通过实现具有对实现的私有内部的特权访问的相关方法来解决这个问题,这使得检查参数是否被传递变得更加容易。 Eg both JRuby and IronRuby have ways to bind multiple overloaded Java / CLI methods to a single Ruby method based on the number and the types of arguments, which makes it possible to implement those three "modes" of count
as three simple overloads of the same method. 例如,JRuby和IronRuby都有办法根据参数的数量和类型将多个重载的 Java / CLI方法绑定到单个 Ruby方法,从而可以将这三个“ count
模式”实现为三个简单的重载方法。 Here's the example of count
from IronRuby , and this is count
from JRuby . 这里有一个例子count
从IronRuby的 ,这是count
从JRuby的 。
Ruby, however, doesn't support overloading, so you have to implement it manually, which can be a bit awkward. 但是,Ruby不支持重载,所以你必须手动实现它,这可能有点尴尬。 Something like this: 像这样的东西:
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
As you can see, it is a bit awkward. 如你所见,它有点尴尬。 Why don't I simply use nil
as a default argument for the optional item
parameter? 我为什么不干脆用nil
作为可选默认参数item
参数? Well, because nil
is a valid argument, and I wouldn't be able to distinguish between someone passing no argument and someone passing nil
as an argument. 好吧,因为nil
是一个有效的参数,我无法区分没有参数的人和传递nil
作为参数的人。
For comparison, here is how count
is implemented in Rubinius : 为了比较,这里是如何在Rubinius中实现count
:
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
Where I (ab)use the fact that the default argument for an optional parameter is an arbitrary Ruby expression with side-effects such as setting variables, Rubinius uses a special undefined
object that is provided by the Rubinius runtime and is equal?
在我(ab)使用可选参数的默认参数是具有副作用(如设置变量)的任意Ruby表达式的事实中,Rubinius使用由Rubinius运行时提供的特殊undefined
对象并且是equal?
only to itself. 只对自己。
Thank you for your help! 谢谢您的帮助! Just before I came to check if there are any answers I came up with the following solution. 就在我来检查是否有任何答案之前,我提出了以下解决方案。 It can be definitely improved, and I'll try to shorten it a bit, but I prefer to first post it here as I came up with it, it might be helpful for other newbies like me. 它可以肯定地改进,我会尝试缩短它,但我更喜欢先在这里发布它,因为我提出它,它可能对像我这样的其他新手有帮助。 In the code below I'm using a #my_each method that I that works the same as the normal #each. 在下面的代码中,我使用的#my_each方法与普通的#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
I also found these two links helpful: A method with an optional parameter and http://augustl.com/blog/2008/procs_blocks_and_anonymous_functions/ (which reminded me that a method can yield a block even if it's not defined as an argument such as &block). 我还发现这两个链接很有用: 带有可选参数的方法和http://augustl.com/blog/2008/procs_blocks_and_anonymous_functions/ (这提醒我一个方法可以产生一个块,即使它没有被定义为参数,例如&块)。 I saw Jorg has commented in the first link's discussion, too. 我看到Jorg也在第一个链接的讨论中发表了评论。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.