15 странностей в Ruby, о которых вам стоит знать

Автор оригинала: Jesus Castello
  • Перевод
Ruby — замечательный язык со множеством интересных деталей, которые вы могли раньше и не видеть.
В этом посте я собрал несколько таких деталей в список.

1. Heredoc + Метод


Если у вас есть какие-то текстовые данные, которые вы хотите встроить в программу, вы можете использовать “heredoc”. В результате вы получите строку, например так:

input = <<-IN
ULL
RRDDD
LURDL
IN

Но дополнительно к этому можно использовать пост-процессинг, например разделить текст по словам. Ruby позволяет делать такое:

input = <<-IN.split
ULL
RRDDD
LURDL
IN

А ещё в Ruby 2.3 появился «волнистый» heredoc <<~. Он удаляет все пробелы, использованные для отступов, распространённую проблему использования heredoc для текста.

2. Вызов метода двойным двоеточием


Может, кому-то пригодится.

"abc"::size
# 3
 
[1,2,3,4,5]::size
# 5

3. Puts с несколькими аргументами


Довольно простая вещь, но часто бывает полезна.

puts 1,2,3
1
2
3

4. Бесконечное взятие по индексу


Сразу пример:

words = ["abc", "foo"]
words[0][0][0][0][0]

Конечно же это работает потому, что [] — это просто метод, и просто возвращает первый символ строки, который тоже является строкой.

5. Деструктуризация аргументов блока


Хотите избавиться от пары локальных переменных? Вам это понравится.

a = [[1,2],[3,4]]
 
a.each do |(first, last)|
  # ...
end

Эквивалентно:

a = [[1,2],[3,4]]
 
a.each do |sub_array|
  first, last = sub_array
  # ...
end

Зато экономит строчку кода.

6. Специальные глобальные переменные


При использовании регексов со скобками будут определены специальные константы $1 для первой группы, $2 для второй и т.д. Стоит помнить, что они ведут себя не как обычные переменные: имеют другую область видимости (общую для метода и потока), не могут быть переназначены. Список их можно увидеть здесь.

$1 = 'test'
# SyntaxError: (eval):2: Can't set variable $1

7. Оператор присоединения со строкой


Оператор присоединения (<<) работает со строкой не как ожидается, если в качестве аргумента передать число:

"" << 97
# a

Он интерпретирует число как ASCII-код. Штатный способ для того же:

97.chr
# a

8. Символьные литералы


Не уверен, что это на самом деле кому-то пригодится.

?a
"a"
 
?aa
# Syntax error

Можете писать в комментах, что об этом думаете.

9. Модуль RbConfig


RbConfig — это недокументированный модуль, содержащий кое-какую инфу о вашей конфигурации Ruby. Например, в RbConfig::CONFIG содержатся флаги компиляции интерпретатора, версия, операционная система.

RbConfig.constants
# [:TOPDIR, :DESTDIR, :CONFIG, :MAKEFILE_CONFIG]

RbConfig::CONFIG['host_os']
# "linux-gnu"
 
RbConfig::CONFIG['ruby_version']
# "2.4.0"

10. Пробелы, везде пробелы!


Между вызываемым методом и получателем можно ставить сколько угодно пробелов.

a = [1,2,3]
 
a    [0]
a .size
a   . empty?

Ага, это валидный синтакс в Ruby.

11. Бесконечное наследование констант


String::String::Fixnum::Float

Пытливые умы догадались сразу: все константы верхнего уровня (определённые не внутри какого-либо класса) содержатся в классе Object, и могут быть вызваны любым классом, унаследованным от Object. Чтобы лучше понять, посмотрите на Object.constants в irb.

12. Последовательный оператор присоединения


Оператор << можно объединять в цепочки:

a = []
a << 1 << 2 << 3
# a = [1, 2, 3]

13. BEGIN и END


Два ключевых слова, которые используют довольно редко. Думаю, что они они пришли из мира Perl / Unix, где привычное действие — писать короткие скрипты для обработки вывода других программ. Как оно работает:

puts 123
BEGIN {
  puts "Program starting..."
}

