简体   繁体   中英

Thinking recursively with F#

I have a function that takes an int and a list of ints. It returns a list of ints smaller than the first argument.

I am wanting to replicate this same code without using List.Filter :

let less e L = 
   L |> List.filter(fun a -> a < e)

less 3 [1;2;4;5]

I want to learn to think recursively as if I were writing this in SML

How would I write this recursively?

Basically the logic goes like this. For the base case, if the list is empty then you are done. Just return the empty list. For a list with an item at the head ( x ) and a tail ( xs ), if x < e return x appended the result of less applied to the tail. Otherwise, just return less applied to the tail.

let rec less e L =
  match L with
  | [] -> []
  | x :: xs -> if x < e then x :: (less e xs) else (less e xs)

However, this is somewhat inefficient as it is not tail recursive. Tail recursive calls are when the result of a recursive call is immediately returned. These are more efficient because the compiler can transform them basically into loops that do not consume additional stack space for each recursion. To do this we need a helper function with an accumulator:

let lessTailRec e L =
  let rec loop e L acc =
    match L with
    | [] -> acc
    | x :: xs -> if x < e then loop e xs (x :: acc) else loop e xs acc
  loop e L [] |> List.rev

I'm not sure if this compiles, as I just wrote it up in SO. Also, it's certainly not the best way to do it, but it gets you on the recursive track (mike z's shows a better way).

let rec less e L =
    match L with
    | [] -> []
    | (head::tail) -> if head < e then (head :: less e tail) else (less e tail)

Essentially, it consists of a couple steps.

First is pulling the head off the list (and if it's an empty list, then return an empty list)

Second is comparing the head with e

Third is sending the tail of the list down the line of recursion

Fourth is reconstructing the list, optionally including the head of the list.

The List.Filter function is just a generic function that lets you inject a function to replace the second step and saves you all the boilerplate. Usually you want to use List.Filter in all cases except when learning what it does.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM