Representar valores en elementos y elementos intermedios en Haskell

Dec 21 2020

Mientras hago la programación de la interfaz de usuario, a menudo encuentro la necesidad de representar una lista de valores y agregar información relacionada entre los valores representados. En el siguiente ejemplo de código, estoy renderizando valores numéricos en cadenas donde aparecen entre paréntesis y renderizo la distancia de dos valores en una cadena que se coloca entre las representaciones de los valores. El código funciona, pero me pregunto si una implementación de la mysteryfunción está disponible como parte de la biblioteca estándar de Haskell. También me interesan los nombres utilizados para esta función en otras bibliotecas, ya que el uso de palabras legibles por humanos facilita la búsqueda en Google.

mystery :: (a -> b) -> (a -> a -> b) -> [a] -> [b]
mystery n d [] = []
mystery n d [x] = [n x]
mystery n d (x:xs) = (n x) : (d x (head xs)) : mystery n d xs

node x = "(" ++ show x ++ ")"
distance x y = "-" ++ (show $ abs $ x - y) ++ "-"
render xs = concat $ mystery node distance xs
-- render [25, 68, 54, 15] == "(25)-43-(68)-14-(54)-39-(15)"

Respuestas

3 DDub Dec 21 2020 at 06:11

Su mysteryfunción en realidad es hacer un par de cosas a la vez, y si separa los comportamientos, puede ser un poco más fácil ver lo que está sucediendo.

Primero, estás mapeando ntodos los elementos. Podemos escribir eso como fmap n xs. A continuación, está construyendo nuevos elementos d x ypara todos los pares de elementos adyacentes xy y. Podemos escribir eso como zipWith d xs (tail xs).

El último paso es tomar estas dos construcciones y hacer una nueva lista con elementos que se alternan entre ellos. Curiosamente, esta fue una pregunta que se hizo hace 9 años , pero aún no tiene una respuesta súper satisfactoria. Probablemente la respuesta más simple sea definir su propia función:

alternate [] ys = ys
alternate (x:xs) ys = x : alternate ys xs

Entonces, podemos definir mysterycomo una línea:

mystery n d x = alternate (fmap n x) (zipWith d x (tail x))

Si realmente quieres que sea de una sola línea, lo mejor que se me ocurrió fue usar concaty transposede una manera un poco original:

mystery n d x = concat $ transpose $ (pure $ fmap n x) <> (pure $ zipWith d x (tail x))

Básicamente, estamos haciendo listas singleton de los dos componentes usando pure, fusionándolos con <>, luego haciendo transposeesta "lista de listas" para obtener los elementos correctamente intercalados y concatobteniendo el resultado.