класс 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:
-
Наследует от класса Объект.
-
Включает модуль 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, сменённое на противоположное. -
abs2: Возвращает квадратself. -
angle(алиасargиphase): Возвращает 0, еслиselfположительно, Math::PI в противном случае. -
ceil: Возвращает наименьшее число, большее или равноеself, с заданной точностью. -
coerce: Возвращает массив[coerced_self, coerced_other]для заданного другого значения. -
conj(алиасconjugate): Возвращает комплексно сопряжённое значениеself. -
denominator: Возвращает знаменатель (всегда положительный) представленияRationalself. -
div: Возвращает результат деленияselfна заданное значение, преобразованный в целое число. -
divmod: Возвращает массив[quotient, modulus]в результате деленияselfна заданный делитель. -
fdiv: ВозвращаетFloatрезультат деленияselfна заданный делитель. -
floor: Возвращает наибольшее число, меньшее или равноеself, с заданной точностью. -
i: Возвращает объектComplexComplex(0, self). для заданного значения. -
numerator: Возвращает числитель представленияRationalself; имеет тот же знак, что и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: Возвращает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 использует эту реализацию.
Для 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);
} Унарный минус — возвращает принимающий объект с обратным знаком.
Исходный код
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
Исходный код
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));
} Возвращает наименьшее число с плавающей точкой или целое число, которое больше или равно 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);
} Возвращает 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_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 используют эту реализацию.
Исходный код
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 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));
} Возвращает наибольшее число с плавающей точкой или целое число, которое меньше или равно 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 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;
} Returns +self+ if +self+ is not a zero value, +nil+ otherwise;
uses method <tt>zero?</tt> for the evaluation.
The returned +self+ allows the method to be chained:
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"]
Of the Core and Standard Library classes,
Integer, Float, Rational, and Complex use this implementation. Связанные: 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
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.
Из классов Ядра и Стандартной библиотеки, только 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;
} Генерирует последовательность чисел; при наличии блока, проходит по последовательности.
Из классов Ядра и Стандартной библиотеки, 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 = (предел - self)/шаг.
Исходный код
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 в производном классе.
Из классов Ядра и Стандартной библиотеки, только 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.
Исходный код
Ruby Core © 1993–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.