Tail Recursive Combinations

☆樱花仙子☆ 提交于 2019-12-11 12:33:27

问题


I have this code:

    let rec combinations acc listC = 
        match listC with
        | [] -> acc 
        | h::t ->
            let next = [h]::List.map (fun t ->  h::t) acc @ acc
            combinations next t 

It looks tail recursive, but I keep getting stack overflows with it. Any ideas on how to make it work?


回答1:


combinations is tail recursive. Your problem is with the @ operator. Appending a list with it iterates the whole list, so as your acc becomes large, you will get a SO.

You can see here, that the @ operator is not tail recursive. The non-optimized version looks like: let rec (@) x y = match x with [] -> y | (h::t) -> h :: (t @ y).

To get around this problem, there are a couple of options:

  1. If you don't care about order, you could write a tail-recursive method to prepend the result like this:

    let rec prepend lst1 lst2 = match lst1 with | [] -> lst2 | head::tail -> prepend tail (head::lst2)

> prepend [1;2;3;4] [5;6;7;8];; 
val it : int list = [4; 3; 2; 1; 5; 6; 7; 8]
  1. If you care about order, you could write a method to first reverse the list and then prepend it. The drawback of this, of course, is that it will require twice as much memory since you are allocating an additional list to hold the reversed version of the original list. You can reuse the previous function to write something like this:

    let prepend2 lst1 lst2 = prepend (prepend lst1 []) lst2

> prepend2 [1;2;3;4] [5;6;7;8];; 
val it : int list = [1; 2; 3; 4; 5; 6; 7; 8]


来源:https://stackoverflow.com/questions/30684460/tail-recursive-combinations

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!