Синтаксис для построения списка/конкатенации
Я уже был в Haskell уже два дня, и мне было интересно, какая разница между двумя определениями функций ниже:
Prelude> let swap (x1:x2:xs) = x2:x1:xs
Prelude> swap [1..5]
[2,1,3,4,5]
Prelude> let swap' (x1:x2:xs) = [x2] ++ [x1] ++ xs
Prelude> swap' [1..5]
[2,1,3,4,5]
То есть, что делает x2: x1: xs отличным от [x2] ++ [x1] ++ xs?
Пожалуйста, спасибо.
Ответы
Ответ 1
Подписи типов - это хорошее место для начала:
(:) :: a -> [a] -> [a]
(++) :: [a] -> [a] -> [a]
Вы можете найти их с помощью :type (:)
и :type (++)
в ghci.
Как видно из сигнатур типа, оба используются для создания списков.
Оператор :
используется для построения списков (и для их раздельного разделения). Чтобы создать список [1,2,3]
, вы просто создаете его с помощью 1 : 2 : 3 : []
. Первым элементом :
является элемент, который нужно добавить в начале списка, а второй элемент - либо список (также созданный с помощью :
, либо пустой список, обозначенный []
).
Оператор ++
представляет собой конкатенацию списка. Он берет два списка и присоединяет их вместе. [1,2,3] ++ [4,5,6]
является законным, тогда как 1 ++ [1,2,3]
не является.
Ответ 2
Это не имеет ничего общего с синтаксисом. (:) и (++) - это просто разные операторы. (:) - конструктор, который строит список из элемента и другого списка. (++) создает новый список, который является конкатенацией двух списков. Поскольку (++) не является конструктором, вы не можете использовать его в шаблонах.
Теперь мы переходим к синтаксису: обозначение
[x2]
который вы используете, является сокращением для
x2:[]
Итак, что вы действительно сделали во втором примере:
(x2:[]) ++ (x1:[]) ++ xs
Поэтому при построении списка вы не можете избежать (:), это, в сущности, единственный способ сделать это. Обратите внимание, что вы должны создавать промежуточные списки, чтобы иметь возможность использовать (++).