Является ли "Список" специально обработанным в шаблоне Haskell?
Я новичок в Haskell и надеюсь, что этот вопрос не глупый.
Я видел так много примеров, что, когда у меня есть список, я могу сопоставить "составной элемент" списка с отдельной переменной:
listSizeDesc :: [a] -> String
listSizeDesc [] = "Emtpy"
listSizeDesc (x:xs) = "Something inside"
Однако я попытался сделать что-то вроде:
foo :: Int -> String
foo 0 = "Zero"
foo (n - 1) = "next number is " ++ show n
Это не работает.
Мне кажется, что оба (n-1) и (x: xs) описывают, как аргумент "создан" и привязывают "компонент" к аргументу. Является ли способ List list специально разработанным для удобства рекурсии? Мне кажется, что эта логика соответствия/аргумента-привязки не применяется к другим функциям, кроме (:).
Ответы
Ответ 1
Тип списка - "Тип суммы" с конструктором, что-то вроде:
data List a =
cons a (List a)
| nil
Вы первый пример - соответствие шаблона в типе данных (с синтаксическим сахаром для :
).
Второй пример - совпадение шаблонов для целых чисел, которые не являются определением datatypye. На integer нет шаблона, использующего ваш синтаксис. Вы можете написать свой пример:
foo :: Int -> String
foo 0 = "Zero"
foo n = "next number is " ++ show (n+1)
На стороне примечания, если вы кодируете целые числа с такими типами данных, как:
data Nat = Zero | Succ Nat deriving (Show)
Затем вы можете использовать свой шаблон, как вы хотели изначально.
foo :: Nat -> String
foo Zero = "Zero"
foo [email protected](p) = "next number is " ++ show(n)
Здесь шаблон Succ(p)
играет роль n-1
.
Ответ 2
Проблема, с которой вы сталкиваетесь, заключается в том, что сопоставление шаблонов работает только с конструкторами данных. По сути, конструктор данных очень прост; он просто принимает значения данных и группирует их вместе в какой-то структуре. Например, data Foo = Bar a b
просто берет две части данных и группирует их вместе под меткой Foo. Функция (:)
, которую вы используете в первом примере, больше, чем просто функция; это конструктор данных. Он создает новый список, добавляя левый аргумент к правильному аргументу.
Теперь сопоставление шаблонов просто делает противоположное этому процессу. Он деконструирует тип данных. Когда вы пишете (x:xs)
в своем шаблоне, вы извлекаете две части данных, которые конструктор первоначально сшивал вместе. Таким образом, все соответствия шаблонов - это извлечение данных, которые ранее сшивали конструктор.
Есть одно исключение: n + k шаблонов. В Haskell98 вам разрешили использовать шаблоны формы (n + k). Это было своего рода произвольное исключение, и оно было недавно удалено. Если вы хотите, вы все равно можете использовать его, если вы включите прагму языка NPlusKPatterns.
Ответ 3
Уже есть отличные ответы, поэтому я не буду беспокоиться о главном вопросе. Это не лучшее его использование, но то, что вы пытались сделать, может быть выполнено с помощью просмотра шаблонов.
{-# LANGUAGE ViewPatterns #-}
foo :: Int -> String
foo 0 = "Zero"
foo (pred -> n) = "Next number is " ++ show n
Ответ 4
Просто, чтобы сказать это как можно проще:
Список буквально представляет собой серию конкатенаций. Число может быть эквивалентно результату арифметической операции. Разница в том, что результатом a : b
является просто a : b
.
Подробнее:
Списки и (:) не являются особым случаем вообще. Позвольте сделать наш собственный:
data List2 a = End -- equivalent of "[]"
| Cat a (List2 a) -- non-infix ":"
deriving (Show)
Итак [1, 2, 3]
, который == (1 : (2 : (3 : [])))
, будет записан как:
a = Cat 1 (Cat 2 (Cat 3 End))
Подобно сопоставлению с образцом (x:xs)
, мы можем сопоставлять шаблон List2:
newTail End = End
newTail (Cat _ x) = x
Проверьте это:
*Main> tail [1,2,3]
[2,3]
*Main> newTail a
Cat 2 (Cat 3 End)
Ответ 5
moo :: Int -> String
moo 0 = "Zero"
moo n = "next number is " ++ show (n + 1)
n - 1
- обычное приложение, а не шаблон. Исключение было использовано для +
, и это может быть модель, с которой вы проходите. Вы можете написать что-то вроде
goo :: Int -> String
goo 0 = "Zero"
goo (n+1) = "previous number is " ++ show n
в hugs
; вы все еще можете сделать это с помощью ghc
, если вы включите прагму
{-#LANGUAGE NPlusKPatterns#-}