класс Numeric
Numeric — это класс, от которого должны наследоваться все числовые классы более высокого уровня.
Numeric позволяет создавать экземпляры объектов, размещаемых в куче. Другие основные числовые классы, такие как Integer, реализуются как непосредственные (иммедиат), что означает, что каждый Integer представляет собой один неизменяемый объект, который всегда передаётся по значению.
a = 1 puts 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 (см. 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) means x-y*(x/y).floor
Эквивалентно num.divmod(numeric)[1].
См. #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 (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
#magnitude является псевдонимом #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));
} Возвращает наименьшее возможное Integer, которое больше или равно num.
Numeric достигает этого, преобразуя себя в Float, а затем вызывая Float#ceil.
1.ceil #=> 1 1.2.ceil #=> 2 (-1.2).ceil #=> -1 (-1.0).ceil #=> -1
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].
См. #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_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 68719476736.eql?(68719476736.0) #=> false
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.
Numeric реализует это, преобразуя Integer в Float и вызывая Float#floor.
1.floor #=> 1 (-1).floor #=> -1
static VALUE
num_imaginary(VALUE num)
{
return rb_complex_new(INT2FIX(0), num);
} Возвращает соответствующее мнимое число. Недоступно для комплексных чисел.
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;
} Возвращает значения, соответствующие значению величины num:
-
finite -
nil -
-Infinity -
-1 - +
Infinity -
+
1
static VALUE
num_int_p(VALUE num)
{
return Qfalse;
} Возвращает true или false, если num является целым числом (Integer).
(1.0).integer? #=> false (1).integer? #=> true
static VALUE
num_abs(VALUE num)
{
if (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
#magnitude — это псевдоним #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));
} x.modulo(y) means x-y*(x/y).floor
Эквивалентно num.divmod(numeric)[1].
См. #divmod.
static VALUE
num_negative_p(VALUE num)
{
return negative_int_p(num) ? Qtrue : Qfalse;
} Возвращает true, если num меньше 0.
static VALUE
num_nonzero_p(VALUE num)
{
if (RTEST(num_funcall0(num, rb_intern("zero?")))) {
return Qnil;
}
return num;
} Возвращает self, если num не равно нулю, и 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 compare_with_zero(num, mid);
} Возвращает true, если num больше 0.
static VALUE
numeric_quo(VALUE x, VALUE y)
{
if (RB_FLOAT_TYPE_P(y)) {
return rb_funcall(x, rb_intern("fdiv"), 1, y);
}
#ifdef CANON
if (canonicalization) {
x = rb_rational_raw1(x);
}
else
#endif
{
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, если num — вещественное число (т.е. не 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))) &&
((negative_int_p(x) &&
positive_int_p(y)) ||
(positive_int_p(x) &&
negative_int_p(y)))) {
return rb_funcall(z, '-', 1, y);
}
return z;
} x.remainder(y) means x-y*(x/y).truncate
См. #divmod.
static VALUE
num_round(int argc, VALUE* argv, VALUE num)
{
return flo_round(argc, argv, rb_Float(num));
} Округляет num до заданной точности в десятичных разрядах (по умолчанию 0 разрядов).
Точность может быть отрицательной. Возвращает число с плавающей точкой, когда ndigits больше нуля.
Numeric реализует это, преобразуя себя в Float и вызывая Float#round.
static VALUE
num_sadded(VALUE x, VALUE name)
{
ID mid = rb_to_id(name);
/* ruby_frame = ruby_frame->prev; */ /* pop frame for "singleton_method_added" */
rb_remove_method_id(rb_singleton_class(x), mid);
rb_raise(rb_eTypeError,
"can't define singleton method \"%"PRIsVALUE"\" for %"PRIsVALUE,
rb_id2str(mid),
rb_obj_class(x));
UNREACHABLE;
} Перехватывает попытки добавить методы к объектам Numeric. Всегда вызывает TypeError.
Числа должны быть значениями; одиночные методы не должны к ним добавляться.
static VALUE
num_step(int argc, VALUE *argv, VALUE from)
{
VALUE to, step;
int desc, inf;
RETURN_SIZED_ENUMERATOR(from, argc, argv, num_step_size);
desc = num_step_scan_args(argc, argv, &to, &step);
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)) {
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 (по умолчанию 1) на каждом вызове.
Цикл завершается, когда значение, передаваемое в блок, становится больше limit (если step положительно) или меньше limit (если step отрицательно), где limit по умолчанию равен бесконечности.
В рекомендуемом стиле ключевых аргументов, один или оба из step и limit (бесконечность по умолчанию) могут быть опущены. В стиле аргументов фиксированной позиции шаг 0 (т.е. num.step(limit, 0)) не допускается по причинам совместимости с прошлыми версиями.
Если все аргументы являются целыми числами, цикл работает с целочисленным счётчиком.
Если какие-либо аргументы являются числами с плавающей точкой, все они преобразуются в числа с плавающей точкой, и цикл выполняется по следующему выражению:
floor(n + n*epsilon)+ 1
Где n следующее:
n = (limit - num)/step
В противном случае цикл начинается с num, использует оператор меньше (<) или больше (>) для сравнения счётчика с limit, и увеличивает его с помощью оператора +.
Если блок не указан, возвращается итератор вместо него.
Например:
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.71828182845905 2.91828182845905 3.11828182845905
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, усеченное до целого числа.
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.