F#之旅3 - F# PK C#:简单的求和
原文链接:https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/fvsc-sum-of-squares.html
Comparing F# with C#: A simple sum
F# PK C#:简单的求和
To see what some real F# code looks like, let's start with a simple problem: "sum the squares from 1 to N".
We'll compare an F# implementation with a C# implementation. First, the F# code:
// 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
我们以“计算从1到n的平方的和”这个简单问题开始,来看一下实际的F#代码。
我们将比较F#实现和C#实现的不同。首先,F#如下:
// 定义一个求平方的函数
let square x = x * x
// 定义一个求平方和的函数
let sumOfSquares n =
[1..n] |> List.map square |> List.sum
// 试一下效果
sumOfSquares 100
The mysterious looking |> is called the pipe operator. It just pipes the output of one expression into the input of the next. So the code for sumOfSquares reads as:
1、Create a list of 1 to n (square brackets construct a list).
2、Pipe the list into the library function called List.map, transforming the input list into an output list using the "square" function we just defined.
3、Pipe the resulting list of squares into the library function called List.sum. Can you guess what it does?
4、There is no explicit "return" statement. The output of List.sum is the overall result of the function.
|>,这个神秘的东东叫做管道操作符。它做的就是把一个表达式的输出传到另一个表达式的输入。所以,这些代码的意思是:
1、创建一个从1到n的列表(中括号来创建列表)。
2、将列表传(流)到叫做List.map的库函数,使用我们刚刚定义的“平方”函数来进行从输入到输出的转换。
3、将得到的平方值的列表传到叫做List.sum的库函数。你猜下它做了啥?
4、这里没有显式的“返回”说明。List.sum的输出就是这个函数的全部结果输出了。
Next, here's a C# implementation using the classic (non-functional) style of a C-based language. (A more functional version using LINQ is discussed later.)
接下来,是一份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;
}
}
What are the differences?
The F# code is more compact
The F# code didn't have any type declarations
F# can be developed interactively
Let's take each of these in turn.
有什么区别?
F#的代码更简洁
F#的代码没有任何类型声明
F#能交互式的开发
我们来逐条的理解。
Less code
The most obvious difference is that there is a lot more C# code. 13 C# lines compared with 3 F# lines (ignoring comments). The C# code has lots of "noise", things like curly braces, semicolons, etc. And in C# the functions cannot stand alone, but need to be added to some class ("SumOfSquaresHelper"). F# uses whitespace instead of parentheses, needs no line terminator, and the functions can stand alone.
In F# it is common for entire functions to be written on one line, as the "square" function is. The sumOfSquares function could also have been written on one line. In C# this is normally frowned upon as bad practice.
When a function does have multiple lines, F# uses indentation to indicate a block of code, which eliminates the need for braces. (If you have ever used Python, this is the same idea). So the sumOfSquares function could also have been written this way:
let sumOfSquares n =
[1..n]
|> List.map square
|> List.sum
The only drawback is that you have to indent your code carefully. Personally, I think it is worth the trade-off.
更少的代码
很明显,最大的区别是C#的代码要多得多。13行C#代码对应3行F#代码(不包含注释)。C#的代码有很多“杂音”,比如大括号、分号等等。并且C#的函数不能单独的存在,必须放在一个类里面。F#用空格代替了括号的左右,不需要行尾终结符,并且函数可以独立存在。
在F#里,经常可以像“square”函数一样,把整个函数写成一行。“sumOfSquares”函数也可以写成一行。在F#里,这样做就是不被认可的坏习惯了。
当一个函数有多行时,F#用缩进来凸出一个代码块,而不需要额外的大括号。(如果你用过python,会发现这点和python一样)。因此,“sumOfSquares”函数也可以写成这样:
let sumOfSquares n =
[1..n]
|> List.map square
|> List.sum
唯一的缺点是需要小心你的缩进。个人认为,这样也挺好的。
No type declarations
The next difference is that the C# code has to explicitly declare all the types used. For example, the int i parameter and int SumOfSquares return type. Yes, C# does allow you to use the "var" keyword in many places, but not for parameters and return types of functions.
In the F# code we didn't declare any types at all. This is an important point: F# looks like an untyped language, but it is actually just as type-safe as C#, in fact, even more so! F# uses a technique called "type inference" to infer the types you are using from their context. It works amazingly very well most of the time, and reduces the code complexity immensely.
In this case, the type inference algorithm notes that we started with a list of integers. That in turn implies that the square function and the sum function must be taking ints as well, and that the final value must be an int. You can see what the inferred types are by looking at the result of the compilation in the interactive window. You'll see something like:
val square : int -> int
which means that the "square" function takes an int and returns an int.
没有类型声明
第二个区别,是C#代码显示的声明了所有用到的类型。例如,参数i是int类型,函数SumOfSquares的返回类型也是int。是的,C#也允许在很多地方用“var”关键字类自动推导类型,但是函数参数和函数返回值用不了。
在F#代码里,我们不需要声明所有类型。有一点很重要:F#看起来像是无类型(弱类型)语言,但它确实和C#一样是类型安全的,千真万确!F#用了一种叫做“类型推导”的技术来从上下文推断出类型。大部分时候它工作得出奇的好,极大的减少了代码的复杂性。
在这里,类型推导算法记录到:从一个整型列表开始。那么,又意味着square函数和sum函数必须能接受整型数值作为参数,并且得到的结果也要是整型。如果在交互窗口写代码,你可以直接在窗口里看到推导出来的类型。你会看到类似这样的东西:
val square : int -> int
意思是square函数接受一个整型,并且返回一个整型。
If the original list had used floats instead, the type inference system would have deduced that the square function used floats instead. Try it and see:
// 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
The type checking is very strict! If you try using a list of floats ([1.0..n]) in the original sumOfSquares example, or a list of ints ([1 ..n]) in the sumOfSquaresF example, you will get a type error from the compiler.
如果初始列表里用浮点型代替整型,类型推导系统也能推导出:square用浮点型输入输出。
...
类型检查是非常严格的!如果你尝试输入浮点型列表到之前的sumOfSquares,或者将整型的列表输入到sumOfSquaresF,编译器都会给你报类型错误。
Interactive development
Finally, F# has an interactive window where you can test the code immediately and play around with it. In C# there is no easy way to do this.
For example, I can write my square function and immediately test it:
// define the square function
let square x = x * x
// test
let s2 = square 2
let s3 = square 3
let s4 = square 4
When I am satisfied that it works, I can move on to the next bit of code.
This kind of interactivity encourages an incremental approach to coding that can become addictive!
Furthermore, many people claim that designing code interactively enforces good design practices such as decoupling and explicit dependencies, and therefore, code that is suitable for interactive evaluation will also be code that is easy to test. Conversely, code that cannot be tested interactively will probably be hard to test as well.
交互式开发
最后一点,F#还有一个交互式窗口,可以用来直接输入代码经常测试。C#不容易做到这点。
举个例子,我可以直接写一个square函数并且测试它:
...
当我觉得这个函数让我满意时,我再把它移到别的代码里用。
这样的交互式,让我们可以用增量的方式来写代码,让人上瘾!(我是没感觉的)
此外,很多人声称,把代码设计成可以交互式执行,是一个良好的设计实践,能解耦和明确依赖关系,因此,适合于交互式评审的代码也将是容易测试的代码。反之,无法进行交互测试的代码也会很难测试。
The C# code revisited
My original example was written using "old-style" C#. C# has incorporated a lot of functional features, and it is possible to rewrite the example in a more compact way using the LINQ extensions.
So here is another C# version -- a line-for-line translation of the F# code.
public static class FunctionalSumOfSquaresHelper
{
public static int SumOfSquares(int n)
{
return Enumerable.Range(1, n)
.Select(i => i * i)
.Sum();
}
}
However, in addition to the noise of the curly braces and periods and semicolons, the C# version needs to declare the parameter and return types, unlike the F# version.
Many C# developers may find this a trivial example, but still resort back to loops when the logic becomes more complicated. In F# though, you will almost never see explicit loops like this. See for example, this post on eliminating boilerplate from more complicated loops.
C#代码重写
最初的例子是用“远古风格”的C#写的。C#也包含了很多函数式特性,能用LINQ扩展来把代码重写得更简洁。
下面是另一个C#写的版本,从F#的代码一行行的翻译过来的。
...
然而,大括号和分号之类的“杂音”还在,参数和返回值的类型声明也少不了,还是不像F#版本那么简洁。
一些C#开发可能觉得这个例子不值一提,在遇到复杂的逻辑时,仍然靠循环来做。再看一个例子吧,教你解脱:http://fsharpforfunandprofit.com/posts/conciseness-extracting-boilerplate/
F#之旅3 - F# PK C#:简单的求和的更多相关文章
- F#之旅4 - 小实践之快排
参考文章:https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/fvsc-quicksort.html F#之旅4 - 小 ...
- F# 之旅(上)
写在前面的话 解答一下在上一篇文章<在Visual Studio中入门F#>中有人的提问, 1. 问:是准备写 F# 系列吗? 答:当然不是,本人也是刚刚学习 F#,只是翻译微软官方 ...
- F# 之旅(下)
写在前面的话 学习 F# 一定要去体会函数式编程的特点,推荐一下阮一峰的日志<函数式编程入门教程>. 在这篇文章中 递归函数 记录和可区分联合类型 模式匹配 可选类型 度量单位 类和接口 ...
- F#之旅0 - 开端
F#之旅0 - 开端 UWP的学习告一段落,CozyRSS的UWP版本并没有做.UWP跟wpf开发几乎一模一样,然后又引入了很多针对移动设备的东西,这部分有点像android.没啥太大的意思,不难,估 ...
- Geometric regularity criterion for NSE: the cross product of velocity and vorticity 3: $u\times \f{\om}{|\om|}\cdot \f{\vLm^\be u}{|\vLm^\be u|}$
在 [Chae, Dongho; Lee, Jihoon. On the geometric regularity conditions for the 3D Navier-Stokes equati ...
- HDU 1005 Number Sequence【斐波那契数列/循环节找规律/矩阵快速幂/求(A * f(n - 1) + B * f(n - 2)) mod 7】
Number Sequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)T ...
- 函数$f(x+1)$和$f(x-1)$的奇偶性
前言 廓清认知 1.函数\(y=f(x)\)的奇偶性 ①\(y=f(x)\)为奇函数,则满足\(f(-x)+f(x)=0\),即关于点\((0,0)\)对称: ②\(y=f(x)\)为偶函数,则满足\ ...
- (打表+优化)简单的求和 -- zzuli -- 1783
http://acm.zzuli.edu.cn/problem.php?id=1783 1783: 简单的求和 Time Limit: 1 Sec Memory Limit: 128 MBSubmi ...
- zzuli1783: 简单的求和---求因子和
1783: 简单的求和 Description 定义f(i)代表i的所有因子和(包括1和i),给定一个l,r.求f(l)+f(l+1)+...+f(r). Input 第一行输入一个t(t<10 ...
随机推荐
- 条件编译#if
1.为什么需要条件编译 客户的需求在不停地发生变化,一会儿需要这个功能,一会儿不需要这个功能.我们可以使用条件编译来方便地裁剪功能. 2.条件编译语句#if 条件编译语句#if的形式是 #if exp ...
- leetcode--Majority Element
题目链接:https://leetcode.com/problems/majority-element/ 算法类型:分治法 题目分析:获取长度为n的数组中的众数(出现次数大于等于⌊ n/2 ⌋) 代码 ...
- 用Model-View-ViewModel构建iOS App
如果你已经开发一段时间的iOS应用,你一定听说过Model-View-Controller,即MVC.MVC是构建iOS App的标准模式.然而,最近我已经越来越厌倦MVC的一些缺点.在本文,我将重温 ...
- 关于es5的一些新方法
1.数组方法(1)isArray在之前我们判断数组类型的数据都是用instanceof来判断的,es5新增了对数组的判断,即Array.isArray()(2)every和some这两个方法一般用于对 ...
- Google的Protobuf协议分析
protobuf和thrift类似,也是一个序列化的协议实现,简称PB(下文出现的PB代表protobuf). Github:https://github.com/google/protobuf 上图 ...
- linux vi(vim)常用命令汇总
1 查找 /xxx(?xxx) 表示在整篇文档中搜索匹配xxx的字符串, / 表示向下查找, ? 表示向上查找其中xxx可以是正规表达式,关于正规式就不多说了. 一般来说是区分大小写的, 要想不区分大 ...
- 夏夏的php开发笔记开写啦
主要写一些平时drupal.dedecms.帝国cms.wordpress等php程序开发过程中遇到的问题,以及解决的过程,记录夏夏的成长,把握契机,创造未来
- 兼容8事件绑定与解绑addEventListener、removeEventListener和ie的attachEvent、detachEvent
兼容8事件绑定与解绑addEventListener.removeEventListener和ie的attachEvent.detachEvent ;(function(){ // 事件绑定 bi ...
- 取两个String数组的交集
import org.testng.annotations.Test; import java.util.HashMap; import java.util.LinkedList; import ja ...
- CentOS6.3 重启后/etc/resolv.conf 被还原解决办法(转)
今天一台服务器上不了网,设置了nameserver,重启后/etc/resolv.conf文件就被自动还原了,最后发现是被Network Manager修改了.解决方法:停止Network Manag ...