Renderizando valores em itens e itens intermediários em Haskell
Ao fazer a programação da interface do usuário, muitas vezes encontro a necessidade de renderizar uma lista de valores e adicionar algumas informações relacionadas entre os valores renderizados. No exemplo de código a seguir, estou renderizando valores numéricos em strings onde aparecem entre parênteses e renderizando a distância de dois valores em uma string que é colocada entre as renderizações dos valores. O código funciona, mas estou me perguntando se uma implementação da mystery
função está disponível como parte da biblioteca padrão Haskell. Também estou interessado em nomes usados para essa função em outras bibliotecas, pois o uso de palavras legíveis por humanos torna a busca no Google mais fácil.
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)"
Respostas
mystery
Na verdade, sua função está fazendo algumas coisas ao mesmo tempo e, se você separar os comportamentos, pode ser um pouco mais fácil ver o que está acontecendo.
Primeiro, você está mapeando n
todos os elementos. Podemos escrever isso como fmap n xs
. Em seguida, você está construindo novos elementos d x y
para todos os pares de elementos adjacentes x
e y
. Podemos escrever isso como zipWith d xs (tail xs)
.
A última etapa é pegar essas duas construções e fazer uma nova lista com elementos que se alternam entre elas. Curiosamente, esta foi uma pergunta feita há 9 anos , mas ainda não tem uma resposta super satisfatória. Provavelmente, a resposta mais simples é definir sua própria função:
alternate [] ys = ys
alternate (x:xs) ys = x : alternate ys xs
Então, podemos definir mystery
como uma linha:
mystery n d x = alternate (fmap n x) (zipWith d x (tail x))
Se você realmente quer que seja um one-liner, o melhor que eu consegui foi usar concat
e transpose
de uma maneira um pouco descolada:
mystery n d x = concat $ transpose $ (pure $ fmap n x) <> (pure $ zipWith d x (tail x))
Basicamente, estamos criando listas singleton com os dois componentes usando pure
, fundindo-os juntos com <>
, em seguida, transpose
ing esta "lista de listas" para obter os elementos devidamente intercalados e concat
ing o resultado.