为什么sqrt()不是Numeric的一个方法?

30

Ruby中一切皆对象,所以我不明白为什么我们需要Math模块。在我看来,Math模块中的大部分(全部?)函数应该是数字类型(如整数Integer、浮点数Float等)的方法。

例如,不应该是

Math.sqrt(5)

更合理的做法是:

5.sqrt
同样的情况也适用于sin,cos,tan,log10等函数。 有人知道为什么所有这些函数最终都在Math模块中吗?

"5.sqrt" 太太太过时了。 - Anycorn
Kaptajn,因为在我看来它太过于面向对象编程了,对于从其他语言迁移的程序员来说太过奇怪。 - horseyguy
#abs是一个方法:-6.abs #=> 6 - horseyguy
5个回答

24

我不知道Ruby的早期历史,但是我有一种感觉,Math模块是基于C语言中的<math.h>头文件设计的。尽管如此,在Ruby标准库中它是一个奇怪的存在。

但是,这是Ruby!所以你总是可以使用monkey patching(猴子补丁)来解决问题!

class Float
  def sqrt; Math.sqrt(self); end
  def sin; Math.sin(self); end
  def cos; Math.cos(self); end
  def tan; Math.tan(self); end
  def log10; Math.log10(self); end
end

9

进一步解释Michael的答案,没有必要手动定义所有这些方法。请注意,我明确跳过了需要两个参数的两个Math方法。

class Numeric
  (Math.methods - Module.methods - ["hypot", "ldexp"]).each do |method|
    define_method method do
      Math.send method, self
    end
  end
end

puts 25.sqrt
puts 100.log10

输出:

5.0
2.0 

关于为什么这些方法还未包含在Numeric中,我真的不确定有一个好的理由。我认为像Andrew提到的那样的命名空间污染在Numeric类中并不是特别危险的; Michael可能在历史传承方面是正确的。


9
将其更改为 define_method(method) do |*args| Math.send(method, self, *args) end,不再需要排除 hypotldexp - molf
3
表达式(Math.methods - Module.methods)也可以缩短为只有 Math.methods(false)。 - Lars Haugseth
为了方便起见,将其合并为一行代码(在Numeric类中):Math.methods(false).each {|m| define_method m do |*args| Math.send m, self, *args end } - Camille Goudeseune

8

我重新简化了Mark的答案,不需要删除hypotldexp,因为我自己也在使用这种方法。

class Numeric
  Math.methods(false).each do |method|
    define_method method do |*args|
      Math.send(method, self, *args)
    end
  end
end

>> 3.hypot(4)
=> 5.0

>> Math::PI.sqrt
=> 1.7724538509055159

>> 10.log10
=> 1

3
当谈到模块和命名空间时,《Programming Ruby》(也称之为pickaxe)举了一个例子,一个对象既包含了数学函数,又包含了与道德有关的函数,以便它可以计算针尖上能容纳多少天使。然后指出,如果没有适当的命名空间,sin可能是一个模糊的术语。
因此,也许这是关于命名空间污染的问题。

这个问题通常适用于鸭子类型,而不仅仅是数学函数。我怀疑那可能不是原因。 - KaptajnKold

2

5 ** 0.5表明Ruby的指数运算符可以直接处理平方根,而对于log、sin等函数,它们作为全局函数更有意义,而不是方法:它们的起源在于数学,与任何一个浮点实例都没有密切关联。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接