Haskell中的有效字符串交换

时间:2018-09-01 07:49:35

标签: haskell

我正在尝试解决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])

我的函数返回正确的结果,但是编程练习是定时的,并且看来我的代码运行太慢。

是否可以做一些事情来使我的代码运行更快,或者我采用了错误的方法来解决问题?

2 个答案:

答案 0 :(得分:9)

是的。如果您使用(++) :: [a] -> [a] -> [a],则这将花费您要连接的 first 列表中元素的线性时间。由于result可能很大,因此会导致效率低下:然后算法为 O(n 2

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

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

以上内容还揭示了实现的问题:如果列表的长度为奇数,则该函数将崩溃。在第二种情况下,我们通过返回一个列表来处理带有一个元素的列表(也许您需要根据规范进行修改)。

此外,在这里我们可以受益于Haskell的惰性:如果我们有一个很大的列表,想通过swap函数传递它,但是只对前五个元素感兴趣,那么我们将不< / em>计算整个列表。

我们还可以使用上述功能处理各种列表:数字列表,字符串列表等。

请注意,(++)本身并没有什么坏处:如果需要连接,这当然是最有效的方法。问题在于,您在每个递归步骤中都将再次连接 ,并且每次的左侧列表都在增长。

答案 1 :(得分:4)

在递归调用的累加器之前的末尾添加内容与在递归调用的将来结果之前处添加内容相同:

location

您将不得不在整个过程中相应地修改功能。

这称为保护递归。您所使用的称为尾递归(向左折叠)。

附加的好处是现在它可以在线 (即每个处理的元素花费 O(1)时间)。您正在 left 上创建 swapRec (x:y:xs) = [y]++[x]++result where result = swapRec xs 嵌套,这会导致二次行为。您可以看到它讨论得更多,例如here