문제

i'm new to F# and i've come up with a solution to a fairly simple question, which is, given a string of numbers, I need to multiple each digit by two, then get the sum of these digits. For example, the string "123456" should equate to 24

Here is what I was able to come up with

let input = "123456"
        |> Seq.map (string >> int)
        |> Seq.map (fun(x) -> x * 2)
        |> Seq.map (int >> string)
        |> String.concat String.Empty
        |> Seq.map (string >> int)
        |> Seq.sum

My question is, is there any substantial changes I could make, to make my solution more condensed and more efficient? Any help will be greatly appreciated

도움이 되었습니까?

해결책

You can compact your code by applying some properties.

fun x -> x * 2 is

fun x -> (*) x 2 (operator as a function)

fun x -> (*) 2 x (commutative)

(*) 2 (eta-reduction)

map composition is the same as the composition of the functions to map, your first 3 maps could be written as:

Seq.map (string >> int >> (*) 2 >> string)

map and then concat is collect

map and then sum is sumBy

So your code could be:

"123456"
    |> String.collect (string >> int >> (*) 2 >> string)
    |> Seq.sumBy (string >> int)

다른 팁

I understand that this might be a toy example, but I think that it is actually quite hard to write it in a readable way. I had to run your code line-by-line before I actually understood what is going on!

You can definitely contract it into quite short expression using point-free style (as Gustavo's answer shows), but I would probably not go that far - because you might need to be able to read & understand the code later!

An alternative way would be to use sequence expressions and write something like this:

[ for c in "123456" do
    // Get the number, multiply it & yield its characters
    let number = int (string c)
    yield! string (number * 2) ]
// Sum the numbers (converting char to int via a string)
|> Seq.sumBy (string >> int)

This is a bit shorter than your original version, but it is still (I think) fairly understandable.

In terms of efficiency, instead of converting each character in the string into a string in its own right just so you can then convert it to an int...

|> Seq.map (string >> int)

I would convert the character directly to a number:

|> Seq.map (Char.GetNumericValue >> int)

This results in less garbage to collect, and although I haven't benchmarked it, I would expect it to be faster.

Like Tomas commented I've not considered the digit-requirement in my original post. So heres my corrected version:

"123456"
  |> Seq.map    (string >> int)        // Convert to int sequence
  |> Seq.collect(fun x -> string(x*2)) // Perform multiplication, 
                                       // convert to string and collect the digits as chars
  |> Seq.sumBy  (string >> int)        // Convert the chars to string and than to integer 
                                       // to sum them correctly

It is more or less the same what Gustavo posted.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top