如何在Haskell中按索引访问列表,与此C代码类似?
int a[] = { 34, 45, 56 };
return a[1];
Run Code Online (Sandbox Code Playgroud)
phi*_*mue 142
看看这里,运营商!!.
即[1,2,3]!!1给你2,因为列表是0索引.
gsp*_*spr 85
我并不是说你的问题或给出的答案有任何问题,但也许你想知道Hoogle为将来节省时间的好工具:使用Hoogle,你可以搜索标准库函数匹配给定签名.所以,不知道任何事情!!,在你的情况下,你可能会搜索"需要一个Int和一个whatevers列表的东西,并返回一个这样的东西",即
Int -> [a] -> a
Run Code Online (Sandbox Code Playgroud)
Lo和注视,与!!作为第一个结果(虽然类型签名实际上有相反的两个参数相比,我们搜索的内容).整洁,对吧?
此外,如果您的代码依赖于索引(而不是从列表的前面消耗),则列表实际上可能不是正确的数据结构.对于基于O(1)索引的访问,存在更有效的替代方案,例如数组或向量.
Dav*_*rak 58
使用的另一种方法(!!)是使用
镜头包及其element功能和相关的操作员.该
透镜提供了用于访问各种超出列表结构和嵌套结构的一个统一的接口.下面我将重点介绍示例,并将掩盖镜头包装背后的类型签名和理论
.如果你想更多地了解这个理论,一个好的起点是github repo上的自述文件.
在命令行:
$ 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
Run Code Online (Sandbox Code Playgroud)
使用中缀运算符访问列表
> [1,2,3,4,5] ^? element 2 -- 0 based indexing
Just 3
Run Code Online (Sandbox Code Playgroud)
与(!!)此不同的是,当访问一个超出边界的元素时不会抛出异常,Nothing而是会返回.通常建议避免使用部分函数(!!),head因为它们有更多的极端情况并且更有可能导致运行时错误.您可以在此Wiki页面上阅读更多关于为什么要避免部分功能的内容.
> [1,2,3] !! 9
*** Exception: Prelude.(!!): index too large
> [1,2,3] ^? element 9
Nothing
Run Code Online (Sandbox Code Playgroud)
您可以强制镜头技术成为部分函数,并在使用(^?!)运算符而不是(^?)运算符时超出范围时抛出异常.
> [1,2,3] ^?! element 1
2
> [1,2,3] ^?! element 9
*** Exception: (^?!): empty Fold
Run Code Online (Sandbox Code Playgroud)
然而,这不仅限于列表.例如,相同的技术适用于标准 容器包中的树.
> 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
Run Code Online (Sandbox Code Playgroud)
我们现在可以按深度优先顺序访问树的元素:
> 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
Run Code Online (Sandbox Code Playgroud)
> import qualified Data.Sequence as Seq
> Seq.fromList [1,2,3,4] ^? element 3
Just 4
Run Code Online (Sandbox Code Playgroud)
我们可以从vector包,标准文本包中的 文本,标准bytestring包的 bytestrings以及许多其他标准数据结构访问标准的int索引数组 .这种标准访问方法可以通过将它们作为类型Taversable的实例扩展到您的个人数据结构,请参阅Lens文档中较长的示例Traversables列表..
使用镜头hackage可以简化为嵌套结构.例如,访问列表列表中的元素:
> [[1,2,3],[4,5,6]] ^? element 0 . element 1
Just 2
> [[1,2,3],[4,5,6]] ^? element 1 . element 2
Just 6
Run Code Online (Sandbox Code Playgroud)
即使嵌套数据结构具有不同类型,此组合也可以工作.例如,如果我有一个树列表:
> :{
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
Run Code Online (Sandbox Code Playgroud)
只要符合Traversable要求,您可以任意类型任意嵌套.因此,访问文本序列树的列表并不容易.
许多语言中的常见操作是分配给数组中的索引位置.在python中你可能会:
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
Run Code Online (Sandbox Code Playgroud)
该
镜头包给出与此功能(.~)操作.虽然与python不同,原始列表不会发生变异,而是返回一个新列表.
> let a = [1,2,3,4,5]
> a & element 3 .~ 9
[1,2,3,9,5]
> a
[1,2,3,4,5]
Run Code Online (Sandbox Code Playgroud)
element 3 .~ 9只是一个功能,(&)操作员,镜头包的一部分
,只是反向功能应用.这是更常见的功能应用程序.
> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]
Run Code Online (Sandbox Code Playgroud)
对任意嵌套Traversables 再次赋值再次完美.
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
Run Code Online (Sandbox Code Playgroud)
Lan*_*dei 11
直截了当的答案已经给出:使用!!.
然而,新手经常倾向于过度使用这个运算符,这在Haskell中很昂贵(因为你在单个链表上工作,而不是在数组上工作).有几种有用的技术可以避免这种情况,最简单的就是使用zip.如果你写zip ["foo","bar","baz"] [0..],你得到一个新的列表,其中索引"附加"到一对中的每个元素:[("foo",0),("bar",1),("baz",2)]这通常正是你所需要的.
您可以使用!!,但如果您想递归地使用它,那么下面是一种方法:
dataAt :: Int -> [a] -> a
dataAt _ [] = error "Empty List!"
dataAt y (x:xs) | y <= 0 = x
| otherwise = dataAt (y-1) xs
Run Code Online (Sandbox Code Playgroud)