I have a data called "Sample", defined as
data Sample = Test1 (String,[[String]])
| Test2 (String,[[String]])
Then I create a list[Sample] called "Samples", say
[Test1 ("Sample1",[["works","running"]]), Test2 ("Sample2", []),
Test1 ("Sample3", [["aborts"]] ]
Now, I need to check whether "aborts" is present under "Sample3". I have a basic idea of how to do it but I am not sure how to implement it in Haskell.
My idea is
check:: String -> [String] -> [Sample] -> Bool
check samplename result samples =
I can call it by:
check "Sample3" ["aborts"] Samples
But how do I implement this function.
I have solved like this but I am looking for a better version.
My version is:
check:: String -> [String] -> [Sample] -> Bool
check samplename result samples =
if( "True" `elem` final )
then True
else False
where
final = [x | sample <- samples,
x <- [ case sample of
Test1 (name,res) =
if name == samplename && (result `elem` res)
then "True"
else "False"
Test2 (name, res) = "False"]]
This should do the trick:
data Sample = Test1 String [[String]]
| Test2 String [[String]]
sampleName :: Sample -> String
sampleName (Test1 name _) = name
sampleName (Test2 name _) = name
getData :: Sample -> [[String]]
getData (Test1 _ samples) = samples
getData (Test2 _ samples) = samples
check :: String -> [String] -> [Sample] -> Bool
check name needle samples = any doCheck samples
where
doCheck s = if sampleName s == name
then needle `elem` getData s
else False
If all names are supposed to be distinct, you can abort the search faster:
check2 :: String -> [String] -> [Sample] -> Bool
check2 name needle samples = go samples
where
go [] = False
go (s:ss) = if sampleName s == name
then needle `elem` getData s
else go ss
Testing:
*Main> check "Sample3" ["aborts"] tst
True
Comments on your version:
data Sample = Test1 (String,[[String]])
| Test2 (String,[[String]])
You don't need tuples here (see my code).
if( "True" `elem` final ) then True else False
In general, if expr then True else False
can be always replaced with expr
. And why do you use strings as booleans?
final = [x | sample <- samples,
x <- [ case sample of
Test1 (name,res) -> if name == samplename
&& (result `elem` res)
then "True" else "False"
Test2 (name, res) -> "False"]]
This can be rewritten without list comprehensions by using any
:
check:: String -> [String] -> [Sample] -> Bool
check samplename result samples = any doCheck samples
where
doCheck (Test1 n s) = n == samplename && result `elem` s
doCheck _ = False
If we think about what check must do at a high level, we know it must check for every element of samples to see if the test "Sample3" contains any (or all, i'm unclear which you mean) of the strings result are present.
So we know we need recursion and we can start by making the general outline of the function:
check :: String -> [String] -> [Sample] -> Bool
check samplename result [] = False
check samplename result (x:xs) = ...
So when the list is empty, no matches can occur so we can immediately return false. In the recursive case we know we need to check x and if no match is found, continue checking xs. One possible way to do this is with a helper function check' (you can also just inline the check').
check samplename result (x:xs) = check' x || check samplename result xs
where check' ...
ok, so what does check' do? It checks the datatype Sample to see if any matches occur. We know Sample has two constructors, Test1 and Test2 so check' should look like
check' :: Sample -> Bool
check' (Test1 (name, values)) = ...
check' (Test2 (name, values)) = ...
The first thing we have to do is test the value of name to see if it matches samplename . We can do this easily using guards
check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = ...
check' (Test2 (name, values)) | name == samplename = ...
check' _ = False
Since check' is a child function of check, the variables defined in check are in scope, so we can just refer to them. A new case is added to handle the event that the names do not match.
Ok, so now the idea is to check if any (or all) of the values in result occur in values. Luckily the prelude has a function we can use for this.
elem :: Eq a => a -> [a] -> Bool
The function now becomes
check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = result `elem` values
check' (Test2 (name, values)) | name == samplename = result `elem` values
check' _ = False
The full function is thus:
check :: String -> [String] -> [Sample] -> Bool
check samplename result [] = False
check samplename result (x:xs) = check' x || check samplename result xs
where check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = result `elem` values
check' (Test2 (name, values)) | name == samplename = result `elem` values
check' _ = False
Checking every element of a list with a predicate is so common that the prelude has standard functions for this. one possible way to define check is using the functions or and map . This will generally result in a less efficient function though:
check :: String -> [String] -> [Sample] -> Bool
check samplename result samples = or (map check' samples)
where check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = result `elem` values
check' (Test2 (name, values)) | name == samplename = result `elem` values
check' _ = False
The function can further be simplified by adapting an alternative structure for the datatype, such as
type ID = Int
type Name = String
type Values = [[String]]
data Sample = Test ID Name Values
The function then becomes
check :: String -> [String] -> [Sample] -> Bool
check samplename result samples = or (map check' samples)
where check' :: Sample -> Bool
check' (Test _ name values) | name == samplename = result `elem` values
check' _ = False
Lastly since the result of check' is a Bool, and guards are Bool as well, we can refactor check' to a simpler form which doesn't need the fallthrough case
check :: String -> [String] -> [Sample] -> Bool
check samplename result samples = or (map check' samples)
where check' :: Sample -> Bool
check' (Test _ name values) = name == samplename && result `elem` values
This ** or (map .. ..)** pattern is so common that again there's a prelude function any which does this. check can then be further simplified to
check :: String -> [String] -> [Sample] -> Bool
check samplename result samples = any check' samples
where check' :: Sample -> Bool
check' (Test _ name values) = name == samplename && result `elem` values
Here is my version of the solution. But I dont think your data represent any real problem , you can easily use Map if you want to store something like [Sample] which is basically a list of key value pairs. Well the idea behind my solution is also inspired from Map . I have written a lookup'
function similar to lookup which given a key returns the value . writing the rest function is trivial then. Similar to your approach but less messy .
data Sample = Test1 (String,[[String]])
| Test2 (String,[[String]])
deriving Show
samples = [ Test1 ("Sample1",[["works","running"]]), Test2 ("Sample2", []), Test1 ("Sample3", [["aborts"]]) ]
fromSample :: Sample -> (String,[[String]])
fromSample (Test1 x) = x
fromSample (Test2 x) = x
lookup' :: String -> [Sample] -> Maybe [[String]]
lookup' str [] = Nothing
lookup' str (x:xs) | fst pair == str = Just $ snd pair
| otherwise = lookup' str xs
where pair = fromSample x
check :: String -> [String] -> [Sample] -> Bool
check sample str xs = case lookup' sample xs of
Nothing -> False
Just list -> str `elem` list
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.