Подтвердить что ты не робот

Является ли "Список" специально обработанным в шаблоне 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 специально разработанным для удобства рекурсии? Мне кажется, что эта логика соответствия/аргумента-привязки не применяется к другим функциям, кроме (:).

4b9b3361

Ответ 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#-}