Hapus beberapa elemen dari daftar berdasarkan posisi

Saya ingin menghapus beberapa elemen dari daftar. Sejauh ini saya memiliki fungsi hapus:

deleteElem :: Int -> [a] -> [a]
deleteElem _ [] = []
deleteElem x zs | x > 0 = take (x-1) zs ++ drop x zs
                | otherwise = zs

Dan saya ingin menghapus misalnya dua posisi, yang disimpan dalam daftar dari daftar elemen. Jadi ini berfungsi dengan baik:

map (deleteElem 2) [["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"], [hello", "whatever", "foo", "bar"]]

Saya akan mendapatkan hasilnya:

[["hello", "whatever", "bar"], ["hello", "whatever", "bar"], ["hello", "whatever", "bar"], [hello", "whatever", "bar"]]

Tapi sekarang saya ingin melamar deleteElem [2,3]


person Paktwis Homayun    schedule 25.09.2013    source sumber
comment
pemetaan ganda dengan deleteElem sebagai operator. Tapi itu juga tidak berhasil   -  person Paktwis Homayun    schedule 25.09.2013


Jawaban (2)


Jika saya menafsirkan pertanyaan Anda dengan benar, Anda mengatakan bahwa Anda menginginkan cara untuk menerapkan fungsi Anda ke daftar indeks, bukan satu indeks pada satu waktu.

Cara paling sederhana yang dapat saya lakukan untuk melakukan ini adalah dengan membuat fungsi lain yang disebut deleteElems alih-alih deleteElem (perhatikan akhiran s.)

deleteElems akan bertipe [Int] -> [a] -> [a] dan akan memanggil setiap indeks.

CATATAN: Lihat PEMBARUAN di bagian bawah untuk solusi yang benar (Saya meninggalkan bagian ini di sini sehingga orang lain dapat belajar dari upaya awal saya untuk memecahkan masalah dan mengapa hal itu salah.)

Inilah salah satu cara untuk melakukannya:

deleteElems xs zs = foldr (\x z -> deleteElem x z) zs xs

yang dapat disingkat menjadi:

deleteElems xs zs = foldr deleteElem zs xs

Prelude> deleteElems [2,3] [1..10]
[1,4,5,6,7,8,9,10]

atau dari contoh Anda:

Prelude> map (deleteElems [2,3]) [["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"], ["hello", "whatever", "foo", "bar"]]
[["hello","bar"],["hello","bar"],["hello","bar"],["hello","bar"]]

deleteElems menggunakan foldr untuk memanggil deleteElem berulang kali guna menghapus indeks di xs dari zs. Untuk penjelasan lebih mendalam tentang foldr, lihat Bagaimana cara kerjafoldr?.

PEMBARUAN:

Sesuai komentar, implementasi deleteElems di atas sebenarnya salah karena ketika diberikan daftar indeks, katakanlah [2,4,6], pertama-tama ia akan menghapus indeks 2, mengembalikan daftar baru, kemudian menghapus indeks 4 pada daftar baru dan kembalikan daftar yang lebih baru, lalu hapus indeks 6 pada daftar yang lebih baru. Proses ini tidak komutatif, artinya mengubah urutan indeks, atau memberikan deleteElems indeks [6,4,2] tidak akan menghasilkan hal yang sama.

Saya memiliki cara untuk mendapatkan perilaku yang diantisipasi (menghapus indeks yang diberikan dari daftar asli) menggunakan fungsi intersect dari Data.List:

deleteElems xs zs = foldr1 intersect $ map ($ zs) $ map deleteElem xs

Versi baru deleteElems ini menerapkan deleteElem menggunakan setiap indeks di xs, membuat daftar length xs jumlah daftar fungsi deleteElem yang dimasukkan ke setiap indeks tertentu. Kemudian map ($ zs) menerapkan masing-masing fungsi deleteElem kari ke zs, menghasilkan daftar daftar, di mana setiap daftar bagian dalam hanya deleteElem diterapkan ke salah satu indeks dan zs. Terakhir, kami menggunakan intersect dari Data.List untuk menemukan daftar dengan semua elemen yang benar telah dihapus.

Selain itu, saya tetap merekomendasikan untuk memeriksa Bagaimana cara kerjafoldr?.

person DJG    schedule 25.09.2013
comment
Cobalah deleteElems [2, 4, 6] [1..10] == deleteElems [6, 4, 2] [1..10]. Apakah itu sama? Apakah harus sama? - person bheklilr; 25.09.2013
comment
Anda dapat mengurutkan Daftar Indeks secara menurun, maka masalahnya juga akan terpecahkan. - person kaan; 25.09.2013

Mari kita definisikan menghapus elemen pada posisi i sebagai pemisahan daftar pada posisi i, lalu menggabungkan kembali daftar tanpa elemen kepala pada bagian kedua daftar. Ini adalah apa yang telah Anda terapkan.

Sekarang, menghapus beberapa elemen seperti menghapus elemen dari bagian kedua daftar menggunakan prosedur yang sama - jika kita dapat menentukan sisa indeks relatif terhadap kepala bagian kedua.

Ini memerlukan definisi sederhana dari deleteElems:

deleteElems is = del [i-p | (p:i:_) <- tails $ sort $ 0:is] where
  del [] xs = xs
  del is xs = (r++) $ concatMap tail ss where
     (r:rs,ts) = unzip $ zipWith splitAt is $ xs:ts
     ss = filter (not . null) $ rs ++ [last ts]

Di sini pemahaman daftar membuat daftar indeks relatif terhadap indeks sebelumnya. Kemudian del menggunakan zipWith splitAt untuk membagi daftar pada posisi yang ditentukan. Catatan xs:ts mewakili daftar "bagian kedua" pada semua iterasi, dan r:rs adalah daftar "bagian pertama" pada semua iterasi. Jelasnya, r adalah "bagian pertama" pertama, dan disertakan secara utuh. Sisanya dipangkas dengan tail. Memfilter daftar kosong diperlukan jika indeks yang sama ditentukan lebih dari satu kali dalam daftar indeks.

person Sassa NF    schedule 25.09.2013