【问题标题】:Efficient string swapping in HaskellHaskell 中的高效字符串交换
【发布时间】:2019-02-07 02:43:31
【问题描述】:

我正在尝试解决在 Haskell 中进行函数式编程练习的问题。我必须实现一个函数,给定一个包含偶数个字符的字符串,该函数返回相同的字符串并交换了字符对。

像这样:

“helloworld”->“ehllworodl”

这是我当前的实现:

swap :: String -> String
swap s = swapRec s ""
  where
    swapRec :: String -> String -> String
    swapRec [] result       = result
    swapRec (x:y:xs) result = swapRec xs (result++[y]++[x])

我的函数返回正确的结果,但是编程练习是定时的,而且我的代码似乎运行得太慢了。

我可以做些什么来让我的代码运行得更快,或者我使用了错误的方法来解决问题?

【问题讨论】:

  • 是的,不要使用(++),因为它需要O(n)

标签: haskell


【解决方案1】:

在累加器的末尾附加一些东西传递到一个递归调用

    swapRec (x:y:xs) resultSoFar  =  swapRec xs 
         (resultSoFar ++ [y] ++ [x])

与在递归调用返回的结果开头添加它相同

    swapRec (x:y:xs)  =  [y] ++ [x] ++ swapRec xs 

您必须在整个过程中相应地修改您的功能。

这称为保护递归。您使用的是尾递归(left 折叠)。

额外的好处是它现在将在线(即,每个处理的元素花费 O(1) 时间)。您在 left 上创建了 (++) 嵌套,这会导致 quadratic 行为,如所讨论的,例如here.

【讨论】:

    【解决方案2】:

    是的。如果您使用(++) :: [a] -> [a] -> [a],那么这将花费您要连接的 first 列表的元素数量的线性时间。由于result 可能很大,这将导致效率低下:那么算法是O(n2)

    但是,您不需要 用累加器构造结果。您可以返回一个列表,并通过递归调用处理剩余元素,例如:

    swap :: [a] -> [a]
    swap [] = []
    swap [x] = [x]
    swap (x:y:xs) = y : x : swap xs

    上面还发现了实现的一个问题:如果列表的长度是奇数,那么函数就会崩溃。在第二种情况下,我们通过返回该列表来处理具有一个元素的列表(也许您需要根据规范对其进行修改)。

    此外,我们可以从 Haskell 的惰性中受益:如果我们有一个很大的列表,想要通过 swap 函数传递它,但只对前五个元素感兴趣,那么我们将 计算整个列表。

    我们还可以用上面的函数处理各种列表:数字列表、字符串列表等

    请注意,(++) 本身并不是固有不好的:如果您需要连接,这当然是最有效的方法。问题是你在每个递归步骤中都会再次连接,并且左列表每次都在增长。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-28
      • 2010-09-27
      • 2013-08-17
      • 2011-08-22
      • 1970-01-01
      • 2018-07-10
      相关资源
      最近更新 更多