класс 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
Публичные методы экземпляра
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));
} x.modulo(y) означает x-y*(x/y).floor.
Эквивалентно num.divmod(numeric)[1].
См. Numeric#divmod.
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);
} Унарный минус — возвращает получатель, с отрицательным знаком.
static VALUE
num_cmp(VALUE x, VALUE y)
{
if (x == y) return INT2FIX(0);
return Qnil;
} Возвращает ноль, если number равно other, иначе возвращает nil.
static VALUE
num_abs(VALUE num)
{
if (rb_num_negative_int_p(num)) {
return num_funcall0(num, idUMinus);
}
return num;
} Возвращает абсолютное значение num.
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);
} Возвращает квадрат самого себя.
static VALUE
numeric_arg(VALUE self)
{
if (f_positive_p(self))
return INT2FIX(0);
return DBL2NUM(M_PI);
} Возвращает 0, если значение положительное, pi в противном случае.
static VALUE
numeric_arg(VALUE self)
{
if (f_positive_p(self))
return INT2FIX(0);
return DBL2NUM(M_PI);
} Возвращает 0, если значение положительное, pi в противном случае.
static VALUE
num_ceil(int argc, VALUE *argv, VALUE num)
{
return flo_ceil(argc, argv, rb_Float(num));
} Возвращает наименьшее число, большее или равное num, с точностью до ndigits десятичных знаков (по умолчанию: 0).
Numeric реализует это, преобразуя свое значение в Float и вызывая Float#ceil.
static VALUE
num_clone(int argc, VALUE *argv, VALUE x)
{
return rb_immutable_obj_clone(argc, argv, x);
} Возвращает получатель. freeze не может быть false.
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);
} Если numeric имеет тот же тип, что и num, возвращает массив [numeric, num]. В противном случае возвращает массив с обоими numeric и num, представленными как объекты Float.
Этот механизм приведения типов используется Ruby для обработки числовых операций со смешанными типами: он предназначен для поиска совместимого общего типа между двумя операндами оператора.
1.coerce(2.5) #=> [2.5, 1.0] 1.2.coerce(3) #=> [3.0, 1.2] 1.coerce(2) #=> [2, 1]
static VALUE
numeric_conj(VALUE self)
{
return self;
} Возвращает self.
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);
} Использует / для выполнения деления, затем преобразует результат в целое число. Numeric не определяет оператор /; это оставляется подклассам.
Эквивалентно num.divmod(numeric)[0].
См. Numeric#divmod.
static VALUE
num_divmod(VALUE x, VALUE y)
{
return rb_assoc_new(num_div(x, y), num_modulo(x, y));
} Возвращает массив, содержащий частное и остаток, полученные путем деления num на numeric.
Если q, r = x.divmod(y), то
q = floor(x/y) x = q*y + r
Частное округляется к отрицательной бесконечности, как показано в следующей таблице:
a | b | a.divmod(b) | a/b | a.modulo(b) | a.remainder(b) ------+-----+---------------+---------+-------------+--------------- 13 | 4 | 3, 1 | 3 | 1 | 1 ------+-----+---------------+---------+-------------+--------------- 13 | -4 | -4, -3 | -4 | -3 | 1 ------+-----+---------------+---------+-------------+--------------- -13 | 4 | -4, 3 | -4 | 3 | -1 ------+-----+---------------+---------+-------------+--------------- -13 | -4 | 3, -1 | 3 | -1 | -1 ------+-----+---------------+---------+-------------+--------------- 11.5 | 4 | 2, 3.5 | 2.875 | 3.5 | 3.5 ------+-----+---------------+---------+-------------+--------------- 11.5 | -4 | -3, -0.5 | -2.875 | -0.5 | 3.5 ------+-----+---------------+---------+-------------+--------------- -11.5 | 4 | -3, 0.5 | -2.875 | 0.5 | -3.5 ------+-----+---------------+---------+-------------+--------------- -11.5 | -4 | 2, -3.5 | 2.875 | -3.5 | -3.5
Примеры
11.divmod(3) #=> [3, 2] 11.divmod(-3) #=> [-4, -1] 11.divmod(3.5) #=> [3, 0.5] (-11).divmod(3.5) #=> [-4, 3.0] 11.5.divmod(3.5) #=> [3, 1.0]
static VALUE
num_dup(VALUE x)
{
return x;
} Возвращает получатель.
static VALUE
num_eql(VALUE x, VALUE y)
{
if (TYPE(x) != TYPE(y)) return Qfalse;
if (RB_TYPE_P(x, T_BIGNUM)) {
return rb_big_eql(x, y);
}
return rb_equal(x, y);
} Возвращает true, если num и numeric имеют один и тот же тип и равные значения. Сравните это с Numeric#==, который выполняет преобразование типов.
1 == 1.0 #=> true 1.eql?(1.0) #=> false 1.0.eql?(1.0) #=> true
static VALUE
num_fdiv(VALUE x, VALUE y)
{
return rb_funcall(rb_Float(x), '/', 1, y);
} Возвращает деление с плавающей точкой.
static VALUE
num_finite_p(VALUE num)
{
return Qtrue;
} Возвращает true, если num является конечным числом, иначе возвращает false.
static VALUE
num_floor(int argc, VALUE *argv, VALUE num)
{
return flo_floor(argc, argv, rb_Float(num));
} Возвращает наибольшее число, меньшее или равное num, с точностью до ndigits десятичных знаков (по умолчанию: 0).
Numeric реализует это, преобразуя свое значение в Float и вызывая Float#floor.
static VALUE
num_imaginary(VALUE num)
{
return rb_complex_new(INT2FIX(0), num);
} Возвращает соответствующее мнимое число. Недоступно для комплексных чисел.
-42.i #=> (0-42i) 2.0.i #=> (0+2.0i)
static VALUE
numeric_imag(VALUE self)
{
return INT2FIX(0);
} Возвращает ноль.
static VALUE
numeric_imag(VALUE self)
{
return INT2FIX(0);
} Возвращает ноль.
static VALUE
num_infinite_p(VALUE num)
{
return Qnil;
} Возвращает -1, 1 или nil в зависимости от того, является ли значение конечным, бесконечно большим или бесконечно малым.
static VALUE
num_int_p(VALUE num)
{
return Qfalse;
} Возвращает true, если значение является целым числом (Integer).
1.0.integer? #=> false 1.integer? #=> true
static VALUE
num_abs(VALUE num)
{
if (rb_num_negative_int_p(num)) {
return num_funcall0(num, idUMinus);
}
return num;
} Возвращает абсолютное значение.
12.abs #=> 12 (-34.56).abs #=> 34.56 -34.56.abs #=> 34.56
Numeric#magnitude является псевдонимом для Numeric#abs.
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 означает остаток от деления.
Эквивалентно операции получения остатка от деления.
См. Numeric#divmod.
static VALUE
num_negative_p(VALUE num)
{
return rb_num_negative_int_p(num) ? Qtrue : Qfalse;
} Возвращает true, если значение меньше 0.
static VALUE
num_nonzero_p(VALUE num)
{
if (RTEST(num_funcall0(num, rb_intern("zero?")))) {
return Qnil;
}
return num;
} Возвращает self, если значение не равно нулю, и nil в противном случае.
Это поведение полезно при объединении сравнений:
a = %w( z Bb bB bb BB a aA Aa AA A )
b = a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a <=> b }
b #=> ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb", "z"]
static VALUE
numeric_numerator(VALUE self)
{
return f_numerator(f_to_r(self));
} Возвращает числитель.
static VALUE
numeric_arg(VALUE self)
{
if (f_positive_p(self))
return INT2FIX(0);
return DBL2NUM(M_PI);
} Возвращает 0, если значение положительное, и pi в противном случае.
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 (SIGNED_VALUE)num > (SIGNED_VALUE)INT2FIX(0) ? Qtrue : Qfalse;
}
else if (RB_TYPE_P(num, T_BIGNUM)) {
if (method_basic_p(rb_cInteger))
return BIGNUM_POSITIVE_P(num) && !rb_bigzero_p(num) ? Qtrue : Qfalse;
}
return rb_num_compare_with_zero(num, mid);
} Возвращает true, если значение больше 0.
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);
}
if (canonicalization) {
x = rb_rational_raw1(x);
}
else {
x = rb_convert_type(x, T_RATIONAL, "Rational", "to_r");
}
return nurat_div(x, y);
} Возвращает наиболее точное деление (рациональное для целых чисел, число с плавающей точкой для чисел с плавающей точкой).
static VALUE
numeric_real(VALUE self)
{
return self;
} Возвращает self.
static VALUE
num_real_p(VALUE num)
{
return Qtrue;
} Возвращает true, если значение является вещественным числом (т.е. не Complex).
static VALUE
numeric_rect(VALUE self)
{
return rb_assoc_new(self, INT2FIX(0));
} Возвращает массив; [num, 0].
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)))) {
return rb_funcall(z, '-', 1, y);
}
return z;
} Операция remainder означает остаток от деления.
См. Numeric#divmod.
static VALUE
num_round(int argc, VALUE* argv, VALUE num)
{
return flo_round(argc, argv, rb_Float(num));
} Возвращает значение, округлённое до ближайшего значения с точностью до заданного количества десятичных знаков (по умолчанию 0).
Numeric реализует это путём преобразования своего значения в 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);
}
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_TYPE_P(to, T_FLOAT)) {
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;
} Вызывает предоставленный блок с последовательностью чисел, начиная с начального значения, увеличивая его на шаг на каждом вызове.
Цикл завершается, когда значение, передаваемое блоку, становится больше предела (если шаг положительный) или меньше предела (если шаг отрицательный), где предел по умолчанию равен бесконечности.
В рекомендуемом стиле с ключевыми аргументами можно опустить один или оба аргумента, `предел` и `шаг` (по умолчанию бесконечность). В стиле с фиксированным позиционным аргументом шаг равный нулю (т.е. num.step(limit, 0)) запрещён по соображениям совместимости.
Если все аргументы являются целыми числами, цикл выполняется с использованием целочисленного счётчика.
Если хотя бы один из аргументов является числом с плавающей точкой, все аргументы преобразуются в числа с плавающей точкой, и цикл выполняется floor(n + n*Float::EPSILON) + 1 раз, где n = (предел - начальное значение)/шаг.
В противном случае цикл начинается с начального значения, использует оператор меньше (<) или больше (>) для сравнения счётчика с пределом и увеличивает счётчик на величину шага.
Если блок не предоставлен, возвращается Enumerator вместо этого. В частности, итератор является Enumerator::ArithmeticSequence, если и `начальное значение` и `предел` являются числами (Numeric) или nil.
Например:
p 1.step.take(4)
p 10.step(by: -1).take(4)
3.step(to: 5) {|i| print i, " " }
1.step(10, 2) {|i| print i, " " }
Math::E.step(to: Math::PI, by: 0.2) {|f| print f, " " }
Что выведет:
[1, 2, 3, 4] [10, 9, 8, 7] 3 4 5 1 3 5 7 9 2.718281828459045 2.9182818284590453 3.118281828459045
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);
} Вызывает метод дочернего класса to_i для преобразования num в целое число.
1.0.class #=> Float 1.0.to_int.class #=> Integer 1.0.to_i.class #=> Integer
static VALUE
num_truncate(int argc, VALUE *argv, VALUE num)
{
return flo_truncate(argc, argv, rb_Float(num));
} Возвращает num , усечённое (к нулю) до точности ndigits десятичных разрядов (по умолчанию: 0).
Numeric реализует это, преобразуя своё значение в Float и вызывая Float#truncate.
static VALUE
num_zero_p(VALUE num)
{
if (FIXNUM_P(num)) {
if (FIXNUM_ZERO_P(num)) {
return Qtrue;
}
}
else if (RB_TYPE_P(num, T_BIGNUM)) {
if (rb_bigzero_p(num)) {
/* this should not happen usually */
return Qtrue;
}
}
else if (rb_equal(num, INT2FIX(0))) {
return Qtrue;
}
return Qfalse;
} Возвращает true , если num имеет нулевое значение.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.