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
,它返回一个包含两个元素的 Array
,其中包含已强制转换为新类实例的对象和 self
(参见 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?
: 返回 -1、nil
或 +1,具体取决于self
是-Infinity<tt>、有限还是 <tt>+Infinity
。 -
integer?
: 返回self
是否为整数。 -
negative?
: 返回self
是否为负数。 -
nonzero?
: 返回self
是否不为零。 -
positive?
: 返回self
是否为正数。 -
real?
: 返回self
是否为实数。 -
zero?
: 返回self
是否为零。
比较¶ ↑
-
#<=>: 返回
-
-1 如果
self
小于给定值。 -
0 如果
self
等于给定值。 -
1 如果
self
大于给定值。 -
nil
如果self
和给定值不可比较。
-
-
eql?
: 返回self
和给定值是否具有相同的值和类型。
转换¶ ↑
-
-@
: 返回self
的取反值。 -
abs2
: 返回self
的平方。 -
ceil
: 返回大于或等于self
的最小数,精确到给定的精度。 -
coerce
: 返回数组[coerced_self, coerced_other]
,用于给定的其他值。 -
denominator
: 返回self
的Rational
表示的(始终为正的)分母。 -
div
: 返回self
除以给定值并转换为整数的结果。 -
divmod
: 返回一个数组[商, 余数]
,该数组是将self
除以给定除数的结果。 -
floor
: 返回小于或等于self
的最大数字,精确到给定的精度。 -
polar
: 返回数组[self.abs, self.arg]
。 -
quo
: 返回self
除以给定值的商。 -
real
: 返回self
的实部。 -
rect
(别名为rectangular
): 返回数组[self, 0]
。 -
remainder
: 返回self-arg*(self/arg).truncate
,其中arg
为给定的参数。 -
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
。
static VALUE num_uplus(VALUE num) { return num; }
一元减号 - 返回接收者,取反。
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 Core 或标准库中的任何子类都不使用此实现。
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
的最小数字,精度为 digits
个小数位。
Numeric 通过将 self
转换为 Float
并调用 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
组成,它们具有共同的兼容类型。
在 Core 和标准库类中,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_3_0/numeric.rb, line 68 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_3_0/numeric.rb, line 38 def finite? true end
返回小于或等于 self
的最大数字,精度为 digits
个小数位。
Numeric 通过将 self
转换为 Float
并调用 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
是否为有限、-Infinity
或 +Infinity
,返回 nil
、-1 或 1。
# File ruby_3_3_0/numeric.rb, line 48 def infinite? nil end
如果 self
是一个 Integer
,则返回 true
。
1.0.integer? # => false 1.integer? # => true
# File ruby_3_3_0/numeric.rb, line 29 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)); }
如果 self
不是零值,则返回 self
,否则返回 nil
;使用方法 zero?
进行评估。
返回的 self
允许方法进行链式调用
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"]
在核心和标准库类中,Integer
、Float
、Rational
和 Complex
使用此实现。
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_3_0/numeric.rb, line 17 def real self end
如果 self
是一个实数(即不是 Complex
),则返回 true
。
# File ruby_3_3_0/numeric.rb, line 8 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)); }
Generates a sequence of numbers; with a block given, traverses the sequence. Of the Core and Standard Library classes, Integer, Float, and Rational use this implementation. A quick example: squares = [] 1.step(by: 2, to: 10) {|i| squares.push(i*i) } squares # => [1, 9, 25, 49, 81] The generated sequence: - Begins with +self+. - Continues at intervals of +by+ (which may not be zero). - Ends with the last number that is within or equal to +to+; that is, less than or equal to +to+ if +by+ is positive, greater than or equal to +to+ if +by+ is negative. If +to+ is +nil+, the sequence is of infinite length. If a block is given, calls the block with each number in the sequence; returns +self+. If no block is given, returns an Enumerator::ArithmeticSequence. <b>Keyword Arguments</b> With keyword arguments +by+ and +to+, their values (or defaults) determine the step and limit: # 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 <b>Positional Arguments</b> With optional positional arguments +to+ and +by+, their values (or defaults) determine the step and limit: 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]
实现说明
If all the arguments are integers, the loop operates using an integer counter. If any of the arguments are floating point numbers, all are converted to floats, and the loop is executed <i>floor(n + n*Float::EPSILON) + 1</i> times, where <i>n = (limit - self)/step</i>.
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
截断(朝零方向)到 digits
位小数的精度。
Numeric 通过将 self
转换为 Float
并调用 Float#truncate
来实现此功能。
static VALUE num_truncate(int argc, VALUE *argv, VALUE num) { return flo_truncate(argc, argv, rb_Float(num)); }