Jawaban:
Lihatlah di sini , operator yang digunakan yaitu!!
.
Yaitu [1,2,3]!!1
memberi Anda 2
, karena daftar diindeks 0.
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 gunakan lens
stackoverflow.com/a/23627631/2574719
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 Int
dan 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 .
Alternatif untuk menggunakan (!!)
adalah dengan menggunakan
paket lensa dan element
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 .
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
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 Nothing
sebagai gantinya. Seringkali disarankan untuk menghindari fungsi parsial seperti (!!)
atau head
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 .
> [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 1
2
> [1,2,3] ^?! element 9
*** Exception: (^?!): empty Fold
Namun ini tidak hanya terbatas pada daftar. Misalnya, teknik yang sama bekerja pada pohon dari paket kontainer standar .
> import Data.Tree
> :{
let
tree = Node 1 [
Node 2 [Node 4[], Node 5 []]
, Node 3 [Node 6 [], Node 7 []]
]
:}
> putStrLn . drawTree . fmap show $tree
1
|
+- 2
| |
| +- 4
| |
| `- 5
|
`- 3
|
+- 6
|
`- 7
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
Kami juga dapat mengakses urutan dari paket kontainer :
> 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. .
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 Traversable
persyaratan. Jadi mengakses daftar pohon urutan teks bukanlah masalah.
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 .~ 9
hanyalah 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 Traversable
s.
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
Data.Traversable
daripada mengekspor ulang lens
?
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.
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 opsi Maybe
, atau Safe
modul.
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!