Eu estou tentando implementar a função tails
. Esta é minha tentativa:
tails" :: [a] -> [[a]]
tails" [] = []
tails" (x:xs) = xs:[[tails" xs]]
Eu continuo correndo em erros de compilação:
Couldn"t match expected type ‘a’ with actual type ‘[[a]]’
‘a’ is a rigid type variable bound by
the type signature for tails" :: [a] -> [[a]] at..
O que há de errado com minha implementação?
Respostas:
5 para resposta № 1Substituir
tails" (x:xs) = xs:[[tails" xs]]
com:
tails" (x:xs) = xs : tails" xs
4 para resposta № 2
Além do sintaxe erro de tipo, sua implementação não está correta (para a especificação). Compare com este ...
Prelude> let tails [] = [[]]
Prelude| tails y@(x:xs) = y:(tails xs)
Prelude|
Prelude> tails "abc"
["abc","bc","c",""]
1 para resposta № 3
Já faz mais de um ano, mas nenhuma resposta correta foi dada, então aqui está minha opinião sobre isso.
Vamos começar com a assinatura do tipo:
tails" :: [a] -> [[a]]
Isso sugere que tails"
deve retornar uma lista de listas. O caso terminal que você solucionou é apenas uma lista vazia. Altere para:
tails" [] = [[]]
Agora, o caso geral. Vamos "s assumir que nossa tails"
funciona da maneira que queremos:
tails" [] -> [[]]
tails" [1] -> [[1], []]
tails" [2,1] -> [[2,1], [1], []]
tails" [3,2,1] -> [[3,2,1], [2,1], [1], []]
Como podemos ver, queremos incluir a lista original e a lista vazia. Isso significa que, dada uma lista de entrada xs
, queremos concatenar xs
para algo mais.
Mas o que é isso? algo mais? Vamos fazer um pouco de matemática pseudo-haskell:
let A = tails" [3,2,1] = [[3,2,1], [2,1], [1], []]
let B = tails" [2,1] = [[2,1], [1], []]
=> A = [[3,2,1], B] -- (1)
tail [3,2,1] == [2,1]
=> B = tails" (tail [3,2,1]) -- (2)
By (1) and (2)
A = [[3,2,1], tails" (tail [3,2,1])]
=> tails" [3,2,1] = [[3,2,1], tails" (tail [3,2,1])] -- (3)
By replacing [3,2,1] in (3) with xs
tails" xs = [xs, tails" (tail xs)]
Traduzir tudo isso para Haskell nos dá:
tails" :: [a] -> [[a]]
tails" [] = [[]]
tails" xs = xs : (tails" $ tail xs)
main = do
print $ tails" [1,2,3]
-- [[1,2,3],[2,3],[3],[]]
1 para resposta № 4
tails
é o exemplo canônico com listas simples para o esquema de recursão chamado paramorfismo. O exemplo a seguir usa o esquemas de recursão biblioteca de Edward Kmett:
import Data.Functor.Foldable
import Data.List (tails) -- just for our test
paratails :: [a] -> [[a]]
paratails = para alg where
alg :: ListF a ([a], [[a]]) -> [[a]]
alg (Cons x (hs, res)) = (x : hs) : res
alg Nil = [[]]
-- $
-- >>> paratails [1,2,3,4]
-- [[1,2,3,4],[2,3,4],[3,4],[4],[]]
-- >>> paratails []
-- [[]]
-- >>> paratails [1,2,3,4] == (tails [1,2,3,4])
-- True
-- >>> paratails [] == (tails [])
-- True
Nota: Para encurtar o código, você pode usar LambdaCase. Com esta extensão, no entanto, você não pode especificar a assinatura do tipo alg função
1 para resposta № 5
Como agora há um esquemas de recursão resposta dobra, sinto-me obrigado a adicionar um esquemas de recursão desdobramento resposta:
import Data.Functor.Foldable
tailsApo :: [a] -> [[a]]
tailsApo = apo coalgTails
where
coalgTails = case
[] -> Cons [] (Left [])
li@(_:xs) -> Cons li (Right xs)
Eu usei um apomorfismo em vez de um anamorfismo simples porque não queremos produzir uma lista vazia de uma lista vazia (tails [] == [[]]
).