Ключи к пониманию сопоставления с образцом состоят в том, чтобы понять, что x :: y
будет только совпадать со списком с одним элементом x
, за которым следует остальная часть списка y
(который может быть простоNil
, или может быть много элементов), и это _
означает "здесь должно быть что-то, но мы не будем называть это именем".(И что совпадения происходят по порядку, и что списки заканчиваются на Nil
.)
Вы правы, что [A]
является универсальным типом.
Итак, первая строка:
case h :: _ :: Nil => h
говорит, что если наш список выглядит (концептуально) Node(h) -> Node(whatever) -> Nil
, то мы возвращаем h
.Это ровно двухэлементный список с выбранным первым элементом.Обратите внимание, что Nil
не не соответствует произвольному хвосту списка;он соответствует только элементу конца списка Nil
.Это происходит из-за правила, которое Scala использует для различения двух: переменные нижнего регистра обрабатываются как символы подстановки, которым необходимо заполнить соответствующее значение, а переменные верхнего регистра обрабатываются как константы для сопоставления.(Если вам нужно сопоставить имя в нижнем регистре, вы можете, если окружите его обратными чертами.)
Хорошо, теперь предположим, что это не список из двух элементов.Тогда, если он не пустой, он будет соответствовать
case _ :: tail => penultimateRecursive(tail)
, поэтому, если у нас нет двухэлементного списка, мы выбрасываем первый элемент и пытаемся снова.Наконец, если мы каким-то образом никогда не заканчивали двухэлементным списком, мы получаем
case _ => throw new NoSuchElementException
и все готово.(На самом деле это также может быть case Nil
, поскольку это единственная возможность, которая не соответствует двум другим записям.)