我有一个名为“Sample”的数据,定义为
data Sample = Test1 (String,[[String]])
| Test2 (String,[[String]])
然后我创建一个名为“Samples”的列表[Sample],比如说
[Test1 ("Sample1",[["works","running"]]), Test2 ("Sample2", []),
Test1 ("Sample3", [["aborts"]] ]
现在,我需要检查“Sample3”下是否存在“aborts”。我有一个基本的 如何做到这一点的想法,但我不知道如何在Haskell中实现它。
我的想法是
check:: String -> [String] -> [Sample] -> Bool
check samplename result samples =
我可以通过以下方式来称呼它:
check "Sample3" ["aborts"] Samples
但是我该如何实现这个功能。
我已经解决了这个问题,但我正在寻找更好的版本。
我的版本是:
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"]]
答案 0 :(得分:2)
这应该可以解决问题:
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
如果所有名称都不同,您可以更快地中止搜索:
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
测试:
*Main> check "Sample3" ["aborts"] tst
True
对您的版本的评论:
data Sample = Test1 (String,[[String]])
| Test2 (String,[[String]])
这里你不需要元组(参见我的代码)。
if( "True" `elem` final ) then True else False
通常,if expr then True else False
可以始终替换为expr
。为什么你使用字符串作为布尔?
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"]]
使用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
答案 1 :(得分:2)
如果我们考虑检查必须在高级别做什么,我们知道它必须检查样本的每个元素,看看测试“Sample3”是否包含任何(或所有,我不清楚你的意思)字符串结果存在。
所以我们知道我们需要递归,我们可以从函数的大致轮廓开始:
check :: String -> [String] -> [Sample] -> Bool
check samplename result [] = False
check samplename result (x:xs) = ...
因此,当列表为空时,不会发生匹配,因此我们可以立即返回false。在递归的情况下,我们知道我们需要检查x,如果没有找到匹配,继续检查xs。一种可行的方法是使用辅助函数检查'(您也可以只是内联检查')。
check samplename result (x:xs) = check' x || check samplename result xs
where check' ...
好的,那么检查'做什么?它检查数据类型Sample以查看是否发生任何匹配。我们知道Sample有两个构造函数, Test1 和 Test2 所以检查'应该看起来像
check' :: Sample -> Bool
check' (Test1 (name, values)) = ...
check' (Test2 (name, values)) = ...
我们要做的第一件事是测试 name 的值,看它是否与 samplename 相匹配。我们可以使用警卫轻松地完成这项工作
check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = ...
check' (Test2 (name, values)) | name == samplename = ...
check' _ = False
由于check'是check的子函数,check中定义的变量在范围内,所以我们可以引用它们。添加了一个新案例来处理名称不匹配的事件。
好的,现在的想法是检查结果中的任何(或所有)值是否出现在值中。幸运的是,前奏有一个我们可以使用的功能。
elem :: Eq a => a -> [a] -> Bool
现在该功能变为
check' :: Sample -> Bool
check' (Test1 (name, values)) | name == samplename = result `elem` values
check' (Test2 (name, values)) | name == samplename = result `elem` values
check' _ = False
因此完整的功能是:
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
使用谓词检查列表的每个元素是如此常见,以至于prelude具有标准函数。定义检查的一种可能方法是使用或和 map 功能。 这通常会导致功能效率降低:
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
通过调整数据类型的替代结构(例如
),可以进一步简化该功能type ID = Int
type Name = String
type Values = [[String]]
data Sample = Test ID Name Values
然后该功能变为
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
最后,由于检查的结果是Bool,而且警卫也是Bool,我们可以重构检查'更简单的形式,不需要通过案例
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
这个**或(map .. ..)**模式是如此常见,以至于还有一个前奏函数 any 这样做。然后可以将检查进一步简化为
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
答案 2 :(得分:2)
这是我的解决方案版本。但我不认为你的数据代表任何真正的问题,你可以轻松使用Map,如果你想存储像[Sample]这样的东西,它基本上是一个键值对列表。我的解决方案背后的想法也受到Map的启发。我写了一个类似于lookup的lookup'
函数,给定一个键返回值。写其余功能是微不足道的。与您的方法类似,但不那么混乱。
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