"List" специально обрабатывается в соответствии с шаблоном Haskell? - PullRequest
19 голосов
/ 03 ноября 2011

Я новичок в Хаскеле и надеюсь, что этот вопрос не глупый.

Я видел так много примеров, что когда у меня есть список, я могу сопоставить и связать «элемент композиции» списка с отдельной переменной:

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

Ответы [ 5 ]

18 голосов
/ 03 ноября 2011

Проблема, с которой вы сталкиваетесь, заключается в том, что сопоставление с образцом работает только с конструкторами данных.Конструктор данных по сути очень прост;он просто берет значения данных и группирует их в какую-то структуру.Например, data Foo = Bar a b просто берет две части данных и группирует их вместе под меткой Foo.Функция (:), которую вы используете в первом примере, - это больше, чем просто функция;это конструктор данных.Он создает новый список, добавляя левый аргумент к правому аргументу.

Теперь сопоставление с образцом просто противоположно этому процессу.Деконструирует тип данных.Когда вы пишете (x:xs) в своем шаблоне, вы извлекаете две части данных, которые конструктор изначально соединил.Таким образом, все сопоставление с образцом - это извлечение данных, которые конструктор ранее сшил вместе.

Есть одно исключение: n + k шаблонов.В Haskell98 вам было разрешено использовать шаблоны вида (n + k).Это было своего рода произвольное исключение, и оно было недавно удалено.Если хотите, вы можете использовать его, если включите языковую прагму NPlusKPatterns.

14 голосов
/ 03 ноября 2011

Тип списка "Тип суммы" с конструктором, что-то вроде:

data List a =
     cons a (List a)
   | nil

Ваш первый пример - сопоставление с шаблоном для типа данных (с синтаксическим сахаром для :).

Ваш второй пример - это сопоставление с образцом целых чисел, которые не являются определением типа данных. Для целых чисел нет шаблона, использующего ваш синтаксис. Вы можете написать свой пример с:

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 n@Succ(p) = "next number is " ++ show(n)

Здесь шаблон Succ(p) играет роль n-1.

5 голосов
/ 04 ноября 2011

Уже есть несколько отличных ответов, поэтому я не буду беспокоиться о главном вопросе. Это не наилучшее использование, но то, что вы пытались сделать, может быть достигнуто с помощью шаблонов просмотра .

{-# LANGUAGE ViewPatterns #-}

foo :: Int -> String
foo 0 = "Zero"
foo (pred -> n) = "Next number is " ++ show n
4 голосов
/ 04 ноября 2011

Проще говоря:
Список буквально представляет собой серию конкатенаций. Число может быть эквивалентно результату арифметической операции. Разница в том, что результат 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)
3 голосов
/ 03 ноября 2011
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#-}
...