Ключевой шаг, который вам нужно сделать, - это думать индуктивно . Ваше текущее решение:
and :: [Bool] -> Bool
and [] = True
and [True, True] = True
and [False, False] = True
and [True, False] = False
перечисляет некоторые возможности, но, очевидно, работает не для всех списков. Так, как написать тот, который будет работать для списков любой длины?
В Haskell вы обычно можете писать свои функции, разбирая тип данных. В этом случае списки. Списки определены как:
data [a] = []
| a : [a]
Итак, списки имеют два случая: либо пустой список, либо один элемент с хвостом. Давайте начнем писать вашу функцию and
, чтобы она соответствовала этим двум случаям для списков:
and [] = True
and (a:as) = ...
Итак, «базовый случай», пустой список, равен True
. Но что нам делать в случае списка с одним элементом и некоторым хвостом?
Ну, у нас уже есть функция &&
в Haskell:
> True && True
True
> True && False
False
> False && True
False
> False && False
False
Интересно! Итак, &&
принимает два аргумента и правильно определяет, являются ли оба аргумента истинными. И у нас в настоящее время есть список с одним элементом и список для хвоста. И в то же время мы определяем функцию and
, которая приводит к единственному логическому значению при применении к списку.
Таким образом, мы можем сделать индуктивным шагом и использовать определяемую нами функцию and
вместе с двоичным оператором &&
, чтобы завершить наше определение:
and [] = True
and (a:as) = a && (and as)
Таким образом, мы вычисляем хвост списка до некоторого значения (рекурсивно) и используем оператор &&
, чтобы объединить это значение с текущим значением, и это наша написанная функция.
Рекурсия и индукция, обусловленные рекурсивной структурой списков, являются ключевой техникой для изучения в программировании. Если вы можете написать это, вы делаете большой шаг вперед.