class Numeric
Numeric 是所有更高级别数字类应该继承的类。
Numeric 允许实例化堆分配的对象。其他核心数字类(例如 Integer
)被实现为立即数,这意味着每个 Integer
都是一个单一的不可变对象,总是通过值传递。
a = 1 1.object_id == a.object_id #=> true
例如,整数 1
只能有一个实例。Ruby 通过阻止实例化来确保这一点。如果尝试复制,则返回相同的实例。
Integer.new(1) #=> NoMethodError: undefined method `new' for Integer:Class 1.dup #=> 1 1.object_id == 1.dup.object_id #=> true
因此,在定义其他数字类时应使用 Numeric。
从 Numeric 继承的类必须实现 coerce
,它返回一个包含被强制转换为新类的实例和 self
的对象的双元素 Array
(请参阅 coerce
)。
继承类还应该实现算术运算符方法(+
、-
、*
和 /
)和 <=>
运算符(请参阅 Comparable
)。这些方法可以依赖 coerce
来确保与其他数字类实例的互操作性。
class Tally < Numeric def initialize(string) @string = string end def to_s @string end def to_i @string.size end def coerce(other) [self.class.new('|' * other.to_i), self] end def <=>(other) to_i <=> other.to_i end def +(other) self.class.new('|' * (to_i + other.to_i)) end def -(other) self.class.new('|' * (to_i - other.to_i)) end def *(other) self.class.new('|' * (to_i * other.to_i)) end def /(other) self.class.new('|' * (to_i / other.to_i)) end end tally = Tally.new('||') puts tally * 2 #=> "||||" puts tally > 1 #=> true
这里有什么¶ ↑
首先,看看其他地方的内容。类 Numeric
-
继承自 类 Object。
-
包含 模块 Comparable。
在这里,类 Numeric 提供了以下方法:
查询¶ ↑
-
finite?
:如果self
不是无限数或不是数字,则返回 true。 -
infinite?
:根据self
是-Infinity
、有限的还是+Infinity
,返回 -1、nil
或 +1。 -
integer?
:返回self
是否为整数。 -
negative?
:返回self
是否为负数。 -
nonzero?
:返回self
是否不为零。 -
positive?
:返回self
是否为正数。 -
real?
:返回self
是否为实数。 -
zero?
:返回self
是否为零。
比较¶ ↑
-
#<=>:返回
-
如果
self
小于给定值,则返回 -1。 -
如果
self
等于给定值,则返回 0。 -
如果
self
大于给定值,则返回 1。 -
如果
self
和给定值不可比较,则返回nil
。
-
-
eql?
:返回self
和给定值是否具有相同的值和类型。
转换¶ ↑
-
-@
:返回self
的值,取反。 -
abs2
:返回self
的平方。 -
ceil
:返回大于或等于self
的最小数,精确到给定的精度。 -
coerce
:为给定的其他值返回数组[coerced_self, coerced_other]
。 -
denominator
:返回self
的Rational
表示的分母(始终为正)。 -
div
:返回self
除以给定值并转换为整数的值。 -
divmod
:返回将self
除以给定的除数所产生的数组[quotient, modulus]
。 -
floor
:返回小于或等于self
的最大数,精确到给定的精度。 -
polar
:返回数组[self.abs, self.arg]
。 -
quo
:返回self
除以给定值的值。 -
real
:返回self
的实部。 -
rect
(别名为rectangular
):返回数组[self, 0]
。 -
remainder
:对于给定的arg
,返回self-arg*(self/arg).truncate
。 -
round
:返回self
四舍五入到给定精度的最接近的值。 -
truncate
:返回self
截断(向零方向)到给定精度。
其他¶ ↑
公共实例方法
以实数形式返回 self
模 other
。
在核心和标准库类中,只有 Rational
使用此实现。
对于 Rational
r
和实数 n
,这些表达式等效
r % n r-n*(r/n).floor r.divmod(n)[1]
请参阅 Numeric#divmod
。
示例
r = Rational(1, 2) # => (1/2) r2 = Rational(2, 3) # => (2/3) r % r2 # => (1/2) r % 2 # => (1/2) r % 2.0 # => 0.5 r = Rational(301,100) # => (301/100) r2 = Rational(7,5) # => (7/5) r % r2 # => (21/100) r % -r2 # => (-119/100) (-r) % r2 # => (119/100) (-r) %-r2 # => (-21/100)
static VALUE num_modulo(VALUE x, VALUE y) { VALUE q = num_funcall1(x, id_div, y); return rb_funcall(x, '-', 1, rb_funcall(y, '*', 1, q)); }
返回 self
。
# File ruby_3_4_1/numeric.rb, line 89 def +@ self end
一元负号—返回取反的接收器。
static VALUE num_uminus(VALUE num) { VALUE zero; zero = INT2FIX(0); do_coerce(&zero, &num, TRUE); return num_funcall1(zero, '-', num); }
如果 self
与 other
相同,则返回零,否则返回 nil
。
Ruby 核心或标准库中没有子类使用此实现。
static VALUE num_cmp(VALUE x, VALUE y) { if (x == y) return INT2FIX(0); return Qnil; }
返回 self
的绝对值。
12.abs #=> 12 (-34.56).abs #=> 34.56 -34.56.abs #=> 34.56
static VALUE num_abs(VALUE num) { if (rb_num_negative_int_p(num)) { return num_funcall0(num, idUMinus); } return num; }
返回 self
的平方。
static VALUE numeric_abs2(VALUE self) { return f_mul(self, self); }
如果 self
为正数,则返回零,否则返回 Math::PI。
static VALUE numeric_arg(VALUE self) { if (f_positive_p(self)) return INT2FIX(0); return DBL2NUM(M_PI); }
返回大于或等于 self
的最小浮点数或整数,由给定的 ndigits
指定,ndigits
必须是 可转换为整数的对象。
等效于 self.to_f.ceil(ndigits)
。
相关:floor
、Float#ceil
。
static VALUE num_ceil(int argc, VALUE *argv, VALUE num) { return flo_ceil(argc, argv, rb_Float(num)); }
返回 self
。
如果 freeze
的值既不是 true
也不是 nil
,则引发异常。
相关:Numeric#dup
。
static VALUE num_clone(int argc, VALUE *argv, VALUE x) { return rb_immutable_obj_clone(argc, argv, x); }
返回一个包含两个数字元素的 2 元素数组,这两个元素由两个操作数 self
和 other
组成,具有兼容的通用类型。
在核心和标准库类中,Integer
、Rational
和 Complex
使用此实现。
示例
i = 2 # => 2 i.coerce(3) # => [3, 2] i.coerce(3.0) # => [3.0, 2.0] i.coerce(Rational(1, 2)) # => [0.5, 2.0] i.coerce(Complex(3, 4)) # Raises RangeError. r = Rational(5, 2) # => (5/2) r.coerce(2) # => [(2/1), (5/2)] r.coerce(2.0) # => [2.0, 2.5] r.coerce(Rational(2, 3)) # => [(2/3), (5/2)] r.coerce(Complex(3, 4)) # => [(3+4i), ((5/2)+0i)] c = Complex(2, 3) # => (2+3i) c.coerce(2) # => [(2+0i), (2+3i)] c.coerce(2.0) # => [(2.0+0i), (2+3i)] c.coerce(Rational(1, 2)) # => [((1/2)+0i), (2+3i)] c.coerce(Complex(3, 4)) # => [(3+4i), (2+3i)]
如果任何类型转换失败,则引发异常。
static VALUE num_coerce(VALUE x, VALUE y) { if (CLASS_OF(x) == CLASS_OF(y)) return rb_assoc_new(y, x); x = rb_Float(x); y = rb_Float(y); return rb_assoc_new(y, x); }
返回 self
。
# File ruby_3_4_1/numeric.rb, line 78 def conjugate self end
返回分母(始终为正数)。
static VALUE numeric_denominator(VALUE self) { return f_denominator(f_to_r(self)); }
返回一个包含 2 个元素的数组 [q, r]
,其中
q = (self/other).floor # Quotient r = self % other # Remainder
在核心和标准库类中,只有 Rational
使用此实现。
示例
Rational(11, 1).divmod(4) # => [2, (3/1)] Rational(11, 1).divmod(-4) # => [-3, (-1/1)] Rational(-11, 1).divmod(4) # => [-3, (1/1)] Rational(-11, 1).divmod(-4) # => [2, (-3/1)] Rational(12, 1).divmod(4) # => [3, (0/1)] Rational(12, 1).divmod(-4) # => [-3, (0/1)] Rational(-12, 1).divmod(4) # => [-3, (0/1)] Rational(-12, 1).divmod(-4) # => [3, (0/1)] Rational(13, 1).divmod(4.0) # => [3, 1.0] Rational(13, 1).divmod(Rational(4, 11)) # => [35, (3/11)]
static VALUE num_divmod(VALUE x, VALUE y) { return rb_assoc_new(num_div(x, y), num_modulo(x, y)); }
如果 self
和 other
是相同类型且具有相同的值,则返回 true
。
在核心和标准库类中,只有 Integer
、Rational
和 Complex
使用此实现。
示例
1.eql?(1) # => true 1.eql?(1.0) # => false 1.eql?(Rational(1, 1)) # => false 1.eql?(Complex(1, 0)) # => false
方法 eql?
与 ==
的不同之处在于,eql?
要求类型匹配,而 ==
则不要求。
static VALUE num_eql(VALUE x, VALUE y) { if (TYPE(x) != TYPE(y)) return Qfalse; if (RB_BIGNUM_TYPE_P(x)) { return rb_big_eql(x, y); } return rb_equal(x, y); }
返回 self/other
的商,以浮点数形式,使用 self
的派生类中的方法 /
。(Numeric 本身未定义方法 /
。)
在核心和标准库类中,只有 BigDecimal 使用此实现。
static VALUE num_fdiv(VALUE x, VALUE y) { return rb_funcall(rb_Float(x), '/', 1, y); }
如果 self
是有限数,则返回 true
,否则返回 false
。
# File ruby_3_4_1/numeric.rb, line 48 def finite? true end
返回小于或等于 self
的最大浮点数或整数,由给定的 ndigits
指定,ndigits
必须是可转换为整数的对象。
等效于 self.to_f.floor(ndigits)
。
相关:ceil
、Float#floor
。
static VALUE num_floor(int argc, VALUE *argv, VALUE num) { return flo_floor(argc, argv, rb_Float(num)); }
返回 Complex(0, self)
2.i # => (0+2i) -2.i # => (0-2i) 2.0.i # => (0+2.0i) Rational(1, 2).i # => (0+(1/2)*i) Complex(3, 4).i # Raises NoMethodError.
static VALUE num_imaginary(VALUE num) { return rb_complex_new(INT2FIX(0), num); }
如果 self
是有限数,则返回 nil
;如果 self
是 -Infinity
,则返回 -1;如果 self
是 +Infinity
,则返回 1。
# File ruby_3_4_1/numeric.rb, line 58 def infinite? nil end
如果 self
是一个 Integer
,则返回 true
。
1.0.integer? # => false 1.integer? # => true
# File ruby_3_4_1/numeric.rb, line 39 def integer? false end
以实数形式返回 self
模 other
。
在核心和标准库类中,只有 Rational
使用此实现。
对于 Rational
r
和实数 n
,这些表达式等效
r % n r-n*(r/n).floor r.divmod(n)[1]
请参阅 Numeric#divmod
。
示例
r = Rational(1, 2) # => (1/2) r2 = Rational(2, 3) # => (2/3) r % r2 # => (1/2) r % 2 # => (1/2) r % 2.0 # => 0.5 r = Rational(301,100) # => (301/100) r2 = Rational(7,5) # => (7/5) r % r2 # => (21/100) r % -r2 # => (-119/100) (-r) % r2 # => (119/100) (-r) %-r2 # => (-21/100)
如果 self
小于 0,则返回 true
,否则返回 false
。
static VALUE num_negative_p(VALUE num) { return RBOOL(rb_num_negative_int_p(num)); }
Returns +self+ if +self+ is not a zero value, +nil+ otherwise; uses method <tt>zero?</tt> for the evaluation. The returned +self+ allows the method to be chained: a = %w[z Bb bB bb BB a aA Aa AA A] a.sort {|a, b| (a.downcase <=> b.downcase).nonzero? || a <=> b } # => ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb", "z"] Of the Core and Standard Library classes, Integer, Float, Rational, and Complex use this implementation.
相关:zero?
static VALUE num_nonzero_p(VALUE num) { if (RTEST(num_funcall0(num, rb_intern("zero?")))) { return Qnil; } return num; }
返回分子。
static VALUE numeric_numerator(VALUE self) { return f_numerator(f_to_r(self)); }
返回数组 [self.abs, self.arg]
。
static VALUE numeric_polar(VALUE self) { VALUE abs, arg; if (RB_INTEGER_TYPE_P(self)) { abs = rb_int_abs(self); arg = numeric_arg(self); } else if (RB_FLOAT_TYPE_P(self)) { abs = rb_float_abs(self); arg = float_arg(self); } else if (RB_TYPE_P(self, T_RATIONAL)) { abs = rb_rational_abs(self); arg = numeric_arg(self); } else { abs = f_abs(self); arg = f_arg(self); } return rb_assoc_new(abs, arg); }
如果 self
大于 0,则返回 true
,否则返回 false
。
static VALUE num_positive_p(VALUE num) { const ID mid = '>'; if (FIXNUM_P(num)) { if (method_basic_p(rb_cInteger)) return RBOOL((SIGNED_VALUE)num > (SIGNED_VALUE)INT2FIX(0)); } else if (RB_BIGNUM_TYPE_P(num)) { if (method_basic_p(rb_cInteger)) return RBOOL(BIGNUM_POSITIVE_P(num) && !rb_bigzero_p(num)); } return rb_num_compare_with_zero(num, mid); }
返回最精确的除法(整数为有理数,浮点数为浮点数)。
VALUE rb_numeric_quo(VALUE x, VALUE y) { if (RB_TYPE_P(x, T_COMPLEX)) { return rb_complex_div(x, y); } if (RB_FLOAT_TYPE_P(y)) { return rb_funcallv(x, idFdiv, 1, &y); } x = rb_convert_type(x, T_RATIONAL, "Rational", "to_r"); return rb_rational_div(x, y); }
返回 self
。
# File ruby_3_4_1/numeric.rb, line 27 def real self end
如果 self
是实数(即不是 Complex
),则返回 true
。
# File ruby_3_4_1/numeric.rb, line 18 def real? true end
返回数组 [self, 0]
。
static VALUE numeric_rect(VALUE self) { return rb_assoc_new(self, INT2FIX(0)); }
返回 self
除以 other
后的余数。
在核心和标准库类中,只有 Float
和 Rational
使用此实现。
示例
11.0.remainder(4) # => 3.0 11.0.remainder(-4) # => 3.0 -11.0.remainder(4) # => -3.0 -11.0.remainder(-4) # => -3.0 12.0.remainder(4) # => 0.0 12.0.remainder(-4) # => 0.0 -12.0.remainder(4) # => -0.0 -12.0.remainder(-4) # => -0.0 13.0.remainder(4.0) # => 1.0 13.0.remainder(Rational(4, 1)) # => 1.0 Rational(13, 1).remainder(4) # => (1/1) Rational(13, 1).remainder(-4) # => (1/1) Rational(-13, 1).remainder(4) # => (-1/1) Rational(-13, 1).remainder(-4) # => (-1/1)
static VALUE num_remainder(VALUE x, VALUE y) { if (!rb_obj_is_kind_of(y, rb_cNumeric)) { do_coerce(&x, &y, TRUE); } VALUE z = num_funcall1(x, '%', y); if ((!rb_equal(z, INT2FIX(0))) && ((rb_num_negative_int_p(x) && rb_num_positive_int_p(y)) || (rb_num_positive_int_p(x) && rb_num_negative_int_p(y)))) { if (RB_FLOAT_TYPE_P(y)) { if (isinf(RFLOAT_VALUE(y))) { return x; } } return rb_funcall(z, '-', 1, y); } return z; }
返回 self
四舍五入到具有 digits
位小数精度的最接近的值。
Numeric 通过将 self
转换为 Float
并调用 Float#round
来实现此操作。
static VALUE num_round(int argc, VALUE* argv, VALUE num) { return flo_round(argc, argv, rb_Float(num)); }
生成一个数字序列;如果给定了块,则遍历该序列。
在核心和标准库类中,Integer
、Float
和 Rational
使用此实现。
一个简单的例子
squares = [] 1.step(by: 2, to: 10) {|i| squares.push(i*i) } squares # => [1, 9, 25, 49, 81]
生成的序列
-
从
self
开始。 -
以
by
的间隔继续(不能为零)。 -
以最后一个小于或等于
to
的数字结束;也就是说,如果by
为正数,则小于或等于to
,如果by
为负数,则大于或等于to
。如果to
为nil
,则序列是无限长的。
如果给定了块,则使用序列中的每个数字调用该块;返回 self
。如果没有给定块,则返回一个 Enumerator::ArithmeticSequence
。
关键字参数
使用关键字参数 by
和 to
,它们的值(或默认值)确定步长和限制。
# Both keywords given. squares = [] 4.step(by: 2, to: 10) {|i| squares.push(i*i) } # => 4 squares # => [16, 36, 64, 100] cubes = [] 3.step(by: -1.5, to: -3) {|i| cubes.push(i*i*i) } # => 3 cubes # => [27.0, 3.375, 0.0, -3.375, -27.0] squares = [] 1.2.step(by: 0.2, to: 2.0) {|f| squares.push(f*f) } squares # => [1.44, 1.9599999999999997, 2.5600000000000005, 3.24, 4.0] squares = [] Rational(6/5).step(by: 0.2, to: 2.0) {|r| squares.push(r*r) } squares # => [1.0, 1.44, 1.9599999999999997, 2.5600000000000005, 3.24, 4.0] # Only keyword to given. squares = [] 4.step(to: 10) {|i| squares.push(i*i) } # => 4 squares # => [16, 25, 36, 49, 64, 81, 100] # Only by given. # Only keyword by given squares = [] 4.step(by:2) {|i| squares.push(i*i); break if i > 10 } squares # => [16, 36, 64, 100, 144] # No block given. e = 3.step(by: -1.5, to: -3) # => (3.step(by: -1.5, to: -3)) e.class # => Enumerator::ArithmeticSequence
位置参数
使用可选的位置参数 to
和 by
,它们的值(或默认值)确定步长和限制。
squares = [] 4.step(10, 2) {|i| squares.push(i*i) } # => 4 squares # => [16, 36, 64, 100] squares = [] 4.step(10) {|i| squares.push(i*i) } squares # => [16, 25, 36, 49, 64, 81, 100] squares = [] 4.step {|i| squares.push(i*i); break if i > 10 } # => nil squares # => [16, 25, 36, 49, 64, 81, 100, 121]
实现说明
如果所有参数都是整数,则循环使用整数计数器操作。
如果任何参数是浮点数,则全部转换为浮点数,并且循环执行 floor(n + n*Float::EPSILON) + 1 次,其中 n = (limit - self)/step。
static VALUE num_step(int argc, VALUE *argv, VALUE from) { VALUE to, step; int desc, inf; if (!rb_block_given_p()) { VALUE by = Qundef; num_step_extract_args(argc, argv, &to, &step, &by); if (!UNDEF_P(by)) { step = by; } if (NIL_P(step)) { step = INT2FIX(1); } else if (rb_equal(step, INT2FIX(0))) { rb_raise(rb_eArgError, "step can't be 0"); } if ((NIL_P(to) || rb_obj_is_kind_of(to, rb_cNumeric)) && rb_obj_is_kind_of(step, rb_cNumeric)) { return rb_arith_seq_new(from, ID2SYM(rb_frame_this_func()), argc, argv, num_step_size, from, to, step, FALSE); } return SIZED_ENUMERATOR_KW(from, 2, ((VALUE [2]){to, step}), num_step_size, FALSE); } desc = num_step_scan_args(argc, argv, &to, &step, TRUE, FALSE); if (rb_equal(step, INT2FIX(0))) { inf = 1; } else if (RB_FLOAT_TYPE_P(to)) { double f = RFLOAT_VALUE(to); inf = isinf(f) && (signbit(f) ? desc : !desc); } else inf = 0; if (FIXNUM_P(from) && (inf || FIXNUM_P(to)) && FIXNUM_P(step)) { long i = FIX2LONG(from); long diff = FIX2LONG(step); if (inf) { for (;; i += diff) rb_yield(LONG2FIX(i)); } else { long end = FIX2LONG(to); if (desc) { for (; i >= end; i += diff) rb_yield(LONG2FIX(i)); } else { for (; i <= end; i += diff) rb_yield(LONG2FIX(i)); } } } else if (!ruby_float_step(from, to, step, FALSE, FALSE)) { VALUE i = from; if (inf) { for (;; i = rb_funcall(i, '+', 1, step)) rb_yield(i); } else { ID cmp = desc ? '<' : '>'; for (; !RTEST(rb_funcall(i, cmp, 1, to)); i = rb_funcall(i, '+', 1, step)) rb_yield(i); } } return from; }
将 self
作为 Complex
对象返回。
static VALUE numeric_to_c(VALUE self) { return rb_complex_new1(self); }
将 self
作为整数返回;使用派生类中的方法 to_i
进行转换。
在核心和标准库类中,只有 Rational
和 Complex
使用此实现。
示例
Rational(1, 2).to_int # => 0 Rational(2, 1).to_int # => 2 Complex(2, 0).to_int # => 2 Complex(2, 1).to_int # Raises RangeError (non-zero imaginary part)
static VALUE num_to_int(VALUE num) { return num_funcall0(num, id_to_i); }
返回 self
截断(向零方向)到 digits
位小数精度。
Numeric 通过将 self
转换为 Float
并调用 Float#truncate
来实现此操作。
static VALUE num_truncate(int argc, VALUE *argv, VALUE num) { return flo_truncate(argc, argv, rb_Float(num)); }