карта. состав функции фолдра-Хаскелл
Итак, давайте прямо к делу.
:t (map.foldr)
(map.foldr) :: (a1 -> a -> a) -> [a] -> [[a1] -> a]
Что такое [[a1] - > a]? Я действительно пытаюсь понять эту композицию, поэтому я делал это:
-- map.foldr
map.foldr :: (a1 -> a -> a) -> [a] -> [[a1] -> a]
map :: (a1 -> b1) -> [a1] -> [b1]
(.) :: (y -> w) -> (x -> y) -> x -> w
foldr :: (a -> b -> b) -> b -> [a] -> b
y = (a1 -> b1) w = ([a1] -> [b1])
x = (a -> b -> b) y = (b -> [a] -> b)
y = (a1 -> b1)
y = (b -> [a] -> b)
_________________________
Что происходит в этой точке? Спасибо!
3 ответа:
Чтобы ответить на этот вопрос, полезно вспомнить, что делают
foldr
иmap
.Более сложным из них является
foldr
, который имеет тип-- list to be folded -- v foldr :: (a -> b -> b) -> b -> [a] -> b -- ^ ^ --folding function terminal value
Список, который нужно сложить, на самом деле представляет собой цепочку консов
(:)
и терминальный пустой список:1 : 2 : 3 : []
Действие
foldr
заключается в замене конструкторов:
и[]
функцией свертки и терминальным значением соответственно:foldr (+) 0 (1 : 2 : 3 : []) == 1 + 2 + 3 + 0
Функция
map
проще. Один из способов думать об этом таков: принимая функцию и список, и применяя функцию к каждому аргументу списка:map :: (a -> b) -> [a] -> [b] -- ^ ^ -- function list
Однако вы также можете думать об этом как о взятии функции и поднятии ее, чтобы она была функцией, которая действует на списки вместо этого:
map :: (a -> b) -> ( [a] -> [b] ) -- ^ ^ -- function function on lists
Что значит составить эти две функции,map . foldr
? Обратите внимание, что это просто применение функций одна за другой - в частности,(map . foldr) f == map (foldr f)
Поскольку вы применяете
foldr
Сначала, вы должны применить его к функцииf :: a -> b -> b
, и вы получите назад еще одна функция:foldr f :: b -> [a] -> b -- ^ ^ --terminal val list to be folded
Теперь вы применяете
map
, который поднимает функцию, чтобы действовать на списках:map (foldr f) :: [b] -> [[a] -> b] -- ^ ^ --list of terminal vals functions that fold lists
Этот тип выглядит странно, но он действителен. Теперь вместо одного терминального значения вы даете ему список терминальных значений, и вы получаете список функций сворачивания обратно - по одному для каждого терминального значения, которое вы предоставили.
Чтобы было понятнее, мы могли бы рассмотреть конкретную функцию
(+)
, которая имеет тип(+) :: Num a => a -> a -> a
Если мы подставим это в уравнение выше мы получаем
Если мы теперь применим его к списку(map . foldr) (+) :: Num a => [a] -> [[a] -> a] -- ^ ^ -- list of terminal vals functions that fold lists
[0, 1, 2]
, то получим список из трех функций:Мы можем использовать идиому(map . foldr) (+) [0,1,2] :: Num a => [[a] -> a]
map ($x)
, чтобы применить каждую из функций в списке к определенному аргументу. Это должен быть список чисел, и я выберу[3,4,5]
. Смотрите внимательно:> map ($[3,4,5]) ((map.foldr) (+) [0,1,2]) [12, 13, 14]
Список
[3,4,5]
был сложен три раза, используя(+)
в качестве функции складывания, и каждый раз с различным терминальным значением:3 + 4 + 5 + 0 == 12 3 + 4 + 5 + 1 == 13 3 + 4 + 5 + 2 == 14
Когда конечное значение равно
0
, мы просто получаем сумму значений:3 + 4 + 5 == 12
. Когда терминальное значение равно1
, мы получаем на один больше суммы значений (13
), а когда терминальное значение равно2
, мы получаем на два больше суммы значений (14
).
Чтобы продолжить с того места, где вы остановились, два определения
y
должны быть равны:Таким образом, мы можем заключить, что:y = (a1 -> b1) = (b -> [a] -> b) = (b -> ([a] -> b))
a1 = b b1 = [a] -> b
Составу функции были предоставлены два аргумента функции, поэтому результирующий тип просто:
x -> w
Но мы знаем:
x = a -> b -> b w = [a1] -> [b1] = [b] -> [[a] -> b]
Итак, тип результата:
(x -> w) = ((a -> b -> b) -> ([b] -> [[a] -> b])) = (a -> b -> b) -> [b] -> [[a] -> b]
, что соответствует:
(a1 -> a -> a) -> [a] -> [[a1] -> a]
map.foldr :: (a1 -> a -> a) -> [a] -> [[a1] -> a] map :: (a1 -> b1) -> [a1] -> [b1] (.) :: (y -> w) -> (x -> y) -> x -> w foldr :: (a -> b -> b) -> b -> [a] -> b -- if you substitute: x = (a -> b -> b) y = (b -> [a] -> b) -- then you get for map :: (b -> ([a] -> b)) -> [b] -> [[a] -> b] -- so if composition operator applied: map . foldr :: (a -> b -> b) -> [b] -> [[a] -> b]