Сопоставление с образцом с функциями (высшего порядка) в Haskell - PullRequest
4 голосов
/ 01 февраля 2011

Я пытаюсь немного узнать о Haskell с помощью онлайн-книги «Learn you a Haskell», и у меня есть вопрос о функциях высшего порядка.

Я видел некоторые примеры и я хочу сделать несколько более сложных функций, но я не знаю, почему я всегда читаю следующее исключение:

*** Исключение: euler13.hs: (11,0) - (15, 39): применяются неисчерпывающие шаблоны в функции

И эта функция, которую я определил, такова:

apply :: (Num b, Ord b) => (a -> a) -> b -> [a] -> [a]
apply f n [] = []
apply f n [x]
    | n <= 1    = map f [x]
    | otherwise = apply f (n-1) (map f [x])

Я хочу применить n раз конкретную функцию'f' к списку '[x]'.Я попытался сделать эту функцию полиморфной, чтобы тип параметра был «а».Но я также хочу использовать числа и списки, поэтому непосредственно я использую список (если я хочу использовать функцию только для числа, это было бы [число], очевидно)

Может кто-нибудь помочь мне, пожалуйста?Мне нравится этот язык, но это немного сложно, когда вы изучаете, потому что он настолько отличается от Java или c (например)

Спасибо!

Ответы [ 4 ]

13 голосов
/ 01 февраля 2011

Удалите [] вокруг x.В противном случае 2-й шаблон может соответствовать только спискам только с 1 элементом.

apply f n x
    | n <= 1    = map f x
    | otherwise = apply f (n-1) (map f x)
11 голосов
/ 01 февраля 2011

Это ничем не отличается от того, что говорили другие, но, может быть, дело в этом?Существует два основных «конструктора» для списков, и, следовательно, два основных случая, которые необходимо учитывать при определении функций из списков: аргументы вида [] и (:).последний, (:), может объединить что угодно со списком такого рода вещей, таким образом, 1 с [] - 1:[] или [1].Или он может присоединиться к 1 с чем-то вроде этого: 1:(1:[]) то есть 1:[1], то есть [1,1], поскольку специальный синтаксис позволяет нам писать.

Было бы более очевидно, что пошло не такесли вы сами определили списки, напишите:

data List a = Nil | Cons a (List a) deriving (Show, Eq, Ord)

Использование [] и x:xs - это просто кусок сахара для чего-то подобного.Точно так же специальный сахар String позволяет нам писать "abc" вместо ['a','b','c'], что намного лучше, чем 'a':'b':'c':[].(С помощью приведенного выше определения мы должны были бы написать Cons 'a' (Cons 'b' (Cons 'c' Nil))), что немного для короткой строки! - хотя это также выявляет, почему для многих целей следует предпочесть ByteString и Text представления строк.)Более подробное определение списка, подобное этому, нам нужно добавить наш собственный map (или, скорее, fmap), так что мы можем сказать

instance Functor List where 
   fmap f Nil = Nil
   fmap f (Cons first rest) = Cons (f first) (fmap f rest)

Обратите внимание, что при определении fmap для этого случая я имелрассмотреть оба типа конструктора для моего типа List, Nil и Cons first rest (или Cons x xs, как это часто пишется).

Или, может быть, вы не добрались до общего обсуждения класса типа Functor в LYAH - в этом случае, просто учтите, что вы можете определить свой собственный map как

listMap f Nil = Nil
listMap f (Cons first rest) = Cons (f first) (listMap f rest)

В любом случае, учитывая это перезаписанное переписывание типа списка, ваше фактическое определение функции будет:

apply :: (Num b, Ord b) => (a -> a) -> b -> List a -> List a
apply f n Nil = Nil
apply f n (Cons first Nil)
    | n <= 1    = fmap f (Cons first Nil)  -- or listMap f (Cons first Nil)
    | otherwise = apply f (n-1) (fmap f (Cons first Nil))

Были рассмотрены следующие случаи:

apply f n Nil 
apply f n (Cons first Nil)

Cons first Nil - это то же самое, что first : [] или [first] - т.е. [x], как вы пишете.Но это означает, что вы не охватили каждый случай, ваше определение не является исчерпывающим.Вы не сказали, как применить f и n к списку, если в нем более одного участника.Что если список имеет форму Cons x (Cons y Nil) или Cons x (Cons y (Cons z Nil)), а не Nil (ваша первая строка) или Cons x Nil (ваша вторая строка)?

Решение, как другие говорили, или использование нашегоdesugared list-type:

apply :: (Num b, Ord b) => (a -> a) -> b -> List a -> List a
apply f n Nil = Nil
apply f n (Cons first rest)
    | n <= 1    = fmap f (Cons first rest)
    | otherwise = apply f (n-1) (fmap f (Cons first rest))

Здесь переменная rest охватывает все списки, Nil или нет.Таким образом, мы получаем:

*Main> apply (+1) 3 Nil
Nil
*Main> apply (+1) 3 (Cons 3 Nil)
Cons 6 Nil

, как вы бы, но также:

*Main> apply (+1) 3 (Cons 0 (Cons 1 (Cons 2 Nil)))
Cons 3 (Cons 4 (Cons 5 Nil))
5 голосов
/ 01 февраля 2011

Вы определяете apply для двух случаев: n и пустой список и n и список из одного элемента. Что происходит, когда список содержит более одного элемента? Это недостающий шаблон.

2 голосов
/ 01 февраля 2011

Это не новый ответ по сравнению с другими, но, тем не менее, он проницателен.

Вы уже продемонстрировали некоторое понимание сопоставления с образцом в определениях функций; когда первый шаблон не соответствует, оценка переходит к следующему. Шаблоны, которые могут не совпадать, считаются «опровержимыми».

Как правило, хорошая идея, чтобы ваше последнее определение функции было «неопровержимым», то есть оно всегда совпадает. Из отчета Haskell 2010 :

Неопровержимые шаблоны следующие: переменная, подстановочный знак, N apat, где N - конструктор, определенный newtype, а apat неопровержимый, var @ apat, где apat неопровержим, или в форме ~ apat. Все остальные образцы являются опровержимыми.

Ваше недоразумение заключалось в том, что вы думали, что [x] - это переменная (неопровержимый шаблон), когда на самом деле это шаблон, который можно опровергнуть (шаблон для списка из одного элемента, который связывает x с этим единственным элементом). 1015 *

Предположим, вы написали функцию, которая работает только со списками длины 3. Если вы хотите более описательное сообщение об ошибке, чем "неисчерпывающие шаблоны", то вы можете использовать неопровержимый шаблон с подстановочными знаками (подчеркивание). Тривиальный пример:

sum3 [x,y,z] = x+y+z
sum3 _       = error "sum3 only works on lists of length 3"
...