Haskell置换库函数 - 请澄清一下?

时间:2013-06-25 15:09:46

标签: haskell recursion closures permutation

这是Haskell permutations模块中Data.List函数的代码:

permutations            :: [a] -> [[a]]
permutations xs0        =  xs0 : perms xs0 []
  where
    perms []     _  = []
    perms (t:ts) is = foldr interleave (perms ts (t:is)) (permutations is)
      where interleave    xs     r = let (_,zs) = interleave' id xs r in zs
            interleave' _ []     r = (ts, r)
            interleave' f (y:ys) r = let (us,zs) = interleave' (f . (y:)) ys r
                                     in  (y:us, f (t:y:us) : zs)

有人可以花些时间向我解释这段代码的工作原理吗?

我的困惑源于这样一个事实:我非常习惯于编写没有外部依赖关系的函数(即使它们嵌套在另一个函数中),特别是如果它们是递归的。由于permutationsperms以及ttsinterleave'的存在,就功能流程而言,我迷失了。

谢谢!

2 个答案:

答案 0 :(得分:3)

首先,我将以一种可能更容易重写的形式重写代码 理解,内部函数定义移到main函数之外。请注意,我必须向interleave添加一些参数 interleave'这样他们就可以“看到”他们拥有的所有相同变量 在其他功能中定义它们时的访问权限。

为了清晰起见,我还添加了类型签名。

permutations :: [a] -> [[a]]
permutations xs0 =  xs0 : perms xs0 []

函数perms有两个列表,并创建所有可能的列表 两个列表中元素的排列 - 但包括原始顺序。例如:

λ> perms "ab" "XY"
["aXYb","XaYb","aYXb","YaXb","baXY","abXY","aXbY","bXaY","XbaY","XabY","bYXa","YbXa","YXba","bXYa","XbYa","XYba","bYaX","YbaX","YabX","baYX","abYX","aYbX"]

因此,当我们使用空的第二个列表调用它时,如permutations所做的那样,它为我们提供了所有其他输入元素的可能排列。我们所要做的就是坚持原始序列,我们得到答案。 (如果你看一下上面的permutations,你会发现它正是它所做的。)

λ> perms "abc" ""
["bac","cba","bca","cab","acb"]

这是定义或perms

perms :: [a] -> [a] -> [[a]]
perms []     _  = []
perms (t:ts) is = foldr (interleave (t:ts)) (perms ts (t:is)) (permutations is)

函数interleave采用两个列表,并生成所有可能的列表 将它们混合在一起的方式(就像你一包卡片一样)。然后呢 将第三个列表附加到可能的随机播放列表中。例如:

λ> interleave "ab" "XY" ["@", "#"]
["aXYb","XaYb","@","#"]

这是它的定义:

interleave :: [t] -> [t] -> [[t]] -> [[t]]
interleave (t:ts) xs r  = let (_,zs) = interleave' (t:ts) id xs r in zs

interleave' :: [t] -> ([t] -> a) -> [t] -> [a] -> ([t], [a])
interleave' (_:ts) _ []     r = (ts, r)
interleave' (t:ts) f (y:ys) r  = let (us,zs) = interleave' (t:ts) (f . (y:)) ys r
                                     in  (y:us, f (t:y:us) : zs)

答案 1 :(得分:0)

尝试将任何递归调用视为对同一函数的调用,但使用不同的参数(希望,否则您可能会有无限循环),然后尝试遵循一个非常简单的示例的逻辑,如{{1 }},permutations []permutations [1]

查找内部表达式何时减少到基本情况,这是不再发生递归的地方。例如,permutations [1,2]的基础案例interleave'interleave' _ []的{​​{1}}已为。{/ p>

虽然我可能会因为尝试跟踪此函数的绕组而有点迷失,但我相信你会发现某些表达式会达到基本情况并从那里展开,你将能够评估递归调用那导致那里。