我正在为Odin项目(编程新手)做Ruby练习,我们的任务是重新创建Ruby的#count方法。给定一个类似的数组:
nil_list = [false, false, nil]
观察:
当我尝试重新创建它时,这就是我想出的:
module Enumerable
def my_count (find = nil)
result = 0
for i in self
if block_given?
result += 1 if yield(i)
elsif find != nil
result += 1 if i == find
else return self.length
end
end
return result
end
end
这里的问题是,如果我们作为参数输入nil,这实际上并不计算nils,因为这是相同的(根据我的代码),因为没有参数。
即,nil_list.my_count(nil)== 3而不是1。
在输入这个问题时,我的想法略有不同:
module Enumerable
def my_count (find = "")
result = 0
for i in self
if block_given?
result += 1 if yield(i)
elsif find != ""
result += 1 if i == find
else return self.length
end
end
return result
end
end
所以这解决了我搜索nil时遇到的问题,但是现在nil_list.count(“”)== 0而nil_list.my_count(“”)== 3.同样的问题,只是重新定位到“”我假设永远不会被使用。
此时我只是好奇:实际的计数方法如何防止这个问题发生?
答案 0 :(得分:2)
您可以撰写def my_count(*args)
并查看args
的长度。我写道:
module Enumerable
def my_count(*args)
case
when args.size > 1
raise ArgumentError
when args.size == 1
value = args.first
reduce(0) { |acc, x| value == x ? acc + 1 : acc }
when block_given?
reduce(0) { |acc, x| yield(x) ? acc + 1 : acc }
else
reduce(0) { |acc, x| acc + 1 }
end
end
end
答案 1 :(得分:0)
丑陋的事实是:在大多数Ruby实现中,Enumerable#count
实际上并不是用Ruby编写的。在MRI,YARV和MRuby中,它用C语言编写,用JRuby和XRuby编写,用Java编写,用IronRuby和Ruby.NET编写,用C#编写,用MacRuby编写,用Objective-C编写,用MagLev编写,用Smalltalk,在Topaz中,它用RPython编写,在Cardinal中,用PIR或PASM编写,依此类推。它不仅不是用Ruby编写的,它还具有对执行引擎内部的特权访问权限,特别是它可以访问传递的参数,而这些参数是Ruby无法做到的。
这样的重载方法遍布核心库和标准库,但它们不能轻易地用Ruby编写。实施者通过用做支持重载的语言(例如C#或Java)编写它们来作弊,或者他们给予他们对执行引擎内部的特权访问权。
Ruby中的标准解决方法是(ab)使用以下事实:可选参数的默认值只是普通的Ruby表达式,并且默认值表达式中的局部变量在方法体内是可见的:
def my_count(find = (find_passed = false; nil))
if find_passed # find was passed
# do something
else
# do something else
end
end
第二种可能性是使用一些不可伪造的唯一标记作为默认值:
undefined = Object.new
define_method(:my_count) do |find = undefined|
if undefined.equal?(find) # find was not passed
# do something
else
# do something else
end
end