【发布时间】:2016-03-24 11:46:51
【问题描述】:
所以我是 Haskell 的新手,我试图弄清楚为什么我的幼稚实现实际上比我认为的更智能的解决方案要快。
我正在尝试编写一个函数,给定一个String 将返回一个Bool,它指示字符串是否使用一个且正好是一个元音。以下是我的幼稚实现:
singleVowel :: Maybe String -> Bool
singleVowel Nothing = False
singleVowel (Just s) = singleVowel (Just s) = length (nub $ filter (`elem` "aeiouyAEIOUY") s) == 1
请注意,我过滤掉了所有不在元音集中的元素。然后,我使用nub 函数进行另一次传递,以从过滤列表中删除重复项,并查看列表中是否正好有 1 个元音。然后在最坏的情况下,此解决方案将使用O(n) 内存和时间,因为它必须为过滤列表分配内存。
现在对于我的另一个解决方案,我决定使用递归并在每个递归调用中传递一个字符,以跟踪当前元音(如果有的话)。
singleVowelFaster :: Maybe String -> Bool
singleVowelFaster Nothing = False
singleVowelFaster (Just s) = singleVowelHelper s Nothing
singleVowelHelper :: String -> Maybe Char -> Bool
singleVowelHelper [] Nothing = False
singleVowelHelper [] _ = True
singleVowelHelper (x:xs) Nothing = if x `elem` "aeiouyAEIOUY"
then singleVowelHelper xs (Just x)
else singleVowelHelper xs Nothing
singleVowelHelper (x:xs) (Just c) =
(x `elem` "aeiouyAEIOUY" && c == x) && singleVowelHelper xs (Just c)
但由于某些奇怪的原因,“幼稚”的实现比“更智能”的解决方案运行得更快。
难道(xelem"aeiouyAEIOUY" && c == x) 没有被评估,因为 Haskell 是惰性的,所以一旦达到基本情况,所有的 thunk 都会被评估,从而导致比“幼稚”实现慢?
如果是这样,那为什么是这样,当我将(xelem"aeiouyAEIOUY" && c == x)放入if语句中强制评估时,函数的性能是相似的?
我是否也可以说第二个函数具有O(1) 空间使用和O(n) 时间?
【问题讨论】:
-
Maybe String -> Bool的签名在这里很奇怪。你为什么不只拥有String -> Bool而忘记Nothing 案例? -
啊,我只是在试验 Maybe 类型类,真的没必要。
标签: performance haskell lazy-evaluation