使用Ocaml配对列表中的交换元素

时间:2017-02-20 04:33:02

标签: ocaml

我是Ocaml初学者,无法理解尾递归或列表迭代。我们如何遍历列表是2s并交换对?

let rec swap = function
| a :: (b :: _ as t) -> b::a::swap t | smaller -> smaller;;
let newlist = swap [1;2;3;4];;
List.iter print_int newlist;

例如,1234,交换函数交换1和2,然后列表头仍然是2,并且它交换2和3,而它应该是3并且交换3和4。

1 个答案:

答案 0 :(得分:0)

您在swap尝试做的事情应该是

let rec swap = function
    | a :: b :: tail -> b :: a :: (swap tail)
    | whatever -> whatever

通过这种方式,您可以在头部交换两个元素并继续执行之后的操作。

但是,这里存在问题。让我们看看在swap [1; 2; 3; 4; 5; 6]电话:

的情况下会发生什么
    使用swap 调用
  1. [1; 2; 3; 4; 5; 6]
  2. a与1匹配,b与2匹配,并且......
  3. .... 2.1。使用swap

    调用[3; 4; 5; 6]

    .... 2.2。在第二次通话中a与3匹配,b与4匹配,并且......

    ........ 2.2.1。使用swap

    调用[5; 6]

    ........ 2.2.2。第三次调用a内部匹配5,b匹配6,并且...

    ............ 2.2.2.1。使用swap(空列表)

    调用[]

    ............ 2.2.2.2。此调用返回空列表

    ........ 2.2.3。 ...并在之前的swap调用中将其添加到ba的开头,生成6 :: 5 :: [][6; 5]

    ........ 2.2.4。返回

    .... 2.3。 ...并在之前的swap调用中将其添加到其开头添加(自己的)ba,生成4 :: 3 :: [6; 5]或{{1 }}

    .... 2.4。返回

    1. ...并且在之前的[4; 3; 6; 5]调用中将其添加到其开头添加(自己的)swapb,生成a2 :: 1 :: [4; 3; 6; 5]
    2. 作为最终结果返回
    3. 请注意,[2; 1; 4; 3; 6; 5]来电的整个链条必须在两个方向上进行:

      致电1,swap =>致电2,swap [1; 2; 3; 4; 5; 6] =>致电3,swap [3; 4; 5; 6] =>致电4 swap [5; 6],返回swap [],现在返回=>致电3,返回[] =>致电2,返回[6; 5] =>调用1,返回[4; 3; 6; 5]

      这意味着程序整个时间“向下”,它应该保留内存中的前一个路径,因此它可以返回。如果您的列表是200万个条目,则整个路径将是一百万个调用,并且应该保存在内存中。如果你运行这样的东西,你将耗尽堆栈空间。

      如何解决这个问题?通常的技术是使用某种“累加器”。

      [2; 1; 4; 3; 6; 5]

      这些功能是什么?让我们试着看看如果我们拨打let rec swap_helper acc = function | a :: b :: tail -> swap_helper (a :: b :: acc) tail | a :: [] -> a :: acc | [] -> acc ;; let swap2 lst = List.rev (swap_helper [] lst) 会发生什么。首先是调用swap2 [1; 2; 3; 4; 5; 6]

      1. swap_helper [] [1; 2; 3; 4; 5; 6]与1匹配,a与2匹配,btail匹配,因此我们调用[3; 4; 5; 6]。但是,看哪!当下一个调用返回时,我们根本没有对结果进行任何操作,因此没有理由返回 - 编译器将生成代码以立即返回我们将在swap_helper [1; 2] [3; 4; 5; 6]中获得的结果,而不会回到原始代码 - 因为原始调用中的结果没有任何关系。 这称为尾调用优化。

      2. 现在我们正在swap_helper [1; 2] [3; 4; 5; 6]来电,其中swap_helper [1; 2] [3; 4; 5; 6]与3匹配,a与4匹配,b与{{1}匹配},所以我们打电话给下一个tail,之后再没有什么可做的了,所以我们不会再回到这里了!

      3. 所以,[5; 6]致电。此处swap_helper [3; 4; 1; 2] [5; 6]为5,swap_helper [3; 4; 1; 2] [5; 6]为6,ab。我们继续tail,我们再回到这里,因为没有什么可做的了!

      4. []点击最后一场比赛并立即返回swap_helper [5; 6; 3; 4; 1; 2] [] - 但不是任何中间swap_helper [5; 6; 3; 4; 1; 2] []来电,它们已经被遗忘,没有工作要做剩下的 - 但直接原来[5; 6; 3; 4; 1; 2]来电!

      5. 现在在swap_helper我们反转了我们从swap2获得的列表,并根据需要获取swap2

        所以,重复:在swap_helper调用自身的代码中,没有什么可做的,所以没有理由返回,所以我没有理由花时间记住“回来”,就像我们一样不会回去。在原始代码中,我们无法做到这一点,因为[2; 1; 4; 3; 6; 5]正在调用自己,并且在准备好之后仍然必须将swap_helperswap粘贴到第二个调用的结果。