Список — коллекция элементов. В отличие от кортежа, в список можно динамически добавлять и удалять элементы.
Список совсем не похож на массив ни по своему внутреннему устройству, ни по способу работы с ним. По внутреннему устройству это однонаправленный связанный список (singly linked list). А работа с ним сводится к двум простым операциям: добавление нового элемента к голове списка, и разделение списка на голову и хвост.
Создадим список, и добавим в него новый элемент:
my_list = [1, 2, 3, 4] # [1, 2, 3, 4]
other_list = [0 | my_list] # [0, 1, 2, 3, 4]
Оператор | добавляет новый элемент к голове списка и возвращает новый список.
Разделение списка на голову и хвост выглядит похоже:
[head | tail] = my_list
IO.puts(head) # => 1
IO.puts(inspect(tail)) # => [2, 3, 4]
Здесь мы используем сопоставление с образцом. Шаблон (образец) [head | tail]
сопоставляется со списком. Первый элемент списка попадает в переменную head
, остаток списка (хвост) попадает в переменную tail
.
Можно извлечь несколько элементов одновременно:
[item1, item2, item3 | tail] = my_list
IO.puts(item1) # => 1
IO.puts(item2) # => 2
IO.puts(item3) # => 3
IO.puts(inspect(tail)) # => [4]
Таким образом мы извлекли три элемента, и в списке остался только один.
Есть еще функции hd
и tl
, которые извлекают голову и хвост списка:
hd(my_list) # 1
tl(my_list) # [2, 3, 4]
Зачем нужны такие странные операции? На них основана итерация по элементам списка. А на итерации основана вообще любая работа со списками и с другими коллекциями. Это станет понятно позже, когда мы начнем изучать рекурсию и основы функционального программирования.
Реализуйте функцию get_second_item
, которая возвращает сумму первого и второго элементов списка.
Внимательный читатель спросит: "а что функция должна сделать, если в списке только один элемент, или список вообще пустой?". На этот вопрос мы ответим в следующем модуле, где будем изучать ветвления в коде и сопоставление с образцом. Пока будем считать, что функция всегда вызывается со списком, содержащим два или больше элементов.
Solution.get_second_item([20, 22, 24])
# => 42
Solution.get_second_item([1, 2, 3, 4])
# => 3
Еще более внимательный читатель спросит: "а что, если список содержит элементы такого типа, для которого не определена операция суммирования?". В этом случае возникнет исключение. Исключения и обработку ошибок изучим в соответствующем модуле.
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Ваше упражнение проверяется по этим тестам
1defmodule Test do
2 use ExUnit.Case
3 import Solution
4
5 test "get second item" do
6 assert 42 == get_second_item([20, 22, 24])
7 assert 3 == get_second_item([1, 2, 3, 4])
8 assert -3 == get_second_item([-1, -2, -3, -4])
9 end
10end
11
Решение учителя откроется через: