Atualmente, estou lidando com uma função que é assim:
foo = (\(a:b:c:d:e:f:_) -> foobar a b c d e f) . (++ repeat def)
Em outras palavras, dada uma lista, ela usa os seis primeiros elementos para algo e, se a lista tiver menos de seis elementos, será usada def
como substituta dos que estão faltando. Isso é total, mas as partes não são (exatamente como map fromJust . filter isJust
), então eu não gosto. Tentei reescrever isso para que ele não precisasse de parcialidade, e consegui:
foo [] = foobar def def def def def def
foo [a] = foobar a def def def def def
foo [a,b] = foobar a b def def def def
foo [a,b,c] = foobar a b c def def def
foo [a,b,c,d] = foobar a b c d def def
foo [a,b,c,d,e] = foobar a b c d e def
foo (a:b:c:d:e:f:_) = foobar a b c d e f
Eu tecnicamente fiz o que eu quero, mas agora isso é uma bagunça gigantesca. Como posso fazer isso de uma maneira mais elegante e menos repetitiva?
haskell
pattern-matching
Joseph Sible-Restabelecer Monica
fonte
fonte
uncons :: Default a => [a] -> (a,[a])
padrãodef
. Ou um padrãotakeWithDef
. E / ou um padrão de visualização / sinônimo de padrão. Isso requer a escrita de algum código auxiliar auxiliar, no entanto.case xs ++ repeat def of a:b:c:d:e:f:_ -> ...
é local o suficiente para que eu não pense duas vezes apenas em usá-lo e pular toda a maquinaria extra que as respostas existentes estão introduzindo. Geralmente, são os argumentos de globalidade mais globais (que envolvem invariantes mantidos em várias chamadas de função, por exemplo) que me deixam nervosa.takeWithDef
não é utilizável se retornar uma lista regular, pois precisamos padronizar a correspondência com a seguinte: - / A solução adequada é o que Daniel escreveu abaixo em sua segunda resposta.uncons
só recebe o primeiro elemento, então não é tão útil.Respostas:
Usando o pacote seguro , você pode escrever, por exemplo:
fonte
Isso é pelo menos mais curto:
Você pode ver facilmente que os padrões são exaustivos, mas agora você precisa pensar um pouco para ver que sempre termina. Então, eu não sei se você pode considerar isso uma melhoria.
Caso contrário, podemos fazê-lo com a mônada estadual, embora seja um pouco pesada:
Eu também poderia imaginar usando um tipo de fluxo infinito como
porque então você pode construir
foo
forarepeat :: a -> S a
,prepend :: [a] -> S a -> S a
etake6 :: S a -> (a,a,a,a,a,a)
, tudo o que poderia ser total. Provavelmente não vale a pena se você ainda não tem esse tipo de ferramenta.fonte
data S a = a :- S a; infixr 5 :-
, parece bastante limpo;foo xs = case prepend xs (repeat def) of a:-b:-c:-d:-e:-f:-_ -> foobar a b c d e f
.Apenas por diversão (e não recomendado, isso é para brincadeiras), aqui está outra maneira:
O tipo que você usa na correspondência de padrão equivale a passar um natural no nível de tipo para
takeDef
dizer quantos elementos examinar.fonte
foo (takeDef -> a:-b:-c:-d:-e:-f:-Nil) -> foobar a b c d e f
como uma linha. Não conto o resto, pois é um código que deve estar em alguma biblioteca, para reutilização. Se tiver que ser escrito apenas para este caso, é claramente um exagero, como você diz.