有没有更好的方法来编写indexof函数?

时间:2019-08-02 21:46:09

标签: haskell

我在haskell中编写了indexOf函数。有更好的书写方式吗? 我的第二个问题是 在我的函数中,尾函数是惰性计算的吗?

以下是我的功能索引

import Data.List
indexof :: String -> String -> Int
indexof lat patt = helper (tails lat) 0
        where helper [] _  = -1
              helper (x:xs) a = if prefix x patt then a else helper xs (a + 1)

prefix :: String -> String -> Bool
prefix _ [] = True
prefix [] _ = False
prefix (x:xs) (y:ys)  = if x == y then prefix xs ys else False

这按预期工作。

2 个答案:

答案 0 :(得分:4)

使用patt ern作为第一个参数看起来更惯用,通常无法通过-1或其他某个值来解决故障,而是通过使用Nothing并因此使用{{1 }}作为返回类型。

我们可以在此处使用Maybe Int模式,使其更加优雅,foldr具有isPrefixOf :: Eq a => [a] -> [a] -> Bool

Data.List

不过,最好实施Knuth-Morris-Pratt algorithm [wiki],因为这将导致在 O(m + n)中使用模式长度的 m 搜索和 n 字符串的长度。当前方法要求 O(m×n)

  

我的第二个问题是import Data.List(isPrefixOf, tails) indexof :: Eq a => [a] -> [a] -> Maybe Int indexof patt = foldr helper Nothing . tails where helper cur rec | isPrefixOf patt cur = Just 0 | otherwise = fmap (1+) rec 函数是否被延迟计算?

tails确实是懒惰的评估。但是,瓶颈可能不在tails :: [a] -> [[a]]中,因为tails在评估列表的 O(n)中运行,并且需要 O(n)内存也一样,因为tails指针是共享的。因此,它实际上并没有为每个项目构造一个新的列表,它只是在每个时间指向上一个元素的尾部。

答案 1 :(得分:2)

建立在Willem的答案上:通常通过使用[0..]压缩来跟踪索引。这里的方法是找到一个可能匹配的列表[Maybe Int],然后选择第一个(当然,这都是偷懒完成的,因此我们从来没有真正计算出第一个Just之后的匹配列表发生)。

indexOf :: (Eq a) => [a] -> [a] -> Maybe Int
indexOf needle haystack = firstJust $ zipWith findmatch [0..] (tails haystack)
    where
    findmatch ix suffix
       | needle `isPrefixOf` suffix -> Just ix
       | otherwise -> Nothing

    firstJust :: [Maybe a] -> Maybe a
    firstJust = getFirst . mconcat . map First
       -- N.B. should really use `coerce` instead of `map First`

我觉得这很“直接”,我喜欢。我们可以更聪明地减少代码大小:

indexOf needle haystack = listToMaybe . concat $ zipWith findmatch [0..] (tails haystack)
    where
    findmatch ix suffix = [ ix | needle `isPrefixOf` suffix ]

本质上,我们使用零元素列表或一元素列表来模拟Maybe,然后使用稍微更好的库和列表的符号支持来发挥我们的优势。这也可能融合得很好...(我对此没有很好的直觉)

但是,是的,如果您希望它更快,则应该使用KMP(在Text而不是String上)。但是,它涉及的更多。