3

I'd like to translate this ML code into F#.

fun take ([], i) = []
  | take (x::xs, i) = if i > 0 then x::take(xs, i-1) 
                                else [];

I tried this one

let rec take n i =
  match n,i with 
    | [], i -> []
    | x::xs, i -> if i > 0 then x::take(xs, i-1)
                           else [];

let val = take [1;2;3;4] 3

and this one

let rec take input =
  match input with 
    | ([], i) -> []
    | (x::xs, i) -> if i > 0 then x::take(xs, i-1)
                           else [];

let val = take ([1;2;3;4] 3)

But both of them gives me an error take.fs(7,5): error FS0010: Unexpected keyword 'val' in binding. What's wrong with the F# code?

pad
  • 41,040
  • 7
  • 92
  • 166
prosseek
  • 182,215
  • 215
  • 566
  • 871

2 Answers2

9

Just to add a few comments, I think the nicest way to write the function in F# is to use:

let rec take i n=  
  match n, i with
  | [], i -> []
  | _, i when i <= 0 -> []
  | x::xs, i -> x::(take (i-1) xs)

I did two changes:

  • Use pattern matching to test whether i <= 0 (which does the same thing as if, but looks a bit nicer)
  • Reverse the order of arguments so that the most important argument (the input list) is the last one. This makes it possible to use the function nicely with pipelining operator:

    [1;2;3;4] |> take 3
    
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
7

Since val is a reserved keyword in F#, you can't use it as a value. Your first version of take is wrong because the type of take(xs, i-1) (tuple form) is different from the type of take n i (curried form). This works:

let rec take n i =
  match n, i with 
    | [], i -> []
    | x::xs, i -> if i > 0 then x::(take xs (i-1)) else []

let value = take [1;2;3;4] 3

The second version has a mistake in the way you invoke the function. It could be fixed as follows:

let rec take input =
  match input with 
    | [], i -> []
    | x::xs, i -> if i > 0 then x::take(xs, i-1) else []

let value = take ([1;2;3;4], 3) // Notice ',' as tuple delimiter

Or you can write even closer to your ML function:

let rec take = function 
    | [], i -> []
    | x::xs, i -> if i > 0 then x::take(xs, i-1) else []

let value = take ([1;2;3;4], 3)
pad
  • 41,040
  • 7
  • 92
  • 166
  • For the second version, I got `error FS0003: This value is not a function and cannot be applied` error, and it seems to be the same as mine except the final ';'. – prosseek Jun 05 '11 at 21:57
  • No, you should use take ([1;2;3;4], 3) instead of take ([1;2;3;4] 3). Notice ',' is the difference between two versions. – pad Jun 05 '11 at 22:05