класс Numeric
Numeric — это класс, от которого должны наследоваться все числовые классы более высокого уровня.
Numeric позволяет создавать экземпляры объектов, выделенных в куче. Другие основные числовые классы, такие как Integer, реализованы как непосредственные объекты (immediates), что означает, что каждый 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? -
Возвращает true, если
selfне является бесконечным и не является числом типа NaN.
-
-
-
infinite? -
Возвращает -1,
nilили +1, в зависимости от того,selfявляется-Infinity<tt>, finite, or <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 -
Возвращает знаменатель (всегда положительный) дробного представления
Rationalчислаself.
-
-
-
div -
Возвращает значение
self, делённое на заданное значение, преобразованное в целое.
-
-
-
divmod -
Возвращает массив
[quotient, modulus], полученный от деленияselfна заданный делитель.
-
-
-
floor -
Возвращает наибольшее число, меньшее или равное
self, с заданной точностью.
-
-
-
numerator -
Возвращает числитель дробного представления
Rationalчислаself; имеет тот же знак, что иself.
-
-
-
polar -
Возвращает массив
[self.abs, self.arg].
-
-
-
quo -
Возвращает значение
self, делённое на заданное значение.
-
-
-
real -
Возвращает действительную часть
self.
-
-
-
rect(алиасrectangular) -
Возвращает массив
[self, 0].
-
-
-
remainder -
Возвращает
self-arg*(self/arg).truncateдля данногоarg.
-
-
-
round -
Возвращает значение
self, округлённое до ближайшего значения с заданной точностью.
-
-
-
to_c -
Возвращает представление
Complexчислаselfв виде комплексного числа.
-
-
-
to_int -
Возвращает представление
Integerчислаselfв виде целого числа, с усечением при необходимости.
-
-
-
truncate -
Возвращает
selfусеченное (в сторону нуля) до заданной точности.
-
Другие
Общедоступные методы экземпляров
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 на other в виде вещественного числа.
Из классов Ядра и Стандартной библиотеки, только Rational использует эту реализацию.
Для рациональных r и вещественного числа n, эти выражения эквивалентны:
c % n c-n*(c/n).floor c.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)
Numeric#modulo — псевдоним для Numeric#%.
static VALUE
num_uplus(VALUE num)
{
return num;
} Возвращает self.
static VALUE
num_uminus(VALUE num)
{
VALUE zero;
zero = INT2FIX(0);
do_coerce(&zero, &num, TRUE);
return num_funcall1(zero, '-', num);
} Унарный минус — Возвращает значение получателя, с противоположным знаком.
static VALUE
num_cmp(VALUE x, VALUE y)
{
if (x == y) return INT2FIX(0);
return Qnil;
} Возвращает ноль, если self равно other, иначе nil.
Ни один подкласс в ядре Ruby или стандартной библиотеке не использует эту реализацию.
static VALUE
num_abs(VALUE num)
{
if (rb_num_negative_int_p(num)) {
return num_funcall0(num, idUMinus);
}
return num;
} Возвращает абсолютное значение self.
12.abs #=> 12 (-34.56).abs #=> 34.56 -34.56.abs #=> 34.56
Numeric#magnitude — псевдоним для Numeric#abs.
static VALUE
numeric_abs2(VALUE self)
{
return f_mul(self, self);
} Возвращает квадрат значения self.
static VALUE
numeric_arg(VALUE self)
{
if (f_positive_p(self))
return INT2FIX(0);
return DBL2NUM(M_PI);
} Возвращает 0, если значение положительное, иначе пи.
static VALUE
num_ceil(int argc, VALUE *argv, VALUE num)
{
return flo_ceil(argc, argv, rb_Float(num));
} Возвращает наименьшее число, большее или равное self, с точностью до digits десятичных знаков.
Numeric реализует это, преобразуя self в Float и вызывая Float#ceil.
static VALUE
num_clone(int argc, VALUE *argv, VALUE x)
{
return rb_immutable_obj_clone(argc, argv, x);
} Возвращает self.
Вызывает исключение, если значение для freeze не равно ни true, ни nil.
Связанно с: Numeric#dup.
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);
} Возвращает массив из 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
numeric_conj(VALUE self)
{
return self;
} Возвращает self.
static VALUE
numeric_denominator(VALUE self)
{
return f_denominator(f_to_r(self));
} Возвращает знаменатель (всегда положительный).
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);
} Возвращает целую часть частного self/other (через floor), используя метод / в производном классе self. (Сам Numeric не определяет метод /.)
Из классов Ядра и Стандартной библиотеки, Float, Rational и Complex используют эту реализацию.
static VALUE
num_divmod(VALUE x, VALUE y)
{
return rb_assoc_new(num_div(x, y), num_modulo(x, y));
} Возвращает массив из 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_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);
} Возвращает true если self и other одного типа и имеют одинаковые значения.
Из классов Ядра и Стандартной библиотеки, только 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_fdiv(VALUE x, VALUE y)
{
return rb_funcall(rb_Float(x), '/', 1, y);
} Возвращает частное self/other в виде вещественного числа, используя метод / в производном классе self. (Сам Numeric не определяет метод /.)
Из классов Ядра и Стандартной библиотеки, только BigDecimal использует эту реализацию.
# File numeric.rb, line 31 def finite? return true end
Возвращает true если num — конечное число, иначе возвращает false.
static VALUE
num_floor(int argc, VALUE *argv, VALUE num)
{
return flo_floor(argc, argv, rb_Float(num));
} Возвращает наибольшее число, меньшее или равное self, с точностью до digits десятичных знаков.
Числовые типы реализуют это, преобразуя self в Float и вызывая Float#floor.
static VALUE
num_imaginary(VALUE num)
{
return rb_complex_new(INT2FIX(0), 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
numeric_imag(VALUE self)
{
return INT2FIX(0);
} Возвращает ноль.
# File numeric.rb, line 42 def infinite? return nil end
Возвращает -1, 1 или nil в зависимости от того, является ли значение конечным, бесконечным или неопределённым.
# File numeric.rb, line 21 def integer? return false end
Возвращает true, если num является Integer.
1.0.integer? #=> false 1.integer? #=> true
Возвращает абсолютное значение self.
12.abs #=> 12 (-34.56).abs #=> 34.56 -34.56.abs #=> 34.56
Numeric#magnitude является псевдонимом для Numeric#abs.
Возвращает остаток от деления self на other как вещественное число.
Из базовых и стандартных библиотек классов только Rational использует эту реализацию.
Для рационального r и вещественного n, эти выражения эквивалентны:
c % n c-n*(c/n).floor c.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)
Numeric#modulo является псевдонимом для Numeric#%.
static VALUE
num_negative_p(VALUE num)
{
return RBOOL(rb_num_negative_int_p(num));
} Возвращает true, если self меньше 0, иначе false.
static VALUE
num_nonzero_p(VALUE num)
{
if (RTEST(num_funcall0(num, rb_intern("zero?")))) {
return Qnil;
}
return num;
} Возвращает self, если self не равно нулю, иначе nil; использует метод zero? для оценки.
Возвращаемое значение позволяет использовать метод в цепочке:
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
numeric_numerator(VALUE self)
{
return f_numerator(f_to_r(self));
} Возвращает числитель.
Возвращает 0, если значение положительное, и пи в противном случае.
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);
} Возвращает массив; [num.abs, num.arg].
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);
} Возвращает true, если self больше 0, иначе false.
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);
} Возвращает наиболее точное деление (рациональное для целых чисел, вещественное для вещественных чисел).
static VALUE
numeric_real(VALUE self)
{
return self;
} Возвращает self.
# File numeric.rb, line 8 def real? return true end
Возвращает true, если num является вещественным числом (т.е. не Complex).
static VALUE
numeric_rect(VALUE self)
{
return rb_assoc_new(self, INT2FIX(0));
} Возвращает массив; [num, 0].
static VALUE
num_remainder(VALUE x, VALUE y)
{
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 на 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_round(int argc, VALUE* argv, VALUE num)
{
return flo_round(argc, argv, rb_Float(num));
} Возвращает self, округлённое до ближайшего значения с точностью до digits десятичных знаков.
Числовые типы реализуют это, преобразуя self в Float и вызывая Float#round.
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 (by != Qundef) {
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(from, 2, ((VALUE [2]){to, step}), num_step_size);
}
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;
} 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 +step+ (which may not be zero).
- Ends with the last number that is within or equal to +limit+;
that is, less than or equal to +limit+ if +step+ is positive,
greater than or equal to +limit+ if +step+ is negative.
If +limit+ is not given, 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 +limit+ and +step+,
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
numeric_to_c(VALUE self)
{
return rb_complex_new1(self);
} Возвращает значение как комплексное.
static VALUE
num_to_int(VALUE num)
{
return num_funcall0(num, id_to_i);
} Возвращает 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) # Raises RangeError (non-zero imaginary part)
static VALUE
num_truncate(int argc, VALUE *argv, VALUE num)
{
return flo_truncate(argc, argv, rb_Float(num));
} Возвращает self, усеченное (к нулю) с точностью до digits десятичных знаков.
Numeric реализует это, преобразуя self в Float и вызывая Float#truncate.
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.