Merender nilai menjadi item dan item perantara di Haskell

Dec 21 2020

Saat melakukan pemrograman antarmuka pengguna, saya sering menemui kebutuhan untuk membuat daftar nilai dan menambahkan beberapa informasi terkait antara nilai yang diberikan. Dalam contoh kode berikut saya merender nilai numerik menjadi string di mana mereka muncul dalam tanda kurung dan membuat jarak dua nilai menjadi string yang ditempatkan di antara render nilai. Kode berfungsi tetapi saya bertanya-tanya apakah implementasi mysteryfungsi tersedia sebagai bagian dari pustaka standar Haskell. Saya juga tertarik dengan nama yang digunakan untuk fungsi ini di perpustakaan lain, karena menggunakan kata-kata yang dapat dibaca manusia membuat googling lebih mudah.

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)"

Jawaban

3 DDub Dec 21 2020 at 06:11

mysteryFungsi Anda sebenarnya melakukan beberapa hal sekaligus, dan jika Anda memisahkan perilakunya, mungkin akan lebih mudah untuk melihat apa yang sedang terjadi.

Pertama, Anda memetakan nsemua elemen. Kita bisa menulisnya sebagai fmap n xs. Selanjutnya, Anda membangun elemen baru d x yuntuk semua pasangan elemen yang berdekatan xdan y. Kita bisa menulisnya sebagai zipWith d xs (tail xs).

Langkah terakhir adalah mengambil dua konstruksi ini dan membuat daftar baru dengan elemen yang bergantian di antara keduanya. Menariknya, ini adalah pertanyaan yang diajukan 9 tahun yang lalu , tetapi masih belum memiliki jawaban yang sangat memuaskan. Mungkin jawaban paling sederhana adalah dengan mendefinisikan fungsi Anda sendiri:

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

Kemudian, kita dapat mendefinisikan mysterysebagai satu baris:

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

Jika Anda benar - benar menginginkannya menjadi satu baris, yang terbaik yang dapat saya hasilkan adalah menggunakan concatdan transposedengan sedikit cara yang funky:

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

Pada dasarnya, kita membuat daftar tunggal dari dua komponen yang digunakan pure, menggabungkannya dengan <>, lalu transposemenggunakan "daftar daftar" ini untuk mendapatkan elemen yang diselingi dengan benar, dan concathasilnya.