類別 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

在此,類別 Numeric 提供下列方法

查詢

比較

轉換

其他

公開實例方法

self % other → real_numeric 按一下以切換原始碼

傳回以實數表示的 selfother

在核心和標準函式庫類別中,只有 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));
}
別名為:modulo
+self → self 按一下以切換原始碼

傳回 self

static VALUE
num_uplus(VALUE num)
{
    return num;
}
-self → numeric 按一下以切換原始碼

一元減號—傳回接收者,並取反。

static VALUE
num_uminus(VALUE num)
{
    VALUE zero;

    zero = INT2FIX(0);
    do_coerce(&zero, &num, TRUE);

    return num_funcall1(zero, '-', num);
}
self <=> other → zero 或 nil 按一下以切換原始碼

如果 self 等於 other,則傳回 zero,否則傳回 nil

Ruby 核心或標準函式庫中沒有任何子類別使用此實作。

static VALUE
num_cmp(VALUE x, VALUE y)
{
    if (x == y) return INT2FIX(0);
    return Qnil;
}
abs → numeric 按一下以切換原始碼

傳回 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;
}
別名為:magnitude
abs2 → real 按一下以切換原始碼

傳回 self 的平方。

static VALUE
numeric_abs2(VALUE self)
{
    return f_mul(self, self);
}
angle
別名為:arg
arg → 0 或 Math::PI 按一下以切換原始碼

如果 self 為正數,則傳回 zero,否則傳回 Math::PI。

static VALUE
numeric_arg(VALUE self)
{
    if (f_positive_p(self))
        return INT2FIX(0);
    return DBL2NUM(M_PI);
}
別名為:anglephase
ceil(digits = 0) → integer 或 float 按一下以切換原始碼

傳回大於或等於 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));
}
clone(freeze: true) → self 按一下以切換原始碼

傳回 self

如果 freeze 的值既不是 true 也不是 nil,則會引發例外。

相關:Numeric#dup

static VALUE
num_clone(int argc, VALUE *argv, VALUE x)
{
    return rb_immutable_obj_clone(argc, argv, x);
}
coerce(other) → array 按一下以切換來源

傳回一個包含兩個數字元素的 2 元素陣列,由兩個運算元 selfother 組成,屬於一個共用的相容類型。

在核心和標準函式庫類別中,IntegerRationalComplex 使用此實作。

範例

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);
}
conj → self
別名為:conjugate
conjugate
別名也為:conj
denominator → integer 按一下以切換來源

傳回分母(永遠為正)。

static VALUE
numeric_denominator(VALUE self)
{
    return f_denominator(f_to_r(self));
}
div(other) → integer 按一下以切換來源

傳回商數 self/other 作為整數(透過 floor),使用 self 的衍生類別中的方法 /。(Numeric 本身未定義方法 /。)

在核心和標準函式庫類別中,只有 FloatRational 使用此實作。

static VALUE
num_div(VALUE x, VALUE y)
{
    if (rb_equal(INT2FIX(0), y)) rb_num_zerodiv();
    return rb_funcall(num_funcall1(x, '/', y), rb_intern("floor"), 0);
}
divmod(other) → array 按一下以切換來源

傳回一個 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));
}
dup → self 按一下以切換來源

傳回 self

相關:Numeric#clone

static VALUE
num_dup(VALUE x)
{
    return x;
}
eql?(other) → true 或 false 按一下以切換來源

如果 selfother 是同類型且具有相等的值,則傳回 true

在核心和標準函式庫類別中,只有 IntegerRationalComplex 使用此實作。

範例

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);
}
fdiv(other) → float 按一下以切換來源

傳回商數 self/other 作為浮點數,使用 self 的衍生類別中的方法 /。(Numeric 本身未定義方法 /。)

在核心和標準函式庫類別中,只有 BigDecimal 使用此實作。

static VALUE
num_fdiv(VALUE x, VALUE y)
{
    return rb_funcall(rb_Float(x), '/', 1, y);
}
finite? → true 或 false 按一下以切換來源

如果 self 是有限數字,則傳回 true,否則傳回 false

# File numeric.rb, line 38
def finite?
  true
