класс 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 #=> TypeError: can't dup Fixnum
По этой причине, 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)
{
return rb_funcall(x, '-', 1,
rb_funcall(y, '*', 1,
rb_funcall(x, id_div, 1, y)));
} 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 rb_funcall(zero, '-', 1, 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 rb_funcall(num, idUMinus, 0);
}
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 rb_const_get(rb_mMath, id_PI);
} Возвращает 0, если значение положительное, в противном случае — pi.
static VALUE
numeric_arg(VALUE self)
{
if (f_positive_p(self))
return INT2FIX(0);
return rb_const_get(rb_mMath, id_PI);
} Возвращает 0, если значение положительное, в противном случае — pi.
static VALUE
num_ceil(VALUE num)
{
return flo_ceil(rb_Float(num));
} Возвращает наименьшее возможное целое число, которое больше или равно 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);
} Если число имеет тот же тип, что и num, возвращает массив, содержащий numeric и num. В противном случае возвращает массив с числом и 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(rb_funcall(x, '/', 1, 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;
return rb_equal(x, y);
} Возвращает true если num и 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_floor(VALUE num)
{
return flo_floor(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_int_p(VALUE num)
{
return Qfalse;
} Возвращает true если num является целым числом (включая Fixnum и Bignum).
(1.0).integer? #=> false (1).integer? #=> true
static VALUE
num_abs(VALUE num)
{
if (negative_int_p(num)) {
return rb_funcall(num, idUMinus, 0);
}
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)
{
return rb_funcall(x, '-', 1,
rb_funcall(y, '*', 1,
rb_funcall(x, id_div, 1, y)));
} 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, если значение меньше 0.
static VALUE
num_nonzero_p(VALUE num)
{
if (RTEST(rb_funcallv(num, rb_intern("zero?"), 0, 0))) {
return Qnil;
}
return 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 rb_const_get(rb_mMath, id_PI);
} Возвращает 0, если значение положительное, и π в противном случае.
static VALUE
numeric_polar(VALUE self)
{
return rb_assoc_new(f_abs(self), f_arg(self));
} Возвращает массив; [num.abs, num.arg].
static VALUE
num_positive_p(VALUE num)
{
const ID mid = '>';
if (FIXNUM_P(num)) {
if (method_basic_p(rb_cFixnum))
return (SIGNED_VALUE)num > (SIGNED_VALUE)INT2FIX(0) ? Qtrue : Qfalse;
}
else if (RB_TYPE_P(num, T_BIGNUM)) {
if (method_basic_p(rb_cBignum))
return BIGNUM_POSITIVE_P(num) && !rb_bigzero_p(num) ? Qtrue : Qfalse;
}
return compare_with_zero(num, mid);
} Возвращает true, если значение больше 0.
static VALUE
numeric_quo(VALUE x, VALUE y)
{
if (RB_TYPE_P(y, T_FLOAT)) {
return f_fdiv(x, y);
}
#ifdef CANON
if (canonicalization) {
x = rb_rational_raw1(x);
}
else
#endif
{
x = rb_convert_type(x, T_RATIONAL, "Rational", "to_r");
}
return rb_funcall(x, '/', 1, 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 = rb_funcall(x, '%', 1, 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));
} Округляет значение до заданной точности в десятичных знаках (по умолчанию 0 знаков).
Точность может быть отрицательной. Возвращает число с плавающей точкой, если точность больше нуля.
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.
Числа должны быть значениями; к ним не должны добавляться методы singleton.
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 (RTEST(rb_num_coerce_cmp(step, INT2FIX(0), id_eq))) {
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;
} Вызывает предоставленный блок с последовательностью чисел, начинающейся с указанного значения, увеличивая его на указанный шаг (по умолчанию 1) на каждом вызове.
Цикл завершается, когда значение, которое будет передано в блок, больше чем предел (если шаг положительный) или меньше чем предел (если шаг отрицательный). Предел по умолчанию равен бесконечности.
В рекомендуемом стиле с ключевыми аргументами можно опустить один или оба аргумента step и limit (по умолчанию бесконечность). В стиле с фиксированными аргументами шаг 0 (т.е. num.step(limit, 0)) не разрешен по соображениям совместимости.
Если все аргументы — целые числа, цикл работает с целочисленным счетчиком.
Если какой-либо из аргументов — число с плавающей точкой, все аргументы преобразуются в числа с плавающей точкой, и цикл выполняется по следующему выражению:
floor(n + n*epsilon)+ 1
Где <%%CODE_BLOCK_114%%> - следующее:
n = (limit - num)/step
В противном случае цикл начинается с начального значения, использует оператор < или >, чтобы сравнить счетчик с пределом, и увеличивает его с помощью оператора +.
Если блок не указан, возвращается перечислитель Enumerator.
Например:
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 rb_funcallv(num, id_to_i, 0, 0);
} Вызывает метод дочернего класса для преобразования значения в целое число.
1.0.class => Float 1.0.to_int.class => Fixnum 1.0.to_i.class => Fixnum
static VALUE
num_truncate(VALUE num)
{
return flo_truncate(rb_Float(num));
} Возвращает значение, усеченное до целого числа.
Numeric реализует это, преобразуя своё значение в Float и вызывая Float#truncate.
static VALUE
num_zero_p(VALUE num)
{
if (rb_equal(num, INT2FIX(0))) {
return Qtrue;
}
return Qfalse;
} Возвращает true, если значение равно нулю.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.