class 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:
-
Наследуется от class Object.
-
Включает module 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: Возвращает знаменатель (всегда положительный) представленияRationalself. -
div: Возвращает значениеself, деленное на заданное значение и преобразованное в целое число. -
divmod: Возвращает массив[quotient, modulus], полученный в результате деленияselfна заданный делитель. -
fdiv: Возвращает результат деленияselfна заданный делитель в форматеFloat. -
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: Возвращает представлениеIntegerself, усекая при необходимости. -
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_uplus(VALUE num)
{
return num;
} Возвращает self.
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 с точностью до digits десятичных знаков.
Numeric реализует это путем преобразования self в Float и вызова 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 38 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 с точностью digits десятичных знаков.
Numeric реализует это, преобразуя self в Float и вызывая 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 48 def infinite? nil end
Возвращает -1, 1 или nil в зависимости от того, является ли self конечным, -Infinity, или +Infinity.
# File numeric.rb, line 29 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? для оценки.
Возвращаемое значение позволяет использовать метод в цепочке:
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 используют эту реализацию.
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 17 def real self end
Возвращает self.
# File numeric.rb, line 8 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.
Из 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));
} Возвращает значение, округлённое до ближайшего целого с точностью 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;
} Generates a sequence of numbers; with a block given, traverses the sequence.
Of the Core and Standard Library classes,
Integer, Float, and Rational use this implementation.
A quick example:
squares = []
1.step(by: 2, to: 10) {|i| squares.push(i*i) }
squares # => [1, 9, 25, 49, 81]
The generated sequence:
- Begins with +self+.
- Continues at intervals of +by+ (which may not be zero).
- Ends with the last number that is within or equal to +to+;
that is, less than or equal to +to+ if +by+ is positive,
greater than or equal to +to+ if +by+ is negative.
If +to+ is +nil+, the sequence is of infinite length.
If a block is given, calls the block with each number in the sequence;
returns +self+. If no block is given, returns an Enumerator::ArithmeticSequence.
<b>Keyword Arguments</b>
With keyword arguments +by+ and +to+,
their values (or defaults) determine the step and limit:
# 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
<b>Positional Arguments</b>
With optional positional arguments +to+ and +by+,
their values (or defaults) determine the step and limit:
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] Примечания к реализации
If all the arguments are integers, the loop operates using an integer counter. If any of the arguments are floating point numbers, all are converted to floats, and the loop is executed <i>floor(n + n*Float::EPSILON) + 1</i> times, where <i>n = (limit - self)/step</i>.
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) # 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–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.