end
floor(digits = 0) → 整數或浮點數 按一下以切換來源

傳回小於或等於 self 的最大數字,其精度為 digits 小數位數。

數字透過將 self 轉換為 浮點數 並呼叫 Float#floor 來實作此方法。

static VALUE
num_floor(int argc, VALUE *argv, VALUE num)
{
    return flo_floor(argc, argv, rb_Float(num));
}
i → 複數 按一下以切換來源

傳回 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);
}
imag → 0
別名:imaginary
imaginary
別名:imag
infinite? → -1、1 或 nil 按一下以切換來源

傳回 nil、-1 或 1,視 self 是有限、-Infinity+Infinity 而定。

# File numeric.rb, line 48
def infinite?
  nil
end
integer? → true 或 false 按一下以切換來源

如果 self整數,則傳回 true

1.0.integer? # => false
1.integer?   # => true
# File numeric.rb, line 29
def integer?
  false
end
magnitude
別名:abs
modulo
別名:%
negative? → true 或 false 按一下以切換來源

如果 self 小於 0,則傳回 true,否則傳回 false

static VALUE
num_negative_p(VALUE num)
{
    return RBOOL(rb_num_negative_int_p(num));
}
nonzero? → self 或 nil 按一下以切換來源

如果 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"]

在核心和標準函式庫類別中,整數浮點數有理數複數 使用此實作。

static VALUE
num_nonzero_p(VALUE num)
{
    if (RTEST(num_funcall0(num, rb_intern("zero?")))) {
        return Qnil;
    }
    return num;
}
numerator → 整數 按一下以切換來源

傳回分子。

static VALUE
numeric_numerator(VALUE self)
{
    return f_numerator(f_to_r(self));
}
phase
別名為:arg
polar → 陣列 按一下以切換來源

傳回陣列 [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);
}
positive? → true 或 false 按一下以切換來源

如果 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);
}
quo(int_or_rat) → rat 按一下以切換來源
quo(flo) → flo

傳回最精確的除法(整數為有理數,浮點數為浮點數)。

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);
}
real → self 按一下以切換來源

傳回 self

# File numeric.rb, line 17
def real
  self
end
real? → true 或 false 按一下以切換來源

如果 self 是實數(即非 Complex),則傳回 true

# File numeric.rb, line 8
def real?
  true
end
rect → array

傳回陣列 [self, 0]

別名為:rectangular
rectangular
別名為:rect
remainder(other) → real_number 按一下以切換來源

傳回將 self 除以 other 之後的餘數。

在核心和標準函式庫類別中,只有 FloatRational 使用此實作。

範例

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;
}
round(digits = 0) → integer 或 float 按一下以切換來源

傳回 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));
}
step(to = nil, by = 1) {|n| ... } → self 按一下以切換來源
step(to = nil, by = 1) → enumerator
step(to = nil, by: 1) {|n| ... } → self
step(to = nil, by: 1) → enumerator
step(by: 1, to: ) {|n| ... } → self
step(by: 1, to: ) → enumerator
step(by: , to: nil) {|n| ... } → self
step(by: , to: nil) → enumerator
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;
}
to_c → complex 按一下以切換來源

傳回 self 作為 Complex 物件。

static VALUE
numeric_to_c(VALUE self)
{
    return rb_complex_new1(self);
}
to_int → integer 按一下以切換來源

傳回 self 作為整數;使用衍生類別中的方法 to_i 進行轉換。

在核心和標準函式庫類別中,只有 RationalComplex 使用此實作。

範例

Rational(1, 2).to_int # => 0
Rational(2, 1).to_int # => 2
Complex(2, 0).to_int  # => 2
Complex(2, 1)         # Raises RangeError (non-zero imaginary part)
static VALUE
num_to_int(VALUE num)
{
    return num_funcall0(num, id_to_i);
}
truncate(digits = 0) → integer 或 float 按一下以切換來源

傳回 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));
}
zero? → true 或 false 按一下以切換來源

如果 zero 具有零值,則傳回 true,否則傳回 false

在核心和標準函式庫類別中,只有 RationalComplex 使用此實作。

static VALUE
num_zero_p(VALUE num)
{
    return rb_equal(num, INT2FIX(0));
}