numbers = [1, 2, 3, 4, 5]
p numbers.map { |n| n ** 2 } # => [1, 4, 9, 16, 25]
p numbers.map { |n| n.to_s } # => ["1", "2", "3", "4", "5"]
В Crystal уже определено множество других методов, использующих блоки; наиболее распространенными являются те, которые используются для перебора элементов коллекции данных.
Как и while
и until
, ключевые слова next
и break
также можно использовать внутри блоков.
Использование next
внутри блока
Используйте next
, чтобы остановить текущее выполнение блока и вернуться к оператору yield
, который его вызвал. Если значение передается в next
, yield
получит выход. Посмотрите это, например:
def generate
first = yield 1 # This will be 2
second = yield 2 # This will be 10
third = yield 3 # This will be 4
first + second + third
end
result = generate do |x|
if x == 2
next 10
end
x + 1
end
p result
Метод generate
вызывает полученный блок три раза, а затем вычисляет сумму результатов. Наконец, этот метод вызывается, передавая блок, который может завершиться раньше при следующем вызове. Хорошей аналогией является то, что если бы блоки были методами, ключевое слово yield
действовало бы как вызов метода, а next
было бы эквивалентно return
.
Другой способ выйти из выполнения блока — использовать ключевое слово break
.
Использование break
внутри блока
Используйте break
, чтобы остановить метод, вызывающий блок, действуя так, как если бы он вернулся. Расширяя тот же пример, что и раньше, посмотрите на следующее:
result = generate do |x|
if x == 2
break 10 # break instead of next
end
x + 1
end
p result
В этом случае yield
1
будет равна 2
, но yield
2
никогда не вернется; вместо этого метод generate
будет сразу завершен, а result
получит значение 10
. Ключевое слово break
приводит к завершению метода, вызывающего блок.
Возвращение изнутри блока
Наконец, давайте посмотрим, как ведет себя return
при использовании внутри блока. Гипотеза Коллатца — это интересная математическая задача, которая предсказывает, что последовательность, в которой следующее значение вдвое превышает предыдущее, если оно четное, или в три раза больше плюс один, если оно нечетное, в конечном итоге всегда достигнет 1
, независимо от того, какое начальное число выбрано.
Следующий метод collatz_sequence
реализует эту последовательность, бесконечно вызывая блок для каждого элемента. Эта реализация не имеет условия остановки и может либо работать вечно, либо быть завершена раньше вызывающей стороной.
Затем следует реализация метода, который запускает collatz_sequence
с некоторым начальным значением и подсчитывает, сколько шагов необходимо, чтобы достичь 1
:
def collatz_sequence(n)
while true
n = if n.even?
n // 2
else
3 * n + 1
end
yield n
end
end
def sequence_length(initial)
length = 0
collatz_sequence(initial) do |x|
puts "Element: #{x}"
length += 1
if x == 1
return length # <= Note this 'return'
end
end
end
puts "Length starting from 14 is: #{sequence_length(14)}"
Метод sequence_length
отслеживает количество шагов и, как только оно достигает 1
, выполняет возврат. В этом случае обратите внимание, что возврат происходит внутри блока метода collatz_sequence
. Ключевое слово return
останавливает вызов блока (например, next
), останавливает метод, который вызвал блок с yield
(например, break
), но затем также останавливает метод, в котором записывается блок. Напоминаем, что return всегда завершает выполнение определения, которое находится внутри.
В этом примере кода выводится Length starting from 14 is: 17
. Фактически, гипотеза Коллатца утверждает, что этот код всегда найдет решение для любого положительного целого числа. Однако это нерешенная математическая проблема.
Контейнеры данных
Crystal имеет множество встроенных контейнеров данных, которые помогут вам манипулировать и организовывать нетривиальную информацию. Наиболее распространенным на сегодняшний день является массив. Вот краткий обзор наиболее часто используемых контейнеров данных в Crystaclass="underline"