F# - 代码改进

时间:2014-05-13 10:44:23

标签: f#

我是F#的新手,我想出了一个相当简单的问题的解决方案,给定一串数字,我需要将每个数字加倍,然后得到这些数字的总和。例如,字符串“123456”应等于24

这是我能够提出的

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

我的问题是,我能做出哪些重大改变,使我的解决方案更加精简和高效?任何帮助将不胜感激

4 个答案:

答案 0 :(得分:10)

您可以通过应用某些属性来压缩代码。

fun x -> x * 2

fun x -> (*) x 2 (作为函数的运算符)

fun x -> (*) 2 x (可交换)

(*) 2 (eta-reduction)

map组成与要映射的函数的组成相同,您的前3个映射可以写成:

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

map然后concatcollect

map然后sumsumBy

所以你的代码可能是:

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

答案 1 :(得分:2)

我知道这可能是一个玩具示例,但我认为以可读的方式编写它实际上非常困难。在我真正理解发生了什么之前,我必须逐行运行你的代码!

你可以使用无点样式(如Gustavo的答案所示)将它缩小为非常短的表达式,但我可能不会那么远 - 因为你可能需要能够阅读&稍后了解代码!

另一种方法是使用序列表达式并编写如下内容:

[ 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)

这比原始版本略短,但它仍然(我认为)相当容易理解。

答案 2 :(得分:0)

在效率方面,不是将字符串中的每个字符本身转换为字符串,而是将其转换为int ...

|> Seq.map (string >> int)

我会将字符直接转换为数字:

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

这样可以减少收集的垃圾,虽然我没有对它进行基准测试,但我希望它会更快。

答案 3 :(得分:-1)

就像Tomas评论的那样,我没有考虑原帖中的数字要求。所以继承我的更正版本:

"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

这与古斯塔沃发布的内容差不多。