MiP
MiP

Reputation: 6442

Repeating appending to a list until throwing exception

I need to make a generic function that only stops when throwing an exception. In the example bellow, I need to create a positive number list: by creating a mutable list then appending to it infinitely.

I'm required to use exception for breaking the loop, how can I stop using a mutable list to save the result but achieve the same functionality?

let many f =
    let mutable list = []

    let rec many' () =
        list <- list @ f()
        many'()

    try many'() with Failure _ -> ()
    list

let mutable n = Console.ReadLine() |> Int32.Parse

let positiveNumbers = many (fun () ->
                               n <- n - 1
                               if n < 0 then failwith ""
                               else n)

Upvotes: 2

Views: 62

Answers (1)

Funk
Funk

Reputation: 11221

You can move the exception handling into the loop.

let many f =
    let rec loop list =
        try loop <| f() :: list
        with Failure _ -> List.rev list
    loop List.empty

let positiveNumbers = 
    many (fun () ->
        let n = Console.ReadLine() |> Int32.Parse
        if n > 0 then n
        else failwith "")

Still, you shouldn't use exceptions for (expected) program flow. What you're after can be done by introducing a predicate.

let many f predicate =
    let rec loop list =
        let n = f()
        if predicate n then
            loop <| n :: list
        else
            List.rev list
    loop List.empty

let positiveNumbers = 
    many (fun () -> Console.ReadLine() |> Int32.Parse) 
         (fun n  -> n > 0)

Wrapping things up --using the Option type for (extended) validation.

let many f =
    let rec loop list =
        match f() with
        | Some n
            -> loop <| n :: list
        | _ -> List.rev list
    loop List.empty

let f() =
    match Console.ReadLine() |> Int32.TryParse with
    | true, i 
        -> if i > 0 then Some(i) else None
    | _ -> None

let positiveNumbers = many f

Upvotes: 2

Related Questions