从Haskell之路到逻辑的子串练习

时间:2016-03-04 09:06:52

标签: haskell

我最近开始通过Keets Doets的书“Haskell通向逻辑,数学和编程之路”这本书开始工作。 Jan van Eijck(非常好的书)。

在其中一个练习中,任务是定义子字符串:我的解决方案有效 并且比作者的短得多,但是我并不妄想谁是更好的逻辑师。

那么,我错过了什么:

prefix :: String -> String -> Bool
prefix [] y          = True
prefix x []          = False
prefix (x:xs) (y:ys) = (x == y) && (prefix xs ys)

substring :: String -> String -> Bool
substring x []     = False
substring x (y:ys) | prefix x (y:ys) = True
                   | otherwise = substring x ys 

-- Thought the answer provided was a bit overdone
substring' :: String -> String -> Bool 
substring' [] ys = True 
substring' (x:xs) [] = False
substring' (x:xs) (y:ys) = ((x==y) && (prefix xs ys)) || (substring' (x:xs) ys)

亲切的问候Auke

1 个答案:

答案 0 :(得分:3)

由于我想在一个更大的项目中为自己尝试QuickCheck ,这对我来说是一个很好的练习。 QuickCheck是一个库,可以在生成的测试用例的属性中自动测试您的函数。你也可以创建自己的发电机,但我没有在这里做。

首先,我使用cabal install QuickCheck安装了QuickCheck。我通过import Test.QuickCheck导入了模块,然后我定义了属性:

prop_substring xs ys = substring xs ys == substring' xs ys

此属性(如果提供给QuickCheck)将生成xsys作为String的参数。它将检查属性是否为True,在这种情况下应该发生,因为substring函数当然应该返回相同的结果。

要快速查看该功能,我使用了verboseCheck prop_substring。这将检查100个生成的测试用例。第一个结果是:

Failed:
""
""
*** Failed! Falsifiable (after 1 test):
""
""

所以:不,这两个功能不一样。这是因为在你的函数substring中,如果第一个参数为空,则不测试它是子字符串的基本情况,所以我添加了一行:

substring [] ys = True

然后,我再次测试了它。以下是最后两个生成的测试用例示例:

Passed:
"C8Q<r6\195@\v_\195\DC1\170"
"E\219\DLE"
Passed:
"$ I\SYN\232\164\EOT9\182Ldah\255\173\DC2-B\DC2\SUBuF|\235iQ\236l\vS129\237x?}\187\229C\SYNUVUc/3bO7mE\ESCHB7V\DEL\FSM\EM\202^\162!\GS\DC3\\\nja\201\ESC\ENQOi"
"&?\USx>{\147\DC4g\171\EM\240Ha%\"C\ETX \SI\FS=\DC2\214V%H"
+++ OK, passed 100 tests.

但这只是100次测试,还有更多?您可以使用其他函数并使用其他参数。让我们尝试使用100.000个测试用例:

*Substring> quickCheckWith stdArgs { maxSuccess = 100000 } prop_substring
+++ OK, passed 100000 tests.

所以是的,似乎两个功能都能提供相同的结果!虽然有两个不足之处:第一个是QuickCheck不太可能生成两个带有substring的字符串。当它生成随机String时,它更有可能生成两个完全不同的String。这可以通过创建自己的生成器来解决。第二个缺点是QuickCheck没有给你一个正式的证据。

可以使用属性分析第一个。如果我们将prop_substring更改为:

prop_substring xs ys = 
  collect (substring xs ys) $
  (substring xs ys == substring' xs ys)

然后我们收集结果,因此我们可以看到结果的百分比。对于100.000,这是:

*Substring> quickCheckWith stdArgs { maxSuccess = 100000 } prop_substring
+++ OK, passed 100000 tests:
94% False
 5% True

所以,大约5000返回True。您还可以生成xsys,并为substring - 函数提供参数xsxs++ysxsys++xs仅测试True个案例。两个选项都通过了100.000次测试,所以我们几乎可以假设两个函数都给出了相同的结果。

有关QuickCheck的更多信息有点outdated manual。例如,您可以告诉QuickCheck您需要一定数量的True个测试用例,而不是成功测试用例的总数(当substring个函数都导致False时,也会成功)。