Тип * из foldl
говорит нам, как это должно работать.
foldl :: (b -> a -> b) -> b -> [a] -> b
Ясно [a]
, третий аргумент, который является спискомчего-то, должен быть список Bool
для передачи алгоритму Хорнера.Это означает, что переменная типа a
должна быть Bool
.
Переменная типа b
представляет возможно отдельный тип.Мы пытаемся преобразовать [Bool]
в Int
, поэтому Int
- достойное предположение для b
.
foldl
работает, просматривая список слева ( т.е. , начиная с его заголовка) и каким-то образом объединяя результат до следующего элемента из списка.Второй аргумент обычно называется z
для «нуля» или начального значения для процесса свертывания.Когда foldl
достигает конца списка, он возвращает накопленное значение.
Мы можем синтаксически увидеть, что первый аргумент - это некоторая функция, которая выполняет некоторую операцию над элементами типа b
и типа a
дать b
.Теперь функция, которая игнорирует элемент a
и безоговорочно приводит к тому, что бы b
подходила, но не была бы очень интересной.
Подумайте о том, как работает алгоритм Хорнера.Числа в коленях пути на диаграмме представляют собой условный «результат до сих пор» из предыдущего абзаца.Мы знаем, что b
равно Int
и a
равно Bool
, поэтому функция, переданная в foldl
, должна преобразовать Bool
в Int
и объединить ее с результатом.
Первый шаг в алгоритме Хорнера, кажется, является особым случаем, который должен обрабатываться по-разному, но foldl
использует одну и ту же функцию на всем протяжении.Если вы представите, что для начала «заполняете насос» невидимым горизонтальным движением (, то есть , умноженное на два), мы можем привести типы в соответствие друг с другом, как кусочки головоломки.Это хорошо, потому что два раза ноль по-прежнему равен нулю.
Таким образом, с точки зрения foldl
, алгоритм Хорнера равен
horners :: [Bool] -> Int
horners = foldl f 0
where f x b =
let b' = fromEnum b
in 2*x + b'
Обратите внимание, что 2*x + b'
объединяет последующие горизонтальные и вертикальные перемещения.
Здесь также предлагается, как выразить это в прямой рекурсии.
horners' :: [Bool] -> Int
horners' [] = 0
horners' l = go 0 l
where -- over then down
go x [] = x
go x (b:bs) =
let b' = fromEnum b
in go (2*x + b') bs
Здесь внутренний цикл go
выполняет левый сгиб и объединяет каждый следующий Bool
с полученным результатом.в i
.
* Педагогическое упрощение: фактический тип обобщает тип списка в Foldable
.