Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding F# tail-recursive

Recently, I'm learning F#. I try to solve problem in different ways. Like this:

(*

[0;1;2;3;4;5;6;7;8] -> [(0,1,2);(3,4,5);(6,7,8)]

*)

//head-recursive
let rec toTriplet_v1 list=
    match list with
    | a::b::c::t -> (a,b,c)::(toTriplet_v1 t) 
    | _ -> []

//tail-recursive
let toTriplet_v2 list=
    let rec loop lst acc=
        match lst with
        | a::b::c::t -> loop t ((a,b,c)::acc)
        | _ -> acc
    loop list []

//tail-recursive(???)
let toTriplet_v3 list=
    let rec loop lst accfun=
        match lst with
        | a::b::c::t -> loop t (fun ls -> accfun ((a,b,c)::ls))
        | _ -> accfun []
    loop list (fun x -> x)

let funs = [toTriplet_v1; toTriplet_v2; toTriplet_v3];
funs |> List.map (fun x -> x [0..8]) |> List.iteri (fun i x -> printfn "V%d : %A" (i+1) x)

I thought the results of V2 and V3 should be the same. But, I get the result below:

V1 : [(0, 1, 2); (3, 4, 5); (6, 7, 8)]
V2 : [(6, 7, 8); (3, 4, 5); (0, 1, 2)]
V3 : [(0, 1, 2); (3, 4, 5); (6, 7, 8)]

Why the results of V2 and V3 are different?

like image 240
kev Avatar asked Jun 01 '10 08:06

kev


People also ask

How do you know which f-stop to use?

To keep both the foreground and the background in focus for a photo spanning a lot of distance like this one does, try using stop settings of f/16 or f/22 (definitely over f/11). Also, when you're in the wide depth of field range, use the Sunny 16 Rule: On a sunny day, it's best to use stops of f/16 or higher.

Is higher or lower f-number better?

Lower f/stops give more exposure because they represent the larger apertures, while the higher f/stops give less exposure because they represent smaller apertures. This may seem a little contradictory at first but will become clearer as you take pictures at varying f/stops.

What does f-stop stand for?

Aperture and f-stop. The “f” in f-stop stands for the focal length of the lens. While focal length itself refers to the field of view of a lens, f-stop is about how much light you allow to hit the sensor via the aperture opening.

How do you read an f-number?

Selecting a lower f-number is "opening up" the lens. Selecting a higher f-number is "closing" or "stopping down" the lens.


1 Answers

V2 uses standard accumulating variable to get tail recursion done:

loop ([0;1;2;3;4;5;6;7;8], []) ->
  loop ([3;4;5;6;7;8], [(0,1,2)]) ->
    loop ([6;7;8], [(3,4,5), (0,1,2)]) ->
      loop ([], [(6,7,8), (3,4,5), (0,1,2)]) ->
        [(6,7,8), (3,4,5), (0,1,2)]

V3 uses continuation, or in plain English, an accumulating function:

loop ([0;1;2;3;4;5;6;7;8], x->x) ->
  loop ([3;4;5;6;7;8], x->(0;1;2)::x) ->
    loop ([6;7;8], x->(3;4;5)::x) ->
      loop ([], x->(6,7,8)::x) ->
        [(6,7,8)]  // x->(6,7,8)::x applies to []
    ->
      [(3,4,5);(6,7,8)] // x->(3,4,5)::x applies to [(6,7,8)]
  ->
    [(0,1,2);(3,4,5);(6,7,8)] // x->(0,1,2)::x applies to [(3,4,5);(6,7,8)]

You can see the difference between accumulating variables and accumulating functions:

Using accumulating variables stops at the last call as the accumulating variable stores the answer. However, the accumulating function still does some backtrack work after the last call. It should be noticed that using accumulating functions is indeed tail recursive as the recursive call loop t (fun ls -> accfun ((a,b,c)::ls)) is actually the last statement of this function.

Btw, the code you provided is a good example to show the concept tail recursive functions. One way to understand these sample code is to work on small cases as I did in the above two illustrations. After working on some small cases, you will understand the concept deeper.

like image 200
Yin Zhu Avatar answered Oct 10 '22 15:10

Yin Zhu