una recupera que promedio ordenar listas lista función funciones enlazadas eliminar elementos ejercicios cabeza haskell

haskell - recupera - ¿Por qué la función de ciclo no puede funcionar con una lista vacía?



ordenar lista haskell (5)

El problema surge cuando se trata de acceder a elementos en la lista. Una función de ciclo autodefinida que opera en una lista no vacía no tiene problemas cuando se accede, pero al intentar obtener, por ejemplo, los primeros 3 elementos de la lista vacía ciclada dan como resultado un bucle infinito:

cycle'' xs = xs ++ cycle'' xs take 3 (cycle'' [1,2]) -- returns [1,2,1] take 3 (cycle'' []) -- still looping

Utilicé el cycle funciones en algunos de mis proyectos y hoy descubrí que no es una función total, como se muestra en este ejemplo de GHCI:

λ> Data.List.cycle [] *** Exception: Prelude.cycle: empty list

Sé que Haskells intenta usar funciones totales (excepto las funciones fundamentales head y tail ) y no estoy completamente seguro de por qué el cycle no es una de ellas. En mi opinión, el cycle de la lista vacía es la lista vacía y no veo problemas con esto. ¿Por qué cycle para listas vacías produce un error?

EDITAR : Basado en las primeras respuestas, creo que mi idea no está completamente clara: no quiero que el cycle [] sea ​​un cálculo que nunca termina. Por el contrario, creo que el cycle [] debería ser el:

cycle :: [a] -> [a] cycle [] = [] cycle xs = xs ++ cycle xs

El [] es el cycle [] porque todas las operaciones hacen exactamente lo que yo exijo. Por ejemplo, take 3 [] es [] y, por lo tanto, take 3 (cycle []) podría ser [] . ¿Cuál es el problema con esta solución?


La intención del cycle como se describe en la documentación es:

import Data.List.Nonempty import Data.Stream.Infinite cycle :: NonEmpty a -> Stream a

Los autores de Prelude utilizan una función parcial para pasar en una lista vacía, que es conceptualmente un error de tipo, similar a la head y la tail .

Si desea un ciclo que devuelve [] es tan fácil como:

myCycle :: [a] -> [a] myCycle xs = if null xs then xs else cycle xs

Consulte: semigroups para la definición de No NonEmpty y streams para la definición de Stream y una definición total de cycle .


No tengo una visión especial de la (s) mente (s) de las personas que implementaron la función del cycle .

El cycle :

el ciclo une una lista finita en una circular, o de manera equivalente, la repetición infinita de la lista original. Es la identidad en listas infinitas.

Tradicionalmente, cuando piensas en una lista enlazada circularmente, en una entrada de wiki tienes:

¿Cómo expresaría una lista vacía circular? ¿Un puntero va a sí mismo? Pero incluso eso no encaja.

Mi mejor explicación es que las listas circulares no son listas normales. Son diferentes bestias con diferentes semánticas. Al igual que head realidad solo se define por completo en una lista vacía no vacía porque no hay un primer elemento de una lista vacía, el cycle solo se define completamente en listas no vacías porque no hay una lista enlazada circular vacía.


Tenga en cuenta que tal como está definido actualmente, es consistente con la tail .

tail [] = error ...

cycle está relacionado conceptualmente con la tail . Cuando realiza un cycle una lista, eso significa que puede mirar repetidamente su tail y nunca llegar al "final" ( [] ), porque es un ciclo. (Vea la imagen de Davorak). En otras palabras, siempre es seguro usar tail en una lista de cycle , asumiendo, por supuesto, que era seguro usar un cycle en esa lista en primer lugar.

Yo, por mi parte, creo que es una cosa perfectamente razonable de definir.

tail [] = [] cycle [] = []

Pero debes redefinir tanto el cycle como la tail para tail consistencia.


cycle realmente se define como devolver una lista infinita para todas las entradas. Si intentara hacerlo ingenuamente con una entrada vacía, se sentaría en un bucle infinito. La condición de error es ligeramente más informativa con la misma semántica denotacional.

Editar:

Como la gente no parece entender lo que quiero decir cuando digo que la salida en blanco es mala, considere esta simple función:

labelElements :: [a] -> [b] -> [(a, b)] labelElements labels elements = zip (cycle labels) elements

Es agradable y simple, con una condición de error evidente cuando la lista de etiquetas está vacía. Si el ciclo devolvía una lista vacía en una entrada vacía, haría que labelElements silenciosamente ese error a su salida. En este momento, grita y grita que te equivocaste. Uno de estos es mucho mejor que el otro.