Comparing F# with C#: A simple sum
https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/fvsc-sum-of-squares.html
F# 与 C# 的比较:一个简单的求和
要了解一些真正的 F# 代码是什么样的,让我们从一个简单的问题开始:“对 1 到 N 的平方求和”。
我们将比较一个F#实现和一个C#实现。首先是F#的代码:
// define the square function
let square x = x * x
// define the sumOfSquares function
let sumOfSquares n =
[1..n] |> List.map square |> List.sum
// try it
sumOfSquares 100这个看起来很神秘的|>被称为管道操作符。它只是把一个表达式的输出连接到下一个表达式的输入。所以sumOfSquares的代码是这样工作的:
创建一个 1 到 n 的列表(方括号构成一个列表)。
将该列表输送到名为
List.map的库函数中,使用我们刚刚定义的 "square"函数将输入列表转换为输出列表。将生成的正方形列表通过管道传输到名为
List.sum的库函数中。你能猜出它的作用吗?没有明确的“返回”声明。 List.sum 的输出是函数的整体结果。
接下来,这是一个使用基于C语言的经典(非函数式)风格的C#实现。(后面将讨论使用LINQ的更多功能版本)。
public static class SumOfSquaresHelper
{
public static int Square(int i)
{
return i * i;
}
public static int SumOfSquares(int n)
{
int sum = 0;
for (int i = 1; i <= n; i++)
{
sum += Square(i);
}
return sum;
}
}有什么区别?
F#代码更紧凑
F# 代码没有任何类型声明
F#可以交互式开发
让我们依次来看这些。
更少的代码
最明显的区别是,C#代码多了很多。13行C#代码,而F#代码只有3行(忽略注释)。C#代码有很多 "噪音",比如大括号、分号等等。而且在C#中,这些函数不能独立存在,而是需要添加到一些类中("SumOfSquaresHelper")。F#使用空格代替小括号,不需要行结束符,而且函数可以独立存在。
在 F# 中,将整个函数写在一行中是很常见的,就像“square”函数一样。 sumOfSquares 函数也可以写在一行中。在 C# 中,这通常被认为是不好的做法。
当函数确实有多行时,F# 使用缩进来指示代码块,这样就不需要大括号了。 (如果你曾经使用过 Python,这是相同的想法)。所以 sumOfSquares 函数也可以这样写:
let sumOfSquares n =
[1..n]
|> List.map square
|> List.sum唯一的缺点是您必须小心地缩进代码。就个人而言,我认为值得trade-off的.
没有类型声明
下一个区别是 C# 代码必须显式声明所有使用的类型。例如,int i 参数和 int SumOfSquares 返回类型。是的,C# 确实允许您在许多地方使用“var”关键字,但不能用于函数的参数和返回类型。
在 F# 代码中,我们根本没有声明任何类型。这是很重要的一点:F# 看起来像一种无类型语言,但实际上它与 C# 一样类型安全,事实上,甚至更安全! F# 使用一种称为“类型推断”的技术从上下文中推断您正在使用的类型。它在大多数时候都工作得非常好,并且极大地降低了代码的复杂性。
在这种情况下,类型推断算法注意到我们从一个整数列表开始。这反过来意味着平方函数和求和函数也必须采用整数,并且最终值必须是整数。您可以通过在交互窗口中查看编译结果来了解推断的类型。你会看到类似的东西:
val square : int -> int这意味着“square”函数接受一个 int 并返回一个 int。
如果原始列表使用浮点数代替,则类型推断系统会推断 square 函数使用浮点数代替。试试看:
// define the square function
let squareF x = x * x
// define the sumOfSquares function
let sumOfSquaresF n =
[1.0 .. n] |> List.map squareF |> List.sum // "1.0" is a float
sumOfSquaresF 100.0类型检查非常严格!如果您尝试在原始 sumOfSquares 示例中使用浮点列表 ([1.0..n]),或在 sumOfSquaresF 示例中使用整数列表 ([1..n]),您将从编译器中得到类型错误。
交互开发
最后,F# 有一个交互式窗口,您可以在其中立即测试代码并试用它。在 C# 中,没有简单的方法可以做到这一点。
例如,我可以编写我的 square 函数并立即对其进行测试:
// define the square function
let square x = x * x
// test
let s2 = square 2
let s3 = square 3
let s4 = square 4当我对它的效果满意时,我可以继续写下一段代码。
这种交互性鼓励一种渐进的编码方法,这种方法可能会让人上瘾!
此外,许多人声称,交互式地设计代码可以强制执行良好的设计实践,如解耦和明确的依赖关系,因此,适合交互式评估的代码也将是容易测试的代码。反之,不能进行交互式测试的代码可能也会很难测试。
重新审视 C# 代码
我最初的例子是用 "老式 "C#写的。C#已经融入了很多功能特性,使用LINQ扩展可以以更紧凑的方式重写这个例子。
所以这是另一个 C# 版本 —— F# 代码的逐行翻译。
public static class FunctionalSumOfSquaresHelper
{
public static int SumOfSquares(int n)
{
return Enumerable.Range(1, n)
.Select(i => i * i)
.Sum();
}
}但是,除了花括号和句点和分号的干扰之外,C# 版本还需要声明参数和返回类型,这与 F# 版本不同。
许多C#开发者可能会发现这是一个微不足道的例子,但当逻辑变得更加复杂时,他们还是会求助于循环。但在F#中,你几乎不会看到像这样的显式循环。例如,请看这篇关于从更复杂的循环中消除模板的文章。
Last updated