класс 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:
-
Наследуется от класса Object.
-
Подключает модуль Comparable.
Здесь класс Numeric предоставляет методы для:
Проверка
-
finite?: возвращает true, еслиselfне является бесконечным или не является числом. -
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и заданное значение одинаковые значение и тип.
Преобразование
-
%(также доступен под именемmodulo): возвращает остаток от деленияselfна заданное значение. -
-@: возвращает значениеselfс противоположным знаком. -
abs(также доступен под именемmagnitude): возвращает абсолютное значениеself. -
abs2: возвращает квадратself. -
angle(также доступен под именамиargиphase): возвращает 0, еслиselfположительно, иначе — Math::PI. -
ceil: возвращает наименьшее число, большее или равноеself, с заданной точностью. -
coerce: возвращает массив[coerced_self, coerced_other]для заданного другого значения. -
conj(также доступен под именемconjugate): возвращает комплексно-сопряженное значениеself. -
denominator: возвращает знаменатель (всегда положительный) рационального представленияRationalчислаself. -
div: возвращает значениеself, деленное на заданное значение и преобразованное в целое число. -
divmod: возвращает массив[quotient, modulus], полученный в результате деленияselfна заданный делитель. -
fdiv: возвращает результат деленияselfна заданный делитель в виде числаFloat. -
floor: возвращает наибольшее число, меньшее или равноеself, с заданной точностью. -
i: возвращает объектComplexComplex(0, self)заданное значение. -
imaginary(также доступен под именемimag): возвращает мнимую часть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_int: возвращает представлениеselfв виде числаInteger, при необходимости отбрасывая дробную часть. -
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 в виде вещественного числового значения (Integer, Float или Rational).
Среди классов Core и Standard Library только 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_uminus(VALUE num)
{
VALUE zero;
zero = INT2FIX(0);
do_coerce(&zero, &num, TRUE);
return num_funcall1(zero, '-', num);
} Возвращает self с противоположным знаком.
static VALUE
num_cmp(VALUE x, VALUE y)
{
if (x == y) return INT2FIX(0);
return Qnil;
} Сравнивает self и other.
Возвращает:
-
Ноль, если
selfсовпадает сother. -
nilв противном случае.
Класс Numeric включает модуль Comparable, каждый метод которого использует Numeric#<=> для сравнения.
Ни один подкласс из Ruby Core или Standard Library не использует эту реализацию.
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
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);
} Возвращает ноль, если self положительно, и Math::PI в противном случае.
static VALUE
num_ceil(int argc, VALUE *argv, VALUE num)
{
return flo_ceil(argc, argv, rb_Float(num));
} Возвращает наименьшее значение типа float или integer, которое больше или равно self, с точностью, заданной аргументом ndigits, который должен быть объектом, преобразуемым в целое число.
Эквивалентно self.to_f.ceil(ndigits).
Связанные методы: floor, 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);
} Возвращает массив из двух элементов, содержащий два числовых элемента общего совместимого типа, сформированных из двух операндов self и other.
Среди классов Core и Standard Library эту реализацию используют 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)]
Вызывает исключение, если преобразование типа завершается неудачно.
# File numeric.rb, line 78 def conjugate self end
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);
} static VALUE
num_divmod(VALUE x, VALUE y)
{
return rb_assoc_new(num_div(x, y), num_modulo(x, y));
} Возвращает массив из двух элементов [q, r], где
q = (self/other).floor # Quotient r = self % other # Remainder
Среди классов Core и Standard Library только 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)]
# File numeric.rb, line 9 def dup self end
Возвращает self.
Связанный метод: Numeric#clone.
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 имеют одинаковый тип и равные значения.
Среди классов Core и Standard Library эту реализацию используют только 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 в виде числа с плавающей точкой, используя метод /, определённый в подклассе Numeric. (Сам Numeric не определяет /.)
Среди классов Core и Standard Library только BigDecimal использует эту реализацию.
# File numeric.rb, line 48 def finite? true end
Возвращает true, если self является конечным числом, и false в противном случае.
static VALUE
num_floor(int argc, VALUE *argv, VALUE num)
{
return flo_floor(argc, argv, rb_Float(num));
} Возвращает наибольшее значение типа float или integer, которое меньше или равно self, с точностью, заданной аргументом ndigits, который должен быть объектом, преобразуемым в целое число.
Эквивалентно self.to_f.floor(ndigits).
Связанные методы: ceil, 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.
# File numeric.rb, line 67 def imaginary 0 end
# File numeric.rb, line 58 def infinite? nil end
Возвращает nil, -1 или 1 в зависимости от того, является ли self конечным, -Infinity или +Infinity.
# File numeric.rb, line 39 def integer? false end
Возвращает true, если self является Integer.
1.0.integer? # => false 1.integer? # => true
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?.
Возвращаемое значение 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"]
Среди классов Core и Standard Library эту реализацию используют Integer, Float, Rational и Complex.
Связанный метод: zero?
static VALUE
numeric_numerator(VALUE self)
{
return f_numerator(f_to_r(self));
} Возвращает числитель.
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);
} Возвращает массив [self.abs, self.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);
} Возвращает результат деления с максимально возможной точностью (рациональное число для целых чисел, число с плавающей точкой для чисел с плавающей точкой).
# File numeric.rb, line 18 def real? true end
Возвращает true, если self является вещественным числом (то есть не Complex).
static VALUE
numeric_rect(VALUE self)
{
return rb_assoc_new(self, INT2FIX(0));
} 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;
} Возвращает остаток от деления self на other.
Среди классов Core и Standard Library эту реализацию используют только 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 десятичных знаков.
Numeric реализует это преобразованием 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 (!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;
} Генерирует последовательность чисел; если задан блок, перебирает эту последовательность.
Среди классов Core и Standard Library эту реализацию используют Integer, Float и Rational.
Краткий пример:
squares = []
1.step(by: 2, to: 10) {|i| squares.push(i*i) }
squares # => [1, 9, 25, 49, 81]
Сгенерированная последовательность:
-
Начинается с
self. -
Продолжается с интервалом
by(который не может быть равен нулю). -
Заканчивается последним числом, которое не превышает
to; то есть меньше или равноto, еслиbyположительно, и больше или равноto, еслиbyотрицательно. Еслиtoравноnil, последовательность имеет бесконечную длину.
Если задан блок, вызывает его для каждого числа последовательности; возвращает self. Если блок не задан, возвращает Enumerator::ArithmeticSequence.
Именованные аргументы
При использовании именованных аргументов by и to их значения (или значения по умолчанию) определяют шаг и предел:
# 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
Позиционные аргументы
При использовании необязательных позиционных аргументов to и by их значения (или значения по умолчанию) определяют шаг и предел:
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]
Примечания к реализации
Если все аргументы являются целыми числами, цикл выполняется с использованием целочисленного счётчика.
Если какой-либо аргумент является числом с плавающей точкой, все аргументы преобразуются в числа с плавающей точкой, а цикл выполняется floor(n + n*Float::EPSILON) + 1 раз, где n = (limit - self)/step.
static VALUE
numeric_to_c(VALUE self)
{
return rb_complex_new1(self);
} Возвращает self в виде объекта Complex.
static VALUE
num_to_int(VALUE num)
{
return num_funcall0(num, id_to_i);
} Возвращает self в виде целого числа; преобразование выполняется с помощью метода to_i, определённого в подклассе Numeric. (Сам Numeric не определяет to_i.)
Среди классов Core и Standard Library эту реализацию используют только Rational и Complex.
Примеры:
Rational(1, 2).to_int # => 0 Rational(2, 1).to_int # => 2 Complex(2, 0).to_int # => 2 Complex(2, 1).to_int # 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.
static VALUE
num_zero_p(VALUE num)
{
return rb_equal(num, INT2FIX(0));
}
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.