Dado que hay una manera de enlazar la cabeza y la cola de una lista mediante la coincidencia de patrones, me pregunto si puede usar la coincidencia de patrones para enlazar el último elemento de una lista.¿Se puede usar la coincidencia de patrones para enlazar el último elemento de una lista?
Respuesta
Sí, puede hacerlo, utilizando la extensión ViewPatterns
.
Prelude> :set -XViewPatterns
Prelude> let f (last -> x) = x*2
Prelude> f [1, 2, 3]
6
Tenga en cuenta que este patrón siempre tendrá éxito, sin embargo, por lo que probablemente querrá agregar un patrón para el caso en que la lista está vacía, de lo contrario last
lanzará una excepción.
Prelude> f []
*** Exception: Prelude.last: empty list
También tenga en cuenta que esto es solo azúcar sintáctica. A diferencia de la coincidencia de patrones normal, esto es O (n), ya que todavía está accediendo al último elemento de una lista de enlace único. Si necesita un acceso más eficiente, considere utilizar una estructura de datos diferente, como Data.Sequence
, que ofrece O (1) acceso a ambos extremos.
Puede utilizar ViewPatterns
que ver la coincidencia de patrones al final de una lista, por lo que vamos a hacer
{-# LANGUAGE ViewPatterns #-}
y utilizar reverse
como el viewFunction, porque siempre tiene éxito, así que por ejemplo
printLast :: Show a => IO()
printLast (reverse -> (x:_)) = print x
printLast _ = putStrLn "Sorry, there wasn't a last element to print."
Esto es seguro en el sentido de que no arroja ninguna excepción siempre que cubras todas las posibilidades. (Se puede escribir de nuevo para devolver un Maybe
, por ejemplo.)
La sintaxis
mainFunction (viewFunction -> pattern) = resultExpression
es el azúcar sintáctica para
mainFunction x = case viewFunction x of pattern -> resultExpression
para que pueda ver que en realidad sólo invierte la lista y el patrón coincide con eso, pero se siente mejor. viewFunction
es cualquier función que desee. (Uno de los objetivos de la extensión fue permitir a la gente a utilizar de manera limpia y fácil de acceso para funciones coincidencia de patrones para que no tuvieran que utilizar la estructura subyacente de su tipo de datos cuando definir funciones en él.)
Las otras respuestas explican las soluciones basadas en ViewPatterns
. Si desea hacerlo más coincidencia de patrones similares, puede empaquetar que en un PatternSynonym
:
tailLast :: [a] -> Maybe ([a], a)
tailLast [email protected](_:_) = Just (init xs, last xs)
tailLast _ = Nothing
pattern Split x1 xs xn = x1 : (tailLast -> Just (xs, xn))
y luego escriba su función como por ejemplo,
foo :: [a] -> (a, [a], a)
foo (Split head mid last) = (head, mid, last)
foo _ = error "foo: empty list"
Este es mi primer día de programación Haskell y también me encontré con el mismo problema, pero no pudo resolver a utilizar algún tipo de artefacto externo como se sugiere en las soluciones anteriores.
Mi sensación sobre Haskell es que si el lenguaje central no tiene solución para su problema, entonces la solución es transformar su problema hasta que funcione para el idioma.
En este caso, transformar el problema significa transformar un problema de cola en un problema de cabeza, que parece ser la única operación compatible en la coincidencia de patrones. Resulta que puede hacer eso fácilmente usando una inversión de lista, luego trabajar en la lista invertida usando elementos de encabezado ya que usaría elementos de cola en la lista original, y finalmente, si es necesario, revertir el resultado al orden inicial (por ejemplo, si era una lista).
Por ejemplo, dada una lista de enteros (por ejemplo [1,2,3,4,5,6]), supongamos que queremos construir esta lista en la que cada segundo elemento de la lista original comienza desde el final es reemplazado por su doble (ejercicio tomado de Homework1 de this excellent introduction to Haskell): [2,2,6,4,10,6].
entonces podemos utilizar el siguiente:
revert :: [Integer] -> [Integer]
revert [] = []
revert (x:[]) = [x]
revert (x:xs) = (revert xs) ++ [x]
doubleSecond :: [Integer] -> [Integer]
doubleSecond [] = []
doubleSecond (x:[]) = [x]
doubleSecond (x:y:xs) = (x:2*y : (doubleSecond xs))
doubleBeforeLast :: [Integer] -> [Integer]
doubleBeforeLast l = (revert (doubleSecond (revert l)))
main = putStrLn (show (doubleBeforeLast [1,2,3,4,5,6,7,8,9]))
Es obvio que es mucho más larga que las soluciones anteriores, pero se siente más Haskell-ish a mí.
¡Bienvenido a StackOverflow, y felicitaciones por tan buen comienzo! Invertir la lista para acceder al otro extremo es de hecho una solución muy Haskellish (aunque es bastante ineficiente, pero usar 'last' puede ser incluso peor si necesita hacerlo varias veces ... si el rendimiento es crítico y necesita acceder un último elemento, luego las listas no son un tipo adecuado). – leftaroundabout
Gracias. En mi nivel de novato, estoy realmente más interesado en descubrir los modismos de programación de Haskell que en medir su impacto en el rendimiento. Estoy de acuerdo en que definitivamente es un caso peor tratar de usar una lista de enlaces simples en orden inverso y supongo que esta es la razón por la que Haskell no ofrece coincidencia de patrones de cola al principio. (Las listas de Haskell se implementan usando listas de enlaces simples, ¿no?) – OlivierD
Correcto. Las listas de Haskell son geniales como stacks, o como streams infinitos, pero son realmente malas para el acceso aleatorio, y acceder al otro extremo no es mejor que acceder a cualquier elemento en el medio. – leftaroundabout
- 1. Uso de patrones para encontrar el enésimo elemento
- 2. ¿Cómo verificar el último elemento de una lista de Python?
- 3. Coincidencia de patrones de la lista anidada en Haskell
- 4. usando PARSENAME para buscar el último elemento en una lista
- 5. El compilador de Scala no puede inferir el tipo de mezcla para la coincidencia de patrones
- 6. coincidencia de patrones - implementación
- 7. ¿La "Lista" se maneja especialmente en la coincidencia de patrones de Haskell?
- 8. ¿Cómo eliminar el primer y último elemento de una lista?
- 9. entendimiento de coincidencia de patrones con el operador contras
- 10. Coincidencia de patrones de listas en Python
- 11. Coincidencia de patrones no especializados
- 12. Obtiene el último elemento en la lista de arrays
- 13. Iterador de segundo al último elemento de una lista
- 14. eliminando el último elemento de una lista (esquema)
- 15. jQuery ordenable: sin arrastrar el último elemento de la lista
- 16. Método de lista para eliminar el último elemento de la lista, así como todos los elementos
- 17. Coincidencia de patrones en una expresión let
- 18. Modificar el último elemento de una matriz
- 19. Consultar último elemento de cada lista
- 20. rendimiento de coincidencia de patrones Erlang
- 21. ¿se puede usar la detección de funciones HOG para la coincidencia de puntos clave?
- 22. ¿Cómo se puede hacer referencia al último elemento de una lista en una plantilla de Django? {{list.-1.key}}
- 23. Coincidencia de patrones al comienzo de una cadena en f #
- 24. coincidencia de patrones para la cadena que tiene "{"
- 25. implícito dentro de coincidencia de patrones
- 26. Seq de coincidencia de patrones en Haskell
- 27. Comprensión de la lista Python: ¿acceso al último elemento creado?
- 28. Separación y técnicas de coincidencia de patrones
- 29. Coincidencia de patrones y flujos infinitos
- 30. Scala - la designación de un elemento coincidente en la coincidencia de patrones
last arroja una excepción en una lista vacía. Estoy buscando alejarme del último, la cabeza, la primera y la última. Funciones que no manejan bien las listas vacías. La concordancia de patrones, let (x: xs) = "abcdefg" por ejemplo, es a lo que me refería en mi pregunta. Esperaba que hubiera un enfoque similar para obtener el último elemento, sin usar funciones de preludio inseguras. –
@MichaelLitchard: Bueno, puede definir, por ejemplo, una función 'safeLast' que devuelve' Maybe', con la que puede coincidir el patrón, o tal vez simplemente invertir la lista y luego usar una coincidencia de patrón normal. – hammar
, ya que siempre tiene éxito no es para nada una "coincidencia de patrón". 'f = (* 2). last' es mejor. – u0b34a0f6ae