Reputation: 85955
How can I access a list by index in Haskell, analog to this C code?
int a[] = { 34, 45, 56 };
return a[1];
Upvotes: 135
Views: 246561
Reputation: 706
The "Maybe-way" is a reasonabe approach.
Just to come up with an alternative, where you need to get a default value that you can determine beforehand.
atDefault :: Integral i => a -> i -> [a] -> a
atDefault aDef _ [] = aDef -- case: is empty anyway
atDefault _ 0 (a:_) = a -- case: index is 0 -> take it
atDefault aDef nIndex (_:la)
| nIndex > 0 = atDefault aDef (nIndex - 1) la -- case: index is positive
| otherwise = aDef -- case: index is negative
Alternatively, you may use atDef from package safe.
A use case might be the following:
You want to represent an endless set of numbers by implementing that using list of octets. Assuming the list has always a given set of n elements, assuming n is > 0 - but elements might be also accessed where you would usually consider it out of index range (index >= n or index < 0) - but instead you provide 0 as a default.
Example:
module Main (main) where
import qualified Data.Word as W
main :: IO ()
main =
do
print $ atDefault 0 (-1 :: Int) myNumberList
print $ atDefault 0 (0 :: Int) myNumberList
print $ atDefault 0 (1 :: Integer) myNumberList
print $ atDefault 0 (0x02 :: W.Word8) myNumberList
print $ atDefault 0 (3 :: Int) myNumberList
print $ atDefault 0 (4 :: Int) myNumberList
myNumberList :: [Int]
myNumberList = [17, 19, 23, 27]
atDefault :: Integral i => a -> i -> [a] -> a
atDefault aDef _ [] = aDef -- case: is empty anyway
atDefault _ 0 (a:_) = a -- case: index is 0 -> take it
atDefault aDef nIndex (_:la)
| nIndex > 0 = atDefault aDef (nIndex - 1) la -- case: index is positive
| otherwise = aDef -- case: index is negative
Output:
0
17
19
23
27
0
Upvotes: 1
Reputation: 33861
The following function seems ideal because it is not partial (unlike !!
), and is implemented by an existing library (relude):
(!!?) :: [a] -> Int -> Maybe a
https://hackage.haskell.org/package/relude-1.0.0.1/docs/Relude-List.html#v:-33--33--63-
Prelude> import Relude.List
Prelude Relude.List> ['a'..'f'] !!? 0
Just 'a'
Prelude Relude.List> ['a'..'f'] !!? 100
Nothing
Upvotes: 2
Reputation: 7120
Yet another option is to use genericIndex :: Integral i => [a] -> i -> a function which is less restrictive in it index type parameter than (!!) :: [a] -> Int -> a. Can be handy in some scenarios.
Upvotes: 0
Reputation: 237
I know it's an old post ... but it may be useful for someone ... in a "functional" way ...
import Data.List
safeIndex :: [a] -> Int -> Maybe a
safeIndex xs i
| (i> -1) && (length xs > i) = Just (xs!!i)
| otherwise = Nothing
Upvotes: 4
Reputation: 35973
Look here, the operator used is !!
.
I.e. [1,2,3]!!1
gives you 2
, since lists are 0-indexed.
Upvotes: 203
Reputation: 54574
The straight answer was already given: Use !!
.
However newbies often tend to overuse this operator, which is expensive in Haskell (because you work on single linked lists, not on arrays). There are several useful techniques to avoid this, the easiest one is using zip. If you write zip ["foo","bar","baz"] [0..]
, you get a new list with the indices "attached" to each element in a pair: [("foo",0),("bar",1),("baz",2)]
, which is often exactly what you need.
Upvotes: 13
Reputation:
Haskell's standard list data type forall t. [t]
in implementation closely resembles a canonical C linked list, and shares its essentially properties. Linked lists are very different from arrays. Most notably, access by index is a O(n) linear-, instead of a O(1) constant-time operation.
If you require frequent random access, consider the Data.Array
standard.
!!
is an unsafe partially defined function, provoking a crash for out-of-range indices. Be aware that the standard library contains some such partial functions (head
, last
, etc.). For safety, use an option type Maybe
, or the Safe
module.
Example of a reasonably efficient, robust total (for indices ≥ 0) indexing function:
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
Working with linked lists, often ordinals are convenient:
nth :: Int -> [a] -> Maybe a
nth _ [] = Nothing
nth 1 (x : _) = Just x
nth n (_ : xs) = nth (n - 1) xs
Upvotes: 6
Reputation: 253
You can use !!
, but if you want to do it recursively then below is one way to do it:
dataAt :: Int -> [a] -> a
dataAt _ [] = error "Empty List!"
dataAt y (x:xs) | y <= 0 = x
| otherwise = dataAt (y-1) xs
Upvotes: 8
Reputation: 7444
An alternative to using (!!)
is to use the
lens package and its element
function and associated operators. The
lens provides a uniform interface for accessing a wide variety of structures and nested structures above and beyond lists. Below I will focus on providing examples and will gloss over both the type signatures and the theory behind the
lens package. If you want to know more about the theory a good place to start is the readme file at the github repo.
At the command line:
$ 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
To access a list with the infix operator
> [1,2,3,4,5] ^? element 2 -- 0 based indexing
Just 3
Unlike the (!!)
this will not throw an exception when accessing an element out of bounds and will return Nothing
instead. It is often recommend to avoid partial functions like (!!)
or head
since they have more corner cases and are more likely to cause a run time error. You can read a little more about why to avoid partial functions at this wiki page.
> [1,2,3] !! 9
*** Exception: Prelude.(!!): index too large
> [1,2,3] ^? element 9
Nothing
You can force the lens technique to be a partial function and throw an exception when out of bounds by using the (^?!)
operator instead of the (^?)
operator.
> [1,2,3] ^?! element 1
2
> [1,2,3] ^?! element 9
*** Exception: (^?!): empty Fold
This is not just limited to lists however. For example the same technique works on trees from the standard containers package.
> 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
We can now access the elements of the tree in depth-first order:
> 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
We can also access sequences from the containers package:
> import qualified Data.Sequence as Seq
> Seq.fromList [1,2,3,4] ^? element 3
Just 4
We can access the standard int indexed arrays from the vector package, text from the standard text package, bytestrings fro the standard bytestring package, and many other standard data structures. This standard method of access can be extended to your personal data structures by making them an instance of the typeclass Taversable, see a longer list of example Traversables in the Lens documentation..
Digging down into nested structures is simple with the lens hackage. For example accessing an element in a list of lists:
> [[1,2,3],[4,5,6]] ^? element 0 . element 1
Just 2
> [[1,2,3],[4,5,6]] ^? element 1 . element 2
Just 6
This composition works even when the nested data structures are of different types. So for example if I had a list of trees:
> :{
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
You can nest arbitrarily deeply with arbitrary types as long as they meet the Traversable
requirement. So accessing a list of trees of sequences of text is no sweat.
A common operation in many languages is to assign to an indexed position in an array. In python you might:
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
The
lens package gives this functionality with the (.~)
operator. Though unlike in python the original list is not mutated, rather a new list is returned.
> 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
is just a function and the (&)
operator, part of the
lens package, is just reverse function application. Here it is with the more common function application.
> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]
Assignment again works perfectly fine with arbitrary nesting of Traversable
s.
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
Upvotes: 74
Reputation: 11227
I'm not saying that there's anything wrong with your question or the answer given, but maybe you'd like to know about the wonderful tool that is Hoogle to save yourself time in the future: With Hoogle, you can search for standard library functions that match a given signature. So, not knowing anything about !!
, in your case you might search for "something that takes an Int
and a list of whatevers and returns a single such whatever", namely
Int -> [a] -> a
Lo and behold, with !!
as the first result (although the type signature actually has the two arguments in reverse compared to what we searched for). Neat, huh?
Also, if your code relies on indexing (instead of consuming from the front of the list), lists may in fact not be the proper data structure. For O(1) index-based access there are more efficient alternatives, such as arrays or vectors.
Upvotes: 102