使用Ocaml对列表中的交换元素进行配对
我是一个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。在交换中尝试执行的操作应
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]
匹配,因此我们调用nextswap\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)