Crystal Programming. Введение на основе проекта в создание эффективных, безопасных и читаемых веб-приложений и приложений CLI - Джордж Дитрих
В качестве интересной задачи давайте посмотрим, как получить общую численность населения всех стран вместе взятых. Мы можем использовать метод значений, чтобы получить массив счетчиков населения, а затем вызвать метод суммы для этого массива, чтобы агрегировать его:
puts "Total population: #{population.values.sum}"
Если вы попробуете этот код, вы увидите, что он не работает со следующим сообщением об ошибке:
Unhandled exception: Arithmetic overflow (OverflowError)
Проблема в том, что популяции — это экземпляр Hash(String, Int32), и поэтому вызов значений в нем приведет к созданию экземпляра Array(Int32). Если сложить эти значения, получится 4 503 002 371, но давайте напомним себе, что экземпляр Int32 может представлять только целые числа от -2 147 483 648 до 2 147 483 647.
Результат выходит за пределы этого диапазона и не помещается в экземпляр Int32. В этих случаях Crystal не выполнит операцию вместо автоматического повышения целочисленного типа или предоставления неверных результатов.
Одним из решений было бы с самого начала хранить счетчики населения как Int64, указав тип, как если бы мы делали это с пустым хешем:
population = {
"China" => 1_439_323_776,
"India" => 1_380_004_385,
# ...
"Mexico" => 128_932_753,
} of String => Int64
Другое решение — передать начальное значение методу суммы, используя правильный тип:
puts "Total population: #{population.values.sum(0_i64)}"
Теперь давайте посмотрим, как мы можем перебирать эти коллекции.
Итерация коллекций с блоками
При вызове метода можно передать блок кода, разделенный do...end. Несколько методов получают блок и работают с ним, многие из них позволяют каким-либо образом выполнять циклы. Первый пример — метод цикла. Это просто — он просто зацикливается навсегда, вызывая переданный блок:
loop do
puts "I execute forever"
end
Это прямой эквивалент использования while true:
while true
puts "I execute forever"
end
Два других очень полезных метода, которые берут блоки, — это times и each. Вызов times для целого числа приведет к повторению блока указанное количество раз, а вызов каждого из коллекции вызовет блок для каждого элемента:
5.times do
puts "Hello!"
end
(10..15).each do |x|
puts "My number is #{x}"
end
["apple", "orange", "banana"].each do |fruit|
puts "Don't forget to buy some #{fruit}s!"
end
В предыдущем примере показано, как можно использовать блоки для обхода некоторой коллекции. При написании кода Crystal это предпочтительнее, чем итерация с помощью цикла while. Несколько методов из стандартной библиотеки принимают блок: мы видели каждый, но есть также карта для преобразования каждого элемента во что-то другое, выбора или отклонения для фильтрации элементов на основе некоторого условия и сокращения для вычисления значения на основе каждого элемента.
Синтаксис короткого блока
Очень частым случаем является вызов метода, передающего блок, имеющий только один аргумент, а затем вызов метода для этого аргумента. Например, предположим, что у нас есть массив строк, и мы хотим преобразовать их все в заглавные буквы. Вот три способа написать это:
fruits = ["apple", "orange", "banana"]
# (1) Prints ["APPLE", "ORANGE", "BANANA"]
p(fruits.map do |fruit| fruit.upcase
end)
# (2) Same result, braces syntax
p fruits.map { |fruit| fruit.upcase }
# (3) Same result, short block syntax
p fruits.map &.upcase
В первом фрагменте (1) использовался метод карты вместе с блоком do... end. Метод map выполняет итерацию по массиву, передавая блок для каждого элемента и создавая новый массив с результатом блока. В этом первом примере необходимы круглые скобки, поскольку do...end блоки подключаются к самому внешнему методу, в данном случае p.
Второй фрагмент (2) использует синтаксис { ... } и может опускать круглые скобки, поскольку этот блок подключается к ближайшему вызову метода. Обычно синтаксис { ... } записывается в одну строку, но это не обязательно.
Наконец, мы видим синтаксис коротких блоков в третьем фрагменте (3). Написание &.foo аналогично использованию { |x| x.foo }. Его также можно записать как p fruits.map(&.upcase), как если бы блок был общим аргументом вызова метода.
Отличается только синтаксис; поведение и семантика всех трех фрагментов одинаковы. Обычно везде, где это возможно, используется синтаксис коротких блоков.
Контейнер Tuple также отображается в определениях методов при использовании параметров splat.
Параметры сплата (Splat)
Метод можно определить так, чтобы он принимал произвольное количество аргументов, используя параметры splat. Это делается путем добавления символа * перед именем параметра: теперь при вызове метода он будет ссылаться на кортеж с нулевым или более значениями аргументов. Посмотрите это, например:
def get_pop(population, *countries)
puts "Requested countries: #{countries}"
countries.map { |country| population[country] }
end
puts get_pop(population, "Indonesia", "China", "United States")
Этот код даст следующий результат:
Requested countries: {"Indonesia", "China", "United States"}
{273523615, 1439323776, 331002651}
Использование splat всегда будет создавать кортежи правильных типов, как если бы метод имел такое количество обычных позиционных параметров. В этом примере typeof(countries) будет Tuple(String, String, String); тип будет меняться при каждом использовании. Параметры Splat — наиболее распространенный вариант использования кортежей.
Организация вашего кода в файлах
Написание кода в одном файле подходит для некоторых быстрых тестов или очень небольших приложений, но все остальное в конечном итоге придется
Откройте для себя мир чтения на siteknig.com - месте, где каждая книга оживает прямо в браузере. Здесь вас уже ждёт произведение Crystal Programming. Введение на основе проекта в создание эффективных, безопасных и читаемых веб-приложений и приложений CLI - Джордж Дитрих, относящееся к жанру Программирование. Никаких регистраций, никаких преград - только вы и история, доступная в полном формате. Наш литературный портал создан для тех, кто любит комфорт: хотите читать с телефона - пожалуйста; предпочитаете ноутбук - идеально! Все книги открываются моментально и представлены полностью, без сокращений и скрытых страниц. Каталог жанров поможет вам быстро найти что-то по настроению: увлекательный роман, динамичное фэнтези, глубокую классику или лёгкое чтение перед сном. Мы ежедневно расширяем библиотеку, добавляя новые произведения, чтобы вам всегда было что открыть "на потом". Сегодня на siteknig.com доступно более 200000 книг - и каждая готова стать вашей новой любимой. Просто выбирайте, открывайте и наслаждайтесь чтением там, где вам удобно.


