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?
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.
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.
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.
Selecting a lower f-number is "opening up" the lens. Selecting a higher f-number is "closing" or "stopping down" the lens.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With