Renderowanie wartości na elementy i elementy pośrednie w Haskell

Dec 21 2020

Podczas programowania interfejsu użytkownika często napotykam potrzebę renderowania listy wartości i dodawania powiązanych informacji między wyrenderowanymi wartościami. W poniższym przykładzie kodu renderuję wartości liczbowe w ciągach, w których pojawiają się w nawiasach, i renderuję odległość dwóch wartości w ciągu, który jest umieszczany między renderowaniami wartości. Kod działa, ale zastanawiam się, czy implementacja mysteryfunkcji jest dostępna jako część standardowej biblioteki Haskell. Interesują mnie również nazwy używane do tej funkcji w innych bibliotekach, ponieważ użycie słów czytelnych dla ludzi ułatwia wyszukiwanie w 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)"

Odpowiedzi

3 DDub Dec 21 2020 at 06:11

Twoja mysteryfunkcja faktycznie polega na wykonywaniu kilku rzeczy naraz, a jeśli oddzielisz zachowania, może być trochę łatwiej zobaczyć, co się dzieje.

Najpierw odwzorowujesz nwszystkie elementy. Możemy to napisać jako fmap n xs. Następnie tworzysz nowe elementy d x ydla wszystkich par sąsiednich elementów xi y. Możemy to napisać jako zipWith d xs (tail xs).

Ostatnim krokiem jest skorzystanie z tych dwóch konstrukcji i utworzenie nowej listy z elementami, które naprzemiennie się między nimi przemieszczają. Co ciekawe, było to pytanie zadane 9 lat temu , ale nadal nie ma super satysfakcjonującej odpowiedzi. Prawdopodobnie najprostszą odpowiedzią jest zdefiniowanie własnej funkcji:

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

Następnie możemy zdefiniować mysteryjako jednowierszowy:

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

Jeśli naprawdę chcesz, aby był to jeden wiersz, najlepsze, co mogłem wymyślić, to użycie concati transposetrochę funky:

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

Zasadniczo tworzymy listy singletonowe z dwóch komponentów przy użyciu pure, łącząc je razem z <>, a następnie transposetworząc tę ​​„listę list”, aby elementy były odpowiednio przeplatane i uzyskując concatwynik.