2011-10-11 14 views
6

Inspirado por Comparing list lengthComparando longitud de la lista con las flechas

Si quiero encontrar la lista más larga de una lista de listas, la forma más sencilla es probablemente:

longestList :: [[a]] -> [a] 
longestList = maximumBy (comparing length) 

Una manera más eficiente sería para precalcular las longitudes:

longest :: [[a]] -> [a] 
longest xss = snd $ maximumBy (comparing fst) [(length xs, xs) | xs <- xss] 

Ahora, quiero dar un paso más. Puede que no sea más eficiente para casos normales, pero ¿puedes resolverlo usando las flechas? Mi idea es, básicamente, recorrer todas las listas al mismo tiempo, y seguir caminando hasta que hayas sobrepasado la longitud de cada lista, excepto la más larga.

longest [[1],[1],[1..2^1000],[1],[1]] 

En lo anterior ejemplo (muy artificial), sólo se tendría que dar dos pasos a través de cada lista con el fin de determinar que la lista [1..2^1000] es la más larga, sin necesitar para determinar la longitud completa de dicha lista . ¿Tengo razón en que esto se puede hacer con flechas? Si es así, ¿cómo? Si no, ¿por qué no, y cómo podría implementarse este enfoque?

+1

No veo ninguna conexión con las flechas. – luqui

+0

@luqui una sección del Wikilibro de Haskell en [Uso de flechas] (http://en.wikibooks.org/wiki/Haskell/Understanding_arrows#Using_arrows) parecía indicar que las flechas eran útiles para el análisis de una manera similar a la mía solución propuesta para este problema (observe el primer elemento de cada lista, luego el segundo, etc.) [Stephen's Arrow Tutorial] (http: //en.wikibooks.org/wiki/Haskell/StephensArrowTutorial) me dieron la misma sensación: que las flechas podrían usarse para profundizar en estas listas y almacenar información a medida que avanzan. –

+0

He aceptado una respuesta, pero si alguien puede preparar una respuesta con flechas, o explicar a fondo por qué las flechas no son pertinentes, entonces ciertamente lo aceptaré. –

Respuesta

2

Pensamiento sobre esto un poco más, hay una solución mucho más simple que da las mismas características de rendimiento. Podemos simplemente usar maximumBy con una función de comparación de longitud perezosa:

compareLength [] [] = EQ 
compareLength _ [] = GT 
compareLength [] _ = LT 
compareLength (_:xs) (_:ys) = compareLength xs ys 

longest = maximumBy compareLength 
+0

+1 Eso es muy elegante. Sin embargo, si no me equivoco, vuelve a cruzar las listas cada vez que compara 2 listas para ver cuál es más larga. –

+0

@ DanBurton: Sí, pero solo hasta la más corta de las dos listas. Entonces, es solo una cuestión de qué enfoque tiene los factores constantes más altos, que debo admitir que no he probado. – hammar

+0

Alternativamente, podría usar un tipo de datos algebraicos para números binarios para representar la longitud de una lista. De esta forma, solo obtendrá un factor logarítmico en la longitud de una lista para comparar las longitudes de dos listas y obtener al menos cierto grado de pereza. –

4

bien, cuando estaba escribiendo la pregunta, se me ocurrió una forma sencilla de implementar este (sin flechas, boo!)

longest [] = error "it's ambiguous" 
longest [xs] = xs 
longest xss = longest . filter (not . null) . map (drop 1) $ xss 

Excepto que esta tiene un problema ... que caiga la primera parte de la lista y no lo recupera!

> take 3 $ longest [[1],[1],[1..2^1000],[1]] 
[2,3,4] 

necesita más contabilidad: P

longest xs = longest' $ map (\x -> (x,x)) xs 

longest' [] = error "it's ambiguous" 
longest' [xs] = fst xs 
longest' xss = longest . filter (not . null . snd) . map (sndMap (drop 1)) $ xss 

sndMap f (x,y) = (x, f y) 

Ahora funciona.

> take 3 $ longest [[1],[1],[1..2^1000],[1]] 
[1,2,3] 

Pero no hay flechas. :(Si se puede hacer con las flechas, a continuación, esperemos que esta respuesta se le dará un lugar para empezar.

+0

Además, 'errror" es ambiguo "' es una forma bastante horrible de manejar listas de la misma longitud. Meh. Esta pregunta está diseñada específicamente para casos en los que tiene muchas listas cortas, y una muy larga. –

3

Aquí es la aplicación más sencilla que podía pensar. No hay flechas involucrados, sin embargo.

guardo una lista de pares donde el primer elemento es la lista original, y el segundo es la cola restante. Si solo nos queda una lista, hemos terminado. De lo contrario, tratamos de tomar la cola de todas las listas restantes, filtrando las que están vacías. Si algunos todavía permanecen, sigue adelante. De lo contrario, todos son de la misma longitud y arbitrariamente elegirá la primera.

longest [] = error "longest: empty list" 
longest xss = go [(xs, xs) | xs <- xss] 
    where go [(xs, _)] = xs 
     go xss | null xss' = fst . head $ xss 
       | otherwise = go xss' 
       where xss' = [(xs, ys) | (xs, (_:ys)) <- xss] 
+0

Podría cambiar la segunda línea a 'xss más largo = ir $ map (id &&& id) xss', pero supongo que ese no es el tipo de uso de flecha que estaba pensando :) – hammar

Cuestiones relacionadas