Этот код выведет «Program starting...» ПЕРЕД «123». Мой читатель подсказывает, что это бывает полезно, когда нужно изменить путь к RUBYLIB для 'require', потому что будет гарантированно исполнено перед всеми «require». Также бывает полезно для установки $VERBOSE и других констант окружения.

14. ​ЧоЗаХрень


Я даже не знаю как и зачем это появилось в языке, и посоветую аккуратнее с этим обращаться. Мало кто знает об этой фиче, и её очень трудно понять. Но, предупреждён — значит вооружён:

if (condition1)..(condition2)
  # do something
end

Идея в том, что если первое условие истинно, то переключается невидимый рычаг, и с этого момента условие будет выполняться, пока второе условие тоже не станет истинным. Пример:

(1..20).each do |i|
  puts i if (i == 3)..(i == 15)
end

Оно напечатает все числа с 3 до 15, но если 15 будет пропущено в цикле, то оно так и продолжит печатать.

15. Ключевое слово redo


Ещё одно редко используемое ключевое слово, которое позволяет повторить итерацию в цикле.

10.times do |n|
  puts n
  redo
end

Только не забывайте ставить его под условие, иначе получится бесконечный цикл. Так что с этой фичей надо быть поосторожнее.
Поделиться публикацией

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 15

    0
    В Руби есть способ не конвертировать dictonary в array при использовании map? Можно конечно
    dict.map {|v| v[1] << "test" } 
    # или
    dict.map {|k, v| [k, v << "test"]}.to_h
    

    но это довольно некрасивая конструкция. Тогда как
    dict.map {|k, v| v << "test" }
    

    создаст array из значений.
      +1

      не dictionary, а hash.
      либо hash.reduce({}) { ... } / hash.each_with_object({}) { ... }


      либо рельсовые transform_keys и transform_values

        +1

        Я думаю, вам нужен такой сниппет:


        Hash[dict.map { |k, v| [k, "#{v}test"] }]

        Кстати, обратите внимание, что метод << меняет переменную, на которой вызывается, а это явно неожиданный результат для того, кто будет читать ваш код (от map ожидают создания копии и неизменения содержимого оригинальной переменной). Поэтому лучше использовать интерполяцию (заодно не будет ломаться на нестроковых значениях).


        Если же вам нужно именно изменить существующий хэш, то просто используйте each:


        dict.each { |_k, v| v << "test" }
        0

        Как по мне, всё нормально. Вот только: нафига лезть в незадокументированный модуль? Ведь почти тоже самое есть в глобальных константах RUBY_whatever, где whatever — нужное значение, например — RUBY_VERSION. Весь их список можно посмотреть в irb.

          +2

          А вот за <<~ спасибо — не знал

          –2
          Рубик не винауат
            +1

            14 из перла, конечно, появилось. Обычный триггер. Триггер переключается в истинное состояние, когда срабатывает первое условие и обратно, когда второе. Полезно когда нужно отпарсить кусок, начинающийся с какого-то "слова" и заканчивающийся другим. Тот же heredoc, например.

              +1

              И 13 пункт тоже строго из Perl.

                +4

                А в перле из awk :)

              +1

              За пункт 2 ваш рубокоп должен больно бить вас по рукам, не делайте так: https://github.com/bbatsov/ruby-style-guide#double-colons
              Пункт 14 называется flip-flop и его уже предлагали выпилить его из Ruby, потому что сильно взрывает мозг.

              • НЛО прилетело и опубликовало эту надпись здесь
                  0

                  За $N Rubocop тоже бьёт линейкой: https://github.com/bbatsov/ruby-style-guide#regular-expressions, возможно, кстати, как раз из-за этой недоваренности, но ещё там проблемы с многопоточностью были, КМК.

                  • НЛО прилетело и опубликовало эту надпись здесь
                      0

                      А когда нужно писать именно Proc.new.call(arg) и не работает просто yield arg? Или это именно та самая странность-странность (мне мозг подвзорвало, да)?


                      Ах да, сокращение вызова метода call просто до точки — это тоже прикольная и удобная странность, которую вовсю в широкие массы пихает автор Trailblazer'а.

                      • НЛО прилетело и опубликовало эту надпись здесь

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое