Keluarkan daftar String ([String]) dari daftar char dan int [([Char], Int)] di haskell
Saya memiliki kode ini:
numeros :: Int -> Int -> [Int]
numeros x y
| x < y = [n | n <- [1..x]]
| otherwise = [n | n <- [1..x+1], n /= y]
andares :: [([Char],Int)] -> [Int]
andares xs = [b | (a,b) <- xs]
lado :: [([Char],Int)] -> [[Char]]
lado xs = [a | (a,b) <- xs]
legendaCampainha :: Int -> Int -> [([Char],Int)] -> [String]
legendaCampainha x y xs = ...
Saya perlu menghasilkan sesuatu seperti ini:
> legendaCampainha 10 4 [("Dto", 10), ("Esq", 8), ("Cent", 3)]
["1Dto","1Esq","1Cent","2Dto","2Esq","2Cent","3Dto","3Esq",
"3Cent","5Dto","5Esq","6Dto","6Esq","7Dto","7Esq","8Dto",
"8Esq","9Dto","9Esq","10Dto","11Dto"]
10 adalah jumlah lantai, 4 adalah angka sial (jadi, lantainya 1,2,3,5,6 ... 11), "dto" "esq" "cent" adalah untuk mengatakan jika apartemen di sisi kanan, kiri atau tengah, dan angka di depan (10,8,3) adalah untuk mengatakan berapa banyak apartemen di kanan, kiri atau tengah. Misalnya, ada 10 apartemen di sisi kanan ("dto") dan 8 di sisi kiri ("esq").
Saya sudah membuat 3 fungsi untuk membantu saya melakukan ini, numeros adalah nomor lantai tanpa angka sial (Dalam hal ini [1,2,3,5,6,7,8,9,10,11]. Andares adalah jumlah berapa banyak apartemen di satu sisi tertentu (10,8,3) Lado adalah membaca sisi mana yang ada, dalam hal ini, ["Dto," Esq "," Cent "].
Saya hanya butuh petunjuk untuk menyelesaikan fungsi LegendaCampainha
Jawaban
Saya tidak akan menyelesaikan masalah Anda untuk Anda, tetapi berikut adalah petunjuk seperti yang Anda minta: Ini akan lebih mudah jika Anda mendefinisikan satu fungsi pembantu lagi, dari jenisnya [Char] -> [Int] -> [Char]. Fungsi ini akan mengambil string dan daftar lantai, dan menambahkan setiap nomor lantai ke string tersebut. Jadi misal dikasih argumen "Dto"dan [1,2,3,5,6], itu akan membuahkan hasil ["1Dto", "2Dto", "3Dto", "5Dto", "6Dto"]. Kemudian Anda harus bisa menyelesaikannya legendaCampainhadengan menerapkan fungsi itu ke setiap elemen xsmasukan Anda , lalu menggabungkan hasilnya.
Namun di luar itu, saya ingin berbicara sedikit tentang kode Anda yang lain. Secara fungsional terlihat sangat bagus, tetapi saya ingin melihat lebih dekat pada keterbacaannya.
Pertama, numeros:
numeros :: Int -> Int -> [Int]
numeros x y
| x < y = [n | n <- [1..x]]
| otherwise = [n | n <- [1..x+1], n /= y]
Bagi saya, ada satu hal yang langsung saya perhatikan tentang fungsi ini: [n | n <- [1..x]]pemahaman daftar itu. Jika Anda melihatnya, itu mengambil daftar [1..x], mendapatkan setiap nilai dari daftar secara bergantian, dan kemudian mengembalikan nilai itu tanpa perubahan. Dengan kata lain, [n | n <- [1..x]]bisa lebih mudah ditulis sesederhana itu [1..x]. Jauh lebih mudah dibaca!
Selain itu, bagian kedua dari fungsi ini dapat sedikit disederhanakan, dengan menggunakan fungsi yang telah ditentukan sebelumnya filter. Jika Anda belum pernah menemukan fungsi ini sebelumnya, filter fn listakan menerapkan fnfungsi tersebut ke setiap elemen list, lalu menghapus setiap elemen listyang fnmengembalikan nilai true. Yang sama persis dengan apa yang dilakukan oleh pemahaman daftar itu! Secara khusus, Anda ingin memfilter dengan (/= y)fungsi, yang mengambil satu argumen dan mengembalikan Trueketika argumen itu tidak sama dengan y. Jadi Anda bisa menggantinya [n | n <- [1..x+1], n /= y]dengan filter (/= y) [1..x+1].
Akhirnya, Anda dapat mengganti penjaga dengan ekspresi jika: if x < y then [n | n <- [1..x]] else [n | n <- [1..x+1], n /= y]. Tapi itu masalah preferensi pribadi daripada apa pun - beberapa orang menganggap penjaga lebih mudah dibaca, yang lain lebih suka jika ekspresi.
Selanjutnya, andaresdan lado:
andares :: [([Char],Int)] -> [Int]
andares xs = [b | (a,b) <- xs]
lado :: [([Char],Int)] -> [[Char]]
lado xs = [a | (a,b) <- xs]
Serupa dengan pemahaman daftar kedua, fungsi ini dapat disederhanakan dengan penggunaan fungsi yang telah ditentukan sebelumnya secara bijaksana. Dalam hal ini, yang relevan adalah map: map fn listakan diterapkan fnke setiap nilai list, lalu mengumpulkannya ke dalam daftar baru. Jadi misalnya map (+1) [1,5,10] == [2,6,11]. Selain itu, ada dua fungsi berguna yang harus diperhatikan: fstakan mengembalikan nilai pertama dari 2-tupel, dan sndakan mengembalikan nilai kedua dari 2-tupel. Dengan menggabungkannya, jelas bahwa andares xs = map snd xsdan lado xs = map fst xs- atau, lebih sederhananya, andares = map snddan lado = map fst.
Dan satu komentar lagi: Di Haskell, Stringadalah sinonim dari [Char]. Saya melihat Anda telah menggunakan Stringtanda tangan tipe Anda untuk legendaCampainha, tetapi penggunaan yang lebih konsisten sering kali dapat membuat tanda tangan tipe lebih mudah dipahami saat Anda banyak bekerja dengan string.
Jadi, mengumpulkan semua saran saya, saya mungkin menulis ulang kode Anda sebagai berikut:
numeros :: Int -> Int -> [Int]
numeros x y = if x < y then [1..x] else filter (/=y) [1..x+1]
andares :: [(String,Int)] -> [Int]
andares = map snd
lado :: [(String,Int)] -> [String]
lado = map fst
legendaCampainha :: Int -> Int -> [(String,Int)] -> [String]
legendaCampainha x y xs = ...
Sekarang, saya hanya ingin mengatakan: semua ini, pada dasarnya, adalah masalah preferensi pribadi. Jika menurut Anda kode asli Anda lebih mudah dibaca dan dipahami, maka - bagus! Gunakan itu! Tujuan saya di sini hanyalah untuk menyarankan beberapa alternatif yang mungkin Anda sukai lebih baik, dan memperkenalkan beberapa area Haskell lainnya yang mungkin belum Anda sadari sebelumnya.
(Dan akhirnya, satu petunjuk lagi, setelah saya berbicara sedikit tentang mapdan filter: melihat kembali fungsi yang saya sarankan di awal posting, seharusnya mungkin untuk menulisnya hanya dengan menggunakan map, showdan ++. Dapatkah Anda mencari tahu caranya lakukan itu?)