recursionsplitf#value-restriction

F# Split Function


I'm building a merge sort function and my split method is giving me a value restriction error. I'm using 2 accumulating parameters, the 2 lists resulting from the split, that I package into a tuple in the end for the return. However I'm getting a value restriction error and I can't figure out what the problem is. Does anyone have any ideas?

let split lst = 
    let a = []
    let b = []
    let ctr = 0
    let rec helper (lst,l1,l2,ctr) =
        match lst with
          | [] -> [] 
          | x::xs -> if ctr%2 = 0 then helper(xs, x::l1, l2, ctr+1)
                    else 
                    helper(xs, l1, x::l2, ctr+1)
    helper (lst, a, b, ctr)
    (a,b)

Any input is appreciated.


Solution

  • The code, as you have written it, doesn't really make sense. F# uses immutable values by default, therefore your function, as it's currently written, can be simplified to this:

    let split lst = 
        let a = []
        let b = []
        (a,b)
    

    This is probably not what you want. In fact, due to immutable bindings, there is no value in predeclaring a, b and ctr.

    Here is a recursive function that will do the trick:

    let split lst = 
        let rec helper lst l1 l2 ctr =
            match lst with
            | [] -> l1, l2 // return accumulated lists
            | x::xs -> 
                if ctr%2 = 0 then 
                    helper xs (x::l1) l2 (ctr+1) // prepend x to list 1 and increment
                else 
                    helper xs l1 (x::l2) (ctr+1) // prepend x to list 2 and increment
        helper lst [] [] 0
    

    Instead of using a recursive function, you could also solve this problem using List.fold, fold is a higher order function which generalises the accumulation process that we described explicitly in the recursive function above.

    This approach is a bit more concise but very likely less familiar to someone new to functional programming, so I've tried to describe this process in more detail.

    let split2 lst =
        /// Take a running total of each list and a index*value and return a new 
        /// pair of lists with the supplied value prepended to the correct list
        let splitFolder (l1, l2) (i, x) =
            match i % 2 = 0 with
            |true -> x :: l1, l2 // return list 1 with x prepended and list2
            |false -> l1, x :: l2 // return list 1 and list 2 with x prepended
        lst
        |> List.mapi (fun i x -> i, x) // map list of values to list of index*values
        |> List.fold (splitFolder) ([],[]) // fold over the list using the splitFolder function