Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用Ocaml对列表中的交换元素进行配对_Ocaml - Fatal编程技术网

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

使用Ocaml对列表中的交换元素进行配对,ocaml,Ocaml,我是一个Ocaml初学者,不能理解尾部递归或列表迭代。我们如何迭代列表并交换对 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。在交换中尝试执行的操作应

我是一个Ocaml初学者,不能理解尾部递归或列表迭代。我们如何迭代列表并交换对

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。

交换中尝试执行的操作应该是

let rec swap = function
    | a :: b :: tail -> b :: a :: (swap tail)
    | whatever -> whatever
通过这种方式,您可以交换头部的两个元素,然后继续这些元素之后的操作

然而,这里有一个问题。让我们看看在
交换[1;2;3;4;5;6]
调用的情况下会发生什么:

  • 使用
    [1;2;3;4;5;6]
  • a
    与1匹配,
    b
    与2匹配,以及
  • ……2.1<使用
    [3;4;5;6]

    ……2.2。在第二次调用中,
    a
    与3匹配,
    b
    与4匹配,并且

    ……2.2.1<使用
    [5;6]

    ……2.2.2。在第三次调用中,
    a
    与5匹配,
    b
    与6匹配,并且

    ……2.2.2.1<代码>交换
    通过
    []
    调用(空列表)

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

    ……2.2.3。。。在上一次的
    swap
    调用中,将
    b
    a
    添加到它的开头,生成
    6::5::[]
    [6;5]

    ……2.2.4。这是返回的

    ……2.3。。。在上一次的
    swap
    调用中,它被提取出来,将(它自己的)
    b
    a
    添加到它的开头,生成
    4::3::[6;5]
    [4;3;6;5]

    ……2.4。这是返回的

  • …在上一次的
    swap
    调用中,它被提取出来,在它的开头添加(它自己的)
    b
    a
    ,生成
    2::1::[4;3;6;5]
    [2;1;4;3;6;5]
  • 作为最终结果返回
  • 请注意,整个
    swap
    调用链必须双向进行:

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

    这意味着,在程序运行的整个过程中,它应该在内存中保留上一条路径,以便返回。如果您的列表是,比如说,200万个条目,那么整个路径将是100万个调用,并且应该保存在内存中。如果运行这样的东西,堆栈空间就会用完

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

    这些功能是什么?让我们看看调用
    swap2[1;2;3;4;5;6]
    会发生什么。它首先调用
    swap\u helper[][1;2;3;4;5;6]

  • a
    与1匹配,
    b
    与2匹配,
    tail
    [3;4;5;6]
    匹配,因此我们称之为
    swap\u helper[1;2][3;4;5;6]
    。但是你瞧!当下一个调用返回时,我们根本不处理结果,因此没有理由返回——编译器将生成代码以返回我们将立即在
    swap\u helper[1;2][3;4;5;6]
    中得到的结果,不返回原始代码——因为在原始调用中与该结果无关。这就是所谓的尾部调用优化

  • 现在我们在
    swap\u helper[1;2][3;4;5;6]
    调用中,其中
    a
    与3匹配,
    b
    与4匹配,
    tail
    [5;6]
    匹配,因此我们调用next
    swap\u helper[3;4;1;2][5;6]
    ,之后再无事可做,所以我们不会再回到这里了

  • 因此,
    swap\u helper[3;4;1;2][5;6]
    call。这里
    a
    是5,
    b
    是6,
    tail
    []
    。我们继续
    swap\u helper[5;6;3;4;1;2][
    我们不再回到这里了,因为已经没有什么事情可做了

  • swap\u helper[5;6;3;4;1;2][]
    点击匹配的最后一个案例,并立即返回
    [5;6;3;4;1;2]
    ——但不返回任何中间的
    swap\u helper
    调用,它们已经被忘记了,剩下的部分没有工作要做——而是直接返回原始的
    swap2
    调用

  • 现在在
    swap2
    中,我们根据需要反转从
    swap\u helper
    获得的列表,并获得我们的
    [2;1;4;3;6;5]


    所以,重复一次:在这段代码中,当
    swap\u helper
    调用自身时,没有什么可以做的,所以没有理由返回,所以没有理由花费内存来记住“往回走”,因为我们不会返回。在最初的代码中,我们无法做到这一点,因为
    swap
    正在调用它自己,并且在第二次调用完成后,仍然必须将
    a
    b
    粘附到该调用的结果上。

    您应该显示您编写的一些代码,并解释为什么它似乎不起作用。作为一般提示,如果将长度n的列表分成长度2和长度n-2两部分,则有一个容易解决的问题(长度为2)和同一问题的较小实例@dj007你应该用你的评论内容编辑你的问题,并稍微解释一下你正在展示的代码中有什么不起作用。提示:您可能从编译器/解释器得到的关于非穷尽性的警告很重要。正如杰弗里·斯科菲尔德(Jeffrey Scofield)所说,当swap的参数的形式为
    [a]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)