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