问题
I was wondering if anyone could help me. Suppose I have a tuple of type (String,Int)
, and a list of such tuples [("Strength",12),("Stamina",60),("Health",100)]
.
How can I use the function find
to extract the Int
value of the tuple ("Stamina",60)
if I don't actually know what order the tuples are inside the list but only that a certain tuple containing the string "Stamina"
exists?
I tried
value = snd ( find ("Stamina", _ ) stats )
where stats
is the list of tuples and value
is defined as
value :: a -> Int
..but it doesn't work :/ So any ideas? I'm specifically meant to use find
.
回答1:
It seems like you're trying to use a pattern as an argument to find
. Unfortunately, that won't work. You can only do pattern matching in a few places, such as guards and case
expressions.
As Jubobs has said in his comment, it's ideal to use Data.List.lookup
in this case, but using Data.List.find
is definitely possible as well.
If you take a look at the type of Data.List.find
, you'll see that it looks like this:
find :: Foldable t => (a -> Bool) -> t a -> Maybe a
This tells you two important things:
The first argument has to be a predicate function, i.e. a function that returns True when it's argument is of the value you are looking for and returns False otherwise
The function returns a
Maybe a
, which means it may returnNothing
and you have to deal with this.
Creating the predicate function is pretty easy. You just need something that tests the first value of the tuple using the ==
operator, perhaps a lambda function such as this:
\(x, _) -> x == "Stamina"
And now you can call find
like this:
find (\(x, _) -> x == "Stamina") stats
Alternatively, you could create a general function for comparing the first element of a tuple to a known value, such as the following:
matchFirst x (y, _) = x == y
And later on use the matchFirst
function as an argument to find
:
find (matchFirst "Stamina") stats
Now let's take a look at my second point: how do you deal with find
not finding anything?
If you are absolutely sure that it will always succeed, you can simply use Data.Maybe.fromJust
to extract the tuple from the Maybe
like this:
value = snd $ fromJust $ find (matchFirst "Stamina") stats
Otherwise, if the lookup can actually fail, there are many things you can do. You can, for example, use a sensible default value with Data.Maybe.fromMaybe
or you can change your value
to have the type Maybe Int
.
Finally, one last thing: you've said that value
has the type a -> Int
, i.e. a function that takes anything and returns an Int
. This is not actually the case. Instead, it's just a single value.
回答2:
More alternatives, which are not necessarily better than the above ones:
Using a pattern as the OP suggested
find (\c -> case c of ("A", _) -> True ; _ -> False) [("B",4), ("A", 5), ("C", 7)]
The same, exploiting an extension
{-# LANGUAGE LambdaCase #-}
find (\case ("A", _) -> True ; _ -> False) [("b",4), ("A", 5), ("C", 7)]
回答3:
Simple one-liner here using fst
and an equality ==
.
You need to import Data.List
and Data.Maybe
.
> let set stats = [("Strength",12),("Stamina",60),("Health",100)]
> snd $ fromJust $ find ((=="Strength") . fst) stats
12
回答4:
fn :: Eq a => a -> [(a, b)] -> b
fn x = snd . fromJust . find ((== x) . fst)
Then you would have
tuples :: [(String, Int)]
tuples = undefined -- a list of tuples
string :: String
string = "Stamina"
value :: Int
value = fn string tuples
来源:https://stackoverflow.com/questions/34278528/haskell-using-the-find-function-on-a-list-of-tuples