litbaza книги онлайнРазная литератураCrystal Programming. Введение на основе проекта в создание эффективных, безопасных и читаемых веб-приложений и приложений CLI - Джордж Дитрих

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 11 12 13 14 15 16 17 18 19 ... 75
Перейти на страницу:
{pop} P к } people." hash.each value { End |v| p v } hash.has key?(key) Проверяет, существует ли данный ключ или значение в хеш-структуре. hash.has value?(val) hash.key for(value) Находит пару с заданным значением и возвращает ее ключ. Эта операция является дорогостоящей, поскольку ей приходится искать все пары одну за другой. hash.key for?(value) hash.keys Создает массив всех ключей или массив всех значений хеша. hash.values

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

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 — наиболее распространенный вариант использования кортежей.

Организация вашего кода в файлах

Написание кода в одном файле подходит для некоторых быстрых тестов или очень небольших приложений, но все остальное в конечном итоге придется

1 ... 11 12 13 14 15 16 17 18 19 ... 75
Перейти на страницу:

Комментарии
Минимальная длина комментария - 20 знаков. Уважайте себя и других!
Комментариев еще нет. Хотите быть первым?