Secara pribadi saya tidak dapat memahami bagaimana pengakses indeks-at yang tidak mengembalikan tipe Mungkin dapat diterima sebagai idiomatik Haskell. [1,2,3]!!6akan memberi Anda kesalahan runtime. Ini dapat dengan mudah dihindari jika !!memiliki tipe [a] -> Int -> Maybe a. Alasan utama kami memiliki Haskell adalah untuk menghindari kesalahan runtime seperti itu!
worldsayshi
9
Ini pengorbanan. Simbol yang mereka pilih mungkin adalah simbol paling mengkhawatirkan yang bisa mereka miliki. Jadi saya pikir idenya adalah mengizinkannya untuk kasus-kasus tepi, tetapi membuatnya menonjol sebagai non-idiomatik.
cdosborn
3
itemOf :: Int -> [a] -> Maybe a; x `itemOf` xs = let xslen = length xs in if ((abs x) > xslen) then Nothing else Just (xs !! (x `mod` xslen)). Perhatikan, ini akan gagal secara serempak pada daftar yang tak terbatas.
Saya tidak mengatakan bahwa ada yang salah dengan pertanyaan Anda atau jawaban yang diberikan, tapi mungkin Anda ingin tahu tentang alat hebat Hoogle untuk menghemat waktu Anda di masa mendatang: Dengan Hoogle, Anda dapat mencari fungsi perpustakaan standar yang cocok dengan tanda tangan yang diberikan. Jadi, tidak tahu apa-apa tentang !!, dalam kasus Anda, Anda mungkin mencari "sesuatu yang mengambil Intdan daftar siapa dan mengembalikan satu seperti apa pun", yaitu
Int ->[a]-> a
Lo dan lihatlah , dengan!! sebagai hasil pertama (walaupun jenis tanda tangan sebenarnya memiliki dua argumen secara terbalik dibandingkan dengan apa yang kita mencari). Rapi, ya?
Selain itu, jika kode Anda bergantung pada pengindeksan (alih-alih memakan dari depan daftar), daftar mungkin sebenarnya bukan struktur data yang tepat. Untuk akses berbasis indeks O (1) terdapat alternatif yang lebih efisien, seperti array atau vektor .
Hoogle benar-benar hebat. Setiap programmer Haskell harus mengetahuinya. Ada alternatif yang disebut Hayoo ( holumbus.fh-wedel.de/hayoo/hayoo.html ). Ini mencari saat Anda mengetik tetapi tampaknya tidak secerdas Hoogle.
musiKk
61
Alternatif untuk menggunakan (!!)adalah dengan menggunakan
paket lensa dan elementfungsinya serta operator terkait. The
lensa menyediakan antarmuka yang seragam untuk mengakses berbagai struktur dan struktur bersarang atas dan di luar daftar. Di bawah ini saya akan fokus pada pemberian contoh dan akan mengabaikan kedua tanda tangan tipe dan teori di balik
paket lensa . Jika Anda ingin tahu lebih banyak tentang teori, tempat yang baik untuk memulai adalah file readme di repo github .
Mengakses daftar dan tipe data lainnya
Mendapatkan akses ke paket lensa
Di baris perintah:
$ cabal install lens
$ ghci
GHCi, version 7.6.3: http://www.haskell.org/ghc/:? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.>import Control.Lens
Mengakses daftar
Untuk mengakses daftar dengan operator infix
>[1,2,3,4,5]^? element 2-- 0 based indexing
Just 3
Berbeda dengan (!!)ini , ini tidak akan memunculkan pengecualian saat mengakses elemen di luar batas dan akan kembali Nothingsebagai gantinya. Seringkali disarankan untuk menghindari fungsi parsial seperti (!!)atau headkarena mereka memiliki lebih banyak kasus sudut dan lebih cenderung menyebabkan kesalahan waktu proses. Anda dapat membaca lebih banyak tentang mengapa menghindari fungsi parsial di halaman wiki ini .
>[1,2,3]!!9*** Exception: Prelude.(!!): index too large
>[1,2,3]^? element 9
Nothing
Anda dapat memaksa teknik lensa menjadi fungsi parsial dan membuat pengecualian saat di luar batas dengan menggunakan (^?!)operator alih-alih (^?)operator.
>[1,2,3]^?! element 12>[1,2,3]^?! element 9*** Exception:(^?!): empty Fold
Bekerja dengan tipe selain daftar
Namun ini tidak hanya terbatas pada daftar. Misalnya, teknik yang sama bekerja pada pohon dari paket kontainer standar
.
Sekarang kita dapat mengakses elemen pohon secara mendalam-urutan pertama:
> tree ^? element 0
Just 1> tree ^? element 1
Just 2> tree ^? element 2
Just 4> tree ^? element 3
Just 5> tree ^? element 4
Just 3> tree ^? element 5
Just 6> tree ^? element 6
Just 7
>import qualified Data.Sequence as Seq
> Seq.fromList [1,2,3,4]^? element 3
Just 4
Kita dapat mengakses array indeks int standar dari
paket vektor , teks dari paket teks standar
, bytestring dari paket bytestring standar
, dan banyak struktur data standar lainnya. Metode akses standar ini dapat diperluas ke struktur data pribadi Anda dengan menjadikannya sebagai instance kelas tipe Taversable , lihat daftar yang lebih panjang dari contoh Traversable dalam dokumentasi Lens. .
Struktur bersarang
Menggali struktur bersarang sangatlah mudah dengan hackage lensa . Misalnya mengakses elemen dalam daftar daftar:
>[[1,2,3],[4,5,6]]^? element 0. element 1
Just 2>[[1,2,3],[4,5,6]]^? element 1. element 2
Just 6
Komposisi ini berfungsi bahkan ketika struktur data bersarang memiliki tipe yang berbeda. Jadi misalnya jika saya punya daftar pohon:
>:{let
tree = Node 1[
Node 2[], Node 3[]]:}> putStrLn . drawTree . fmap show $ tree
1|+-2|`-3>:{let
listOfTrees =[ tree
, fmap (*2) tree -- All tree elements times 2, fmap (*3) tree -- All tree elements times 3]:}> listOfTrees ^? element 1. element 0
Just 2> listOfTrees ^? element 1. element 1
Just 4
Anda dapat menumpuk secara sewenang-wenang dengan jenis arbitrer selama memenuhi Traversablepersyaratan. Jadi mengakses daftar pohon urutan teks bukanlah masalah.
Mengubah elemen ke-n
Operasi umum dalam banyak bahasa adalah menetapkan ke posisi terindeks dalam array. Dengan python Anda mungkin:
>>> a =[1,2,3,4,5]>>> a[3]=9>>> a
[1,2,3,9,5]
The
lensa paket memberikan fungsi ini dengan (.~)operator. Meskipun tidak seperti di python, daftar asli tidak bermutasi, tetapi daftar baru dikembalikan.
>let a =[1,2,3,4,5]> a & element 3.~9[1,2,3,9,5]> a
[1,2,3,4,5]
element 3 .~ 9hanyalah sebuah fungsi dan (&)operator, bagian dari
paket lensa , hanyalah aplikasi fungsi terbalik. Ini dia dengan aplikasi fungsi yang lebih umum.
>(element 3.~9)[1,2,3,4,5][1,2,3,9,5]
Penugasan kembali berfungsi dengan baik dengan penumpukan acak Traversables.
>[[1,2,3],[4,5,6]]& element 0. element 1.~9[[1,9,3],[4,5,6]]
Bolehkah saya menyarankan untuk menautkan ke Data.Traversabledaripada mengekspor ulang lens?
dfeuer
@ pdfeuer - Saya menambahkan tautan ke Data.Traversable di basis. Saya juga menyimpan tautan lama dan menunjukkan bahwa ada daftar yang lebih panjang dari contoh yang dapat ditelusuri dalam dokumentasi Lens. Terima kasih untuk sarannya.
Davorak
11
Jawaban langsung sudah diberikan: Gunakan !!.
Namun pemula sering cenderung terlalu sering menggunakan operator ini, yang mahal di Haskell (karena Anda bekerja pada daftar tertaut tunggal, bukan pada array). Ada beberapa teknik yang berguna untuk menghindari hal ini, yang paling mudah adalah menggunakan zip. Jika Anda menulis zip ["foo","bar","baz"] [0..], Anda mendapatkan daftar baru dengan indeks "dilampirkan" ke setiap elemen berpasangan:, [("foo",0),("bar",1),("baz",2)]yang seringkali persis seperti yang Anda butuhkan.
Anda juga perlu berhati-hati dengan tipe Anda di sana. Sebagian besar waktu Anda tidak ingin berakhir dengan indeks menjadi Integer lambat daripada Ints mesin cepat. Bergantung pada apa sebenarnya fungsi Anda dan seberapa eksplisit pengetikan Anda, Haskell mungkin menyimpulkan tipe [0 ..] menjadi [Integer] bukan [Int].
chrisdb
4
Anda dapat menggunakan !!, tetapi jika Anda ingin melakukannya secara rekursif maka di bawah ini adalah salah satu cara untuk melakukannya:
dataAt :: Int ->[a]-> a
dataAt _[]= error "Empty List!"
dataAt y (x:xs)| y <=0= x
| otherwise = dataAt (y-1) xs
Tipe data daftar standar Haskell forall t. [t] dalam penerapannya sangat mirip dengan daftar tertaut C kanonik, dan pada dasarnya berbagi properti. Daftar tertaut sangat berbeda dari array. Terutama, akses oleh indeks adalah O (n) linier-, bukan operasi waktu-konstan O (1).
Jika Anda sering memerlukan akses acak, pertimbangkan Data.Arraystandarnya.
!!adalah fungsi yang ditentukan sebagian yang tidak aman, yang memicu error untuk indeks yang berada di luar rentang. Sadarilah bahwa perpustakaan standar berisi beberapa fungsi parsial seperti ( head, last, dll). Untuk keamanan, gunakan tipe opsi Maybe, atau Safemodul.
Contoh fungsi pengindeksan total yang cukup efisien dan kuat (untuk indeks ≥ 0):
data Maybe a = Nothing | Just a
lookup :: Int ->[a]-> Maybe a
lookup _[]= Nothing
lookup 0(x :_)= Just x
lookup i (_: xs)= lookup (i -1) xs
Bekerja dengan daftar tertaut, seringkali ordinal nyaman:
nth :: Int ->[a]-> Maybe a
nth _[]= Nothing
nth 1(x :_)= Just x
nth n (_: xs)= nth (n -1) xs
[1,2,3]!!6
akan memberi Anda kesalahan runtime. Ini dapat dengan mudah dihindari jika!!
memiliki tipe[a] -> Int -> Maybe a
. Alasan utama kami memiliki Haskell adalah untuk menghindari kesalahan runtime seperti itu!itemOf :: Int -> [a] -> Maybe a; x `itemOf` xs = let xslen = length xs in if ((abs x) > xslen) then Nothing else Just (xs !! (x `mod` xslen))
. Perhatikan, ini akan gagal secara serempak pada daftar yang tak terbatas.!!
adalah fungsi parsial dan dengan demikian tidak aman. Lihatlah komentar di bawah ini dan gunakanlens
stackoverflow.com/a/23627631/2574719Saya tidak mengatakan bahwa ada yang salah dengan pertanyaan Anda atau jawaban yang diberikan, tapi mungkin Anda ingin tahu tentang alat hebat Hoogle untuk menghemat waktu Anda di masa mendatang: Dengan Hoogle, Anda dapat mencari fungsi perpustakaan standar yang cocok dengan tanda tangan yang diberikan. Jadi, tidak tahu apa-apa tentang
!!
, dalam kasus Anda, Anda mungkin mencari "sesuatu yang mengambilInt
dan daftar siapa dan mengembalikan satu seperti apa pun", yaituLo dan lihatlah , dengan
!!
sebagai hasil pertama (walaupun jenis tanda tangan sebenarnya memiliki dua argumen secara terbalik dibandingkan dengan apa yang kita mencari). Rapi, ya?Selain itu, jika kode Anda bergantung pada pengindeksan (alih-alih memakan dari depan daftar), daftar mungkin sebenarnya bukan struktur data yang tepat. Untuk akses berbasis indeks O (1) terdapat alternatif yang lebih efisien, seperti array atau vektor .
sumber
Alternatif untuk menggunakan
(!!)
adalah dengan menggunakan paket lensa danelement
fungsinya serta operator terkait. The lensa menyediakan antarmuka yang seragam untuk mengakses berbagai struktur dan struktur bersarang atas dan di luar daftar. Di bawah ini saya akan fokus pada pemberian contoh dan akan mengabaikan kedua tanda tangan tipe dan teori di balik paket lensa . Jika Anda ingin tahu lebih banyak tentang teori, tempat yang baik untuk memulai adalah file readme di repo github .Mengakses daftar dan tipe data lainnya
Mendapatkan akses ke paket lensa
Di baris perintah:
Mengakses daftar
Untuk mengakses daftar dengan operator infix
Berbeda dengan
(!!)
ini , ini tidak akan memunculkan pengecualian saat mengakses elemen di luar batas dan akan kembaliNothing
sebagai gantinya. Seringkali disarankan untuk menghindari fungsi parsial seperti(!!)
atauhead
karena mereka memiliki lebih banyak kasus sudut dan lebih cenderung menyebabkan kesalahan waktu proses. Anda dapat membaca lebih banyak tentang mengapa menghindari fungsi parsial di halaman wiki ini .Anda dapat memaksa teknik lensa menjadi fungsi parsial dan membuat pengecualian saat di luar batas dengan menggunakan
(^?!)
operator alih-alih(^?)
operator.Bekerja dengan tipe selain daftar
Namun ini tidak hanya terbatas pada daftar. Misalnya, teknik yang sama bekerja pada pohon dari paket kontainer standar .
Sekarang kita dapat mengakses elemen pohon secara mendalam-urutan pertama:
Kami juga dapat mengakses urutan dari paket kontainer :
Kita dapat mengakses array indeks int standar dari paket vektor , teks dari paket teks standar , bytestring dari paket bytestring standar , dan banyak struktur data standar lainnya. Metode akses standar ini dapat diperluas ke struktur data pribadi Anda dengan menjadikannya sebagai instance kelas tipe Taversable , lihat daftar yang lebih panjang dari contoh Traversable dalam dokumentasi Lens. .
Struktur bersarang
Menggali struktur bersarang sangatlah mudah dengan hackage lensa . Misalnya mengakses elemen dalam daftar daftar:
Komposisi ini berfungsi bahkan ketika struktur data bersarang memiliki tipe yang berbeda. Jadi misalnya jika saya punya daftar pohon:
Anda dapat menumpuk secara sewenang-wenang dengan jenis arbitrer selama memenuhi
Traversable
persyaratan. Jadi mengakses daftar pohon urutan teks bukanlah masalah.Mengubah elemen ke-n
Operasi umum dalam banyak bahasa adalah menetapkan ke posisi terindeks dalam array. Dengan python Anda mungkin:
The lensa paket memberikan fungsi ini dengan
(.~)
operator. Meskipun tidak seperti di python, daftar asli tidak bermutasi, tetapi daftar baru dikembalikan.element 3 .~ 9
hanyalah sebuah fungsi dan(&)
operator, bagian dari paket lensa , hanyalah aplikasi fungsi terbalik. Ini dia dengan aplikasi fungsi yang lebih umum.Penugasan kembali berfungsi dengan baik dengan penumpukan acak
Traversable
s.sumber
Data.Traversable
daripada mengekspor ulanglens
?Jawaban langsung sudah diberikan: Gunakan
!!
.Namun pemula sering cenderung terlalu sering menggunakan operator ini, yang mahal di Haskell (karena Anda bekerja pada daftar tertaut tunggal, bukan pada array). Ada beberapa teknik yang berguna untuk menghindari hal ini, yang paling mudah adalah menggunakan zip. Jika Anda menulis
zip ["foo","bar","baz"] [0..]
, Anda mendapatkan daftar baru dengan indeks "dilampirkan" ke setiap elemen berpasangan:,[("foo",0),("bar",1),("baz",2)]
yang seringkali persis seperti yang Anda butuhkan.sumber
Anda dapat menggunakan
!!
, tetapi jika Anda ingin melakukannya secara rekursif maka di bawah ini adalah salah satu cara untuk melakukannya:sumber
Tipe data daftar standar Haskell
forall t. [t]
dalam penerapannya sangat mirip dengan daftar tertaut C kanonik, dan pada dasarnya berbagi properti. Daftar tertaut sangat berbeda dari array. Terutama, akses oleh indeks adalah O (n) linier-, bukan operasi waktu-konstan O (1).Jika Anda sering memerlukan akses acak, pertimbangkan
Data.Array
standarnya.!!
adalah fungsi yang ditentukan sebagian yang tidak aman, yang memicu error untuk indeks yang berada di luar rentang. Sadarilah bahwa perpustakaan standar berisi beberapa fungsi parsial seperti (head
,last
, dll). Untuk keamanan, gunakan tipe opsiMaybe
, atauSafe
modul.Contoh fungsi pengindeksan total yang cukup efisien dan kuat (untuk indeks ≥ 0):
Bekerja dengan daftar tertaut, seringkali ordinal nyaman:
sumber