我怎样才能检查一个字符串((字符串对和(字符串列表))列表?)

时间:2011-10-24 05:35:02

标签: haskell

我有一个名为“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"]]

3 个答案:

答案 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