Haskell : using the `find` function on a list of tuples

懵懂的女人 提交于 2020-03-28 07:06:51

问题


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:

  1. 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

  2. The function returns a Maybe a, which means it may return Nothing 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!