Ruby Integer (), Array () и др. Что это? Откуда они? - PullRequest
5 голосов
/ 11 января 2010

Я иногда сталкивался с преобразованиями вида Array (значение), String (значение) и Integer (значение). Мне кажется, что это всего лишь синтаксический сахар для вызова соответствующих методов value.to_a, value.to_s или value.to_i.

Вот мне и интересно:

  • Где / как они определены? Я не могу найти их в объекте, модуле, классе и т. Д.
  • Существуют ли распространенные сценарии, для которых предпочтительнее использовать их, а не соответствующий / лежащий в основе метод to_X?
  • Могут ли они быть использованы при принуждении общего типа? То есть я могу сделать что-то вроде

    [Integer, String, Array].each {|klass| klass.do_generic_coercion(foo) }
    

? (... и нет, я на самом деле не хочу этого делать; я знаю, какой тип я хочу получить, но я стараюсь избегать выражения case).

Ответы [ 4 ]

10 голосов
/ 11 января 2010

Это хороший и сложный вопрос. Давайте ответим на три части.

Первая часть

Чтобы найти определение, важно понимать, что имя метода - «Массив» и т. Д., Что может быть довольно нелогичным, поскольку методы обычно строчные ...

irb> method(:Array)
=> #<Method: Object(Kernel)#Array>

Это говорит о том, что они определены в ядре и, следовательно, доступны везде, не требуя явного префикса.

Вторая часть

Array(), String(), ... - методы преобразования. Вызов obj.to_a вернет массив, но вызовет NoMethodError, если obj не respond_to? :to_a. Поэтому типичный случай, когда вы предпочитаете использовать Array(), String() вместо to_a или to_s, это когда вы не уверены, что объект реагирует на данный метод преобразования.

String(obj) вернет nil, если obj не respond_to? :to_s. String (obj) также проверит, что результат to_s действительно является строкой; так и должно быть, но, может быть, чрезмерно креативный программист решил вернуть что-то еще?

Большинство других методов преобразования действуют так же, но Array(obj) отличается. Он вернет [obj], если obj не respond_to? :to_a. На самом деле он вызовет to_ary (что является неявной операцией преобразования, в то время как to_a является явной).

Существует еще один важный способ преобразования объектов в 1.9 (и в готовящемся 1.8.8): Array.try_convert(obj). Возвращает nil, если объект не respond_to? :to_ary. Это не будет называть to_a. Хотя они длиннее для ввода, вы можете предпочесть использовать их при написании очень общего кода, который может принимать различные типы объектов и, например, хотеть избежать преобразования хеш-функции в массив (поскольку Hash имеет метод to_a но не to_ary). Когда вашему методу требуется массивоподобный объект, и вы хотите сделать явное преобразование, тогда obj.to_a - это хорошо. Типичное использование Array(obj) было бы в методе, который принимает либо один obj для действия, либо список объектов (хотя обычно это записывается как [*obj]).

Последняя часть

Надеюсь, ответы на первые две части дадут вам окончательный ответ ...

Вы можете использовать:

[Integer, String, Array].each {|klass| klass.try_convert(foo) }

или

[:Integer, :String, :Array].each{|method| send(method, obj)}
8 голосов
/ 11 января 2010

Хороший вопрос! Посмотрим, сможем ли мы это выяснить.

Ross-Harveys-MacBook-Pro:ruby-1.9.1-p376 ross$ irb
irb(main):001:0> Object.ancestors
=> [Object, Kernel]
irb(main):002:0> Kernel.ancestors
=> [Kernel]
irb(main):003:0> Kernel.class
=> Module
irb(main):004:0> Kernel.public_methods.include? "Array"
=> true

Итак, похоже, что эти методы в модуле ядра смешаны с объектом, поэтому они доступны без указания получателя. Мы также можем захотеть взглянуть на реализацию C в object.c:

VALUE
rb_Array(VALUE val)
{
    VALUE tmp = rb_check_array_type(val);

    if (NIL_P(tmp)) {
        tmp = rb_check_convert_type(val, T_ARRAY, "Array", "to_a");
        if (NIL_P(tmp)) {
            return rb_ary_new3(1, val);
        }
    }
    return tmp;
}

Одна вещь кажется простой для вывода, значение по умолчанию .to_a устарело, поэтому кажется, что Array(x) - канонический способ сделать преобразование Очевидно, он ничего не делает, если ему дан массив, вызывает .to_a, если он присутствует, а если нет, то просто оборачивает свой аргумент в массив.

Относительно того, является ли to_a устаревшим ... ну, я сказал "по умолчанию":

Ross-Harveys-MacBook-Pro:puppet_sd ross$ irb
irb(main):001:0> class X; X; end.new.to_a
(irb):1: warning: default `to_a' will be obsolete
0 голосов
/ 11 января 2010

Насколько я понимаю, простая версия выглядит так:

  • object.to_a пытается преобразовать «объект» в массив, используя функцию-член класса.
  • Array(object) пытается создать новый массив с использованием объекта.

Я могу переопределить, что означает .to_a для данного класса (в конце концов, это просто еще один член). Вызов Array(...) определен в ядре, поэтому он ведет себя одинаково для любого класса. Обычно я использую преобразования типов в стиле Array(...), когда заранее не знаю, какой тип объекта будет передан. Лучше обрабатывать случаи, когда объект не знает, как преобразовать себя в массив или может не будет преобразован в массив. Если преобразуемый объект является результатом длинного или сложного выражения, использование стиля Array(...) часто более понятно. Я сохраняю форму .to_a для случаев, когда я знаю класс объекта и что именно ожидать от вывода .to_a (в основном, когда я сам написал или изменил функцию-член .to_a).

0 голосов
/ 11 января 2010

Они определены в модуле ядра Ruby, например:

Array(), Complex(), Float(), Integer(), Rational(), Stirng(), etc.

Я нашел эти ссылки на методики в книге Кирка Дейва Томаса "Progamming Ruby 1.9 ", стр. 555.

Например: Array (arg) преобразует arg как Array, из книги скопированы следующие: «Возвращает arg как массив. Сначала пытается вызвать rg.to_ary, затем arg.to_a. Если оба не удаются, создает массив из одного элемента, содержащий arg (или пустой массив, если arg равен nil). ех.

Array(1..5)   # => [1, 2, 3, 4, 5] 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...