Я понимаю, что foldr сначала применяет оператор op для последнего элемента в списке
Я не совсем уверен, что вы понимаете и этот бит.Сначала давайте рассмотрим тип foldr
(специализированный для списков) и его определение.
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f e [] = e
foldr f e (x:xs) = f x (foldr f xs)
* В данном случае a
равно Double
, а b
равно Double -> Double -> Double
.Первое, что вы знаете, это Double
, потому что вы складываете список пар.Последний тип unit
, который вы возвращаете в пустом регистре.
Итак, ваш foldr
вычисляет функцию типа Double -> Double -> Double
из списка значений типа double.В пустом случае вы просто возвращаете функцию
unit n sum = sum / n
, которая является функцией, которая делит свой второй аргумент на первый.То есть (/) но его аргументы поменялись местами.На каждом этапе сгиба вы модифицируете эту двоичную функцию, используя элемент списка.Вы изменяете его с помощью op
op :: Double -> (Double ->Double ->Double) -> (Double -> Double -> Double)
(x `op` y ) n sum = y (n+1) (sum +x)
Мне очень не нравится буква y
, так как она выглядит как двойная.Давайте перепишем это как h
.И давайте абстрагируемся также над n
и sum
, используя лямбду, чтобы было легче понять, что происходит.
op :: Double -> (Double ->Double ->Double) -> (Double -> Double -> Double)
(x `op` h) = \n sum -> h (n+1) (sum +x)
Таким образом, учитывая двойное значение x
из списка и двоичную функцию h
, рассчитанную до настоящего времени (которая изначально равна unit
), мы собираемся вычислить новую функцию,
\n sum -> h (n+1) (sum+x)
, которая аналогична предыдущей функции h
, за исключением того, что она суммирует 1
с первым аргументом и x
со вторым перед применением h
.
Таким образом, в итоге для списка [x1, x2, x3]
, который сворачивается, вернется функция,
\n sum -> (x1+x2+x3+sum) / (1+1+1+n)
И когда вы примените это к двум полученным нулям,
(\n sum -> (x1+x2+x3+sum) / (1+1+1+n)) 0 0
= (x1+x2+x3+0) / (1+1+1+0)
= (x1+x2+x3) / (1+1+1)