Понимание этой функции транспонирования матриц в Haskell
Эта функция транспонирования матриц работает, но я пытаюсь понять ее пошаговое выполнение, и я не понимаю.
transpose:: [[a]]->[[a]]
transpose ([]:_) = []
transpose x = (map head x) : transpose (map tail x)
с
transpose [[1,2,3],[4,5,6],[7,8,9]]
он возвращает:
[[1,4,7],[2,5,8],[3,6,9]]
Я не понимаю, как работает оператор конкатенации с картой. Это конкатенация каждой главы x в том же вызове функции? Как?
- это
(map head x)
создание списка элементов заголовка каждого списка?
Ответы
Ответ 1
Посмотрим, что делает функция для ввода вашего примера:
transpose [[1,2,3],[4,5,6],[7,8,9]]
<=>
(map head [[1,2,3],[4,5,6],[7,8,9]]) : (transpose (map tail [[1,2,3],[4,5,6],[7,8,9]]))
<=>
[1,4,7] : (transpose [[2,3],[5,6],[8,9]])
<=>
[1,4,7] : (map head [[2,3],[5,6],[8,9]]) : (transpose (map tail [[2,3],[5,6],[8,9]]))
<=>
[1,4,7] : [2,5,8] : (transpose [[3],[6],[9]])
<=>
[1,4,7] : [2,5,8] : (map head [[3],[6],[9]]) : (transpose (map tail [[3],[6],[9]]))
<=>
[1,4,7] : [2,5,8] : [3, 6, 9] : (transpose [[], [], []])
<=>
[1,4,7] : [2,5,8] : [3, 6, 9] : [] -- because transpose ([]:_) = []
<=>
[[1,4,7],[2,5,8],[3,6,9]]
Обратите внимание, что порядок, в котором я решил уменьшить термины, не совпадает с порядком оценки haskell, который не изменит результат.
Изменить: В ответ на ваш отредактированный вопрос:
- это
(map head x)
создание списка элементов заголовка каждого списка?
Да, это так.
Ответ 2
Оператор cons :
присоединяет объект типа a
к списку типа [a]
. В
(map head x) : transpose (map tail x)
LHS - это список (a = [b]
), а RHS - список списков ([a] = [[b]]
), поэтому такая конструкция действительна. Результатом является
[x,y,z,...] : [[a,b,...],[d,e,...],...] = [[x,y,z,...], [a,b,...],[d,e,...],...]
В вашем случае map head x
и map tail x
разбивает матрицу
x = [[1,2,3],[4,5,6],[7,8,9]]
в
map head x = [1,4,7]
map tail x = [[2,3],[5,6],[8,9]]
(и да, map head x
- список элементов заголовка каждого списка.) Вторая часть транспонирована (подробные шаги см. в @sepp2k) сформировать
transpose (map tail x) = [[2,5,8],[3,6,9]]
так что минус [1,4,7]
, дает
map head x : transpose (map tail x) = [1,4,7] : [[2,5,8],[3,6,9]]
= [[1,4,7] , [2,5,8],[3,6,9]]
Ответ 3
ghci
- ваш друг:
*Main> :t map head
map head :: [[a]] -> [a]
*Main> :t map tail
map tail :: [[a]] -> [[a]]
Даже если вы не понимаете map
(проблема, которую вы хотите быстро исправить!), типы этих выражения говорят о том, как они работают. Первый - это один список, взятый из списка списков, поэтому дайте ему простой вектор, чтобы увидеть, что происходит.
Возможно, вы захотите написать
*Main> map head [1,2,3]
но это не работает:
<interactive>:1:14:
No instance for (Num [a])
arising from the literal `3' at :1:14
Possible fix: add an instance declaration for (Num [a])
In the expression: 3
In the second argument of `map', namely `[1, 2, 3]'
In the expression: map head [1, 2, 3]
Помните, что тип аргумента представляет собой список списков, поэтому
*Main> map head [[1,2,3]]
[1]
Немного сложнее
*Main> map head [[1,2,3],[4,5,6]]
[1,4]
Выполнение того же, но с tail
вместо head
дает
*Main> map tail [[1,2,3],[4,5,6]]
[[2,3],[5,6]]
Как вы можете видеть, определение transpose
неоднократно отсекает первую "строку" с помощью map head x
и переносит остальное, что является map tail x
.
Ответ 4
Все те же:
map head xxs
map (\xs -> head xs) xxs
Это лямбда-выражение, что означает, что я возвращаю голову xs для каждого xs
Пример:
map head [[1,2,3],[4,5,6],[7,8,9]]
-> map (\xs -> head xs) [[1,2,3],[4,5,6],[7,8,9]]
-> [head [1,2,3], head [4,5,6], head [7,8,9]]
-> [1,4,7]
Это просто
Ответ 5
Кстати, эта функция не работает при вводе типа [[1,2,3], [], [1,2]]
. Однако функция транспонирования из Data.List
примет этот вход и вернет [[1,1], [2,2], [3]]
.
Когда мы вызываем рекурсивный код transpose
, нам нужно избавиться от []
.