Actualmente estoy lidiando con una función que va así:
foo = (\(a:b:c:d:e:f:_) -> foobar a b c d e f) . (++ repeat def)
En otras palabras, dada una lista, usa los primeros seis elementos para algo, y si la lista tiene menos de seis elementos, la usa def
como un sustituto para los que faltan. Esto es total, pero las partes no son (solo como map fromJust . filter isJust
), por lo que no me gusta. Traté de reescribir esto para que no necesite usar ninguna parcialidad, y obtuve esto:
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
Técnicamente hice lo que quería, pero ahora esto es un desastre gigantesco. ¿Cómo puedo hacer esto de una manera más elegante y menos repetitiva?
haskell
pattern-matching
Joseph Sible-Reinstate a Monica
fuente
fuente
uncons :: Default a => [a] -> (a,[a])
predeterminadodef
. O un incumplimientotakeWithDef
. Y / o un patrón de vista / sinónimo de patrón. Sin embargo, esto requiere escribir un código auxiliar auxiliar.case xs ++ repeat def of a:b:c:d:e:f:_ -> ...
es lo suficientemente local como para no pensarlo dos veces antes de usarlo y omitir toda la maquinaria adicional que las respuestas existentes están introduciendo. Generalmente son los argumentos de totalidad más globales (que involucran invariantes mantenidos a través de múltiples llamadas a funciones, por ejemplo) lo que me pone nervioso.takeWithDef
no es utilizable si devuelve una lista regular, ya que necesitamos un patrón que coincida con eso: - / La solución adecuada es lo que Daniel escribió a continuación en su segunda respuesta.uncons
solo obtiene el primer elemento, por lo que no es tan útil.Respuestas:
Usando el paquete seguro , puede escribir, por ejemplo:
fuente
Esto es al menos más corto:
Puede ver fácilmente que los patrones son exhaustivos, pero ahora tiene que pensar un poco para ver que siempre termina. Entonces no sé si puedes considerarlo una mejora.
De lo contrario, podemos hacerlo con la mónada estatal, aunque es un poco pesado:
También podría imaginarme usando un tipo de flujo infinito como
porque entonces se podría construir
foo
fuera derepeat :: a -> S a
,prepend :: [a] -> S a -> S a
ytake6 :: S a -> (a,a,a,a,a,a)
, todo lo cual podría ser total. Probablemente no valga la pena si aún no tiene un tipo tan útil.fuente
data S a = a :- S a; infixr 5 :-
se ve bastante limpio;foo xs = case prepend xs (repeat def) of a:-b:-c:-d:-e:-f:-_ -> foobar a b c d e f
.Solo por diversión (y no recomendado, esto es por diversión), aquí hay otra forma:
El tipo que usa en la coincidencia de patrones equivale a pasar un nivel de tipo natural a
takeDef
decir cuántos elementos mirar.fuente
foo (takeDef -> a:-b:-c:-d:-e:-f:-Nil) -> foobar a b c d e f
como una línea. No cuento el resto ya que es un código que debería estar en alguna biblioteca, para su reutilización. Si tiene que escribirse solo para este caso, es claramente excesivo como usted dice.