# 零、简介

我的管理层总是认为我的程序昨天必须是函数式的。加里·惠勒

## 关于这本书

F#不仅仅是另一种编程语言。它主要是为函数式编程而设计的，这既要求也要求对计算机程序有不同的思考方式。函数式编程基于一个非常简单的原则，即行为由输入严格决定，因此相同的输入总是产生相同的行为。函数的行为类似于高中代数中的函数，但可能在包括其他函数和计算在内的多种结构化数据类型下运行，而不仅仅是数字。这个简单的想法对编程实践有许多影响。例如，在命令式语言中，我们很少将函数传递给其他函数，我们经常使用表现出(有意或无意)副作用的可变对象。当使用函数式编程语言时，情况正好相反:函数通常作为参数传递给其他函数，对象是不可变的，除非有意实现，否则没有副作用。

这本书探讨了“如何进行函数式思考”，这与强制性思考非常不同。函数式编程需要考虑计算机程序中类似代数的计算，这些计算是从简单的类似代数的计算中建立起来的。仅仅学习 F#语法是不够的。因此，我将很少花时间讨论 F#语言本身——学习 F#语法的资源有很多，例如 *F#简洁地*[<sup>【1】</sup>](IFP_0010.htm#_ftn1)和 Wikibooks 的 *F#编程*。[<sup>【2】</sup>](IFP_0010.htm#_ftn2)如果你对 F#完全陌生，我强烈建议你先通读那两个资源。

相反，本书将讨论函数式编程概念，例如:

*   携带
*   部分应用
*   不变
*   一流的功能
*   高阶函数
*   功能管道
*   功能组合
*   递归
*   映射、过滤和缩小
*   延续
*   延续传球风格
*   单子(计算表达式)

理解这些概念以及它们如何改变你对编程的思考方式，对于成为一名熟练的函数式程序员至关重要。

即使您没有立即计划使用函数式编程语言，这些概念中的许多正在进入命令式语言，如 C#和 C++。作为一名命令式语言程序员，熟悉这些概念也会加强你的命令式编程技能。

本书分为四章:

*   第 1 章–基本词汇和概念
*   第二章——改变你的思维
*   第 3 章–深入
*   第 4 章–命令/功能交互

目的是以特定的顺序和合理的速度介绍概念，这样，当我们到达[第 3 章](3.html#_Chapter_3_)时，您将很好地理解如何处理 F #-特定的概念。

## 你应该已经知道的

您应该已经接触过 F#或其他一些支持函数式编程的语言，比如 Python、Haskell 或 Clojure，仅举三个例子。一些例子将 F#与 C#进行比较，因此建议掌握大量的 C#知识，尤其是 C# 3.0 及更高版本。

## 使用的资源

本书中的代码示例是用 Visual Studio 2012 编写的，也使用了 Syncfusion Essential Studio 和 Microsoft SQL Server Express。特别是[第 2 章](2.html#_Chapter_2_)和[第 3 章](3.html#_Chapter_3_)中的许多代码示例需要微软的示例数据库 AdventureWorks。AdventureWorks 的正确版本取决于 SQL Server 的版本；请查阅 AdventureWorks 数据库的 Microsoft 文档，以确定数据库的正确版本。如果您没有安装 SQL Server，可以从微软下载免费版的 SQL Server Express 来完成演示。

## 函数式编程的历史

函数式编程的根源在于 lambda 演算，lambda 演算是“数学逻辑和计算机科学中的一种形式系统，用于表达基于函数抽象的计算以及使用变量绑定和替换的应用。”[<sup>【3】</sup>](IFP_0010.htm#_ftn3)λ演算是由数学家阿隆佐·邱奇[<sup>【4】</sup>](IFP_0010.htm#_ftn4)在 20 世纪 30 年代引入的。

就我们的目的而言，lambda 演算的显著特点是它使函数成为一级对象，也就是说它们可以以与正则值或对象相同的方式使用。一级函数的概念在 20 世纪 60 年代由克里斯托弗·斯特雷奇(Christopher Strachey)明确提出，他还创造了“T4”一词，这是函数编程的一个基本概念。顺便说一句，术语 *currying* 有时也被称为*schnfinkling，*以摩西·施芬克尔[<sup>【7】</sup>](IFP_0010.htm#_ftn7)命名，用于起源“……转换一个包含多个参数(或一组参数)的函数的技术，使其可以被称为一个函数链，每个函数只有一个参数……”。[<sup>【8】</sup>](IFP_0010.htm#_ftn8)我们常用 *currying* 这个术语，以哈斯克尔·库里[<sup>【9】</sup>](IFP_0010.htm#_ftn9)命名，他在 Schö nfinkel 论文的基础上进一步发展了组合逻辑的概念。

最早具有功能特征的语言之一是 1958 年由约翰·麦卡锡开发的 Lisp、[<sup>【10】</sup>](IFP_0010.htm#_ftn10)。[<sup>【11】</sup>](IFP_0010.htm#_ftn11)有趣的是，Lisp 开创了许多我们在现代编程语言中认为理所当然的想法:树数据结构、动态类型、条件句和递归等等。

主流语言已经慢慢采用了对函数式编程的支持。Python，[<sup>【12】</sup>](IFP_0010.htm#_ftn12)在 1994 年，增加了对 lambda 表达式和集合操作过滤、映射和减少(也称为“折叠”)的支持。2007 年 C# 3.0 引入了一流的函数。

## c#中的λ表达式

我们看到“lambda expression”这个术语在许多最新的编程语言中使用，包括 C#。[<sup>【13】</sup>](IFP_0010.htm#_ftn13)在 C#中，lambda 表达式是匿名函数，常用来编写 LINQ 查询表达式。例如，C#的集合对象提供选择器函数，您可以使用这些函数来选择集合的特定元素:

```fs
    int[] numbers = { 1, 2, 3, 4, 5 };
    int sumOfOddNumbers = numbers.Sum(x => x % 2 == 1 ? x : 0);
    Console.WriteLine("Sum of odd numbers 1..5: " + sumOfOddNumbers);

```

lambda 表达式:

```fs
    x => x % 2 == 1 ? x : 0

```

如果 x 为奇数，则返回`x`；否则返回 0。

## c#中的函数式编程

如果你曾经使用过 C#的`Action<T>`或`Func<T, TResult>`委托，那么你已经在用函数式编程风格编写了(从技术上讲，如果你曾经使用过委托和事件，那么你已经在做一些函数式编程了)。

例如，您可以将一个方法指定为`Func`(只要它匹配预期的返回类型和参数类型)，并将其用作一个函数来限定另一个函数的操作。在下面的例子中，我们指定了一个方法(一个取`int`并返回`int`的函数)来过滤我们想要求和的数组中的值:

```fs
    static int EvenOrZero(int x)
    {
     return x % 2 == 0 ? x : 0;
    }

    int[] numbers = { 1, 2, 3, 4, 5 };
    int sumOfEvenNumbers = numbers.Sum((Func<int, int>)EvenOrZero);

```

在本例中，强制转换对于解决`Func<int, int>`和`Func<int, int?>`之间的歧义是必要的。

或者，您可以使用 lambda 表达式实例化函数(这消除了强制转换的问题):

```fs
    Func<int, int> evenOrZero = x => x % 2 == 0 ? x : 0;
    int[] numbers = { 1, 2, 3, 4, 5 };
    int sumOfEvenNumbers2 = numbers.Sum(evenOrZero);

```

在这两种情况下，`Action<T>`和`Func<T, TResult>`委托及其变体的使用利用了 C#的委托能力，使函数成为一级公民，由此也可以组成更高阶的函数。

## LINQ 与函数编程

在前面的例子中，我们真正做的是使用 LINQ (Language INtegrated Query)的方法语法，并提供一个谓词或一个 lambda 表达式。您通常会得到 LINQ 语法和 lambda 表达式的混合，所以当使用命令式语言(如 C#和 LINQ 和 lambda 表达式)时，线条会变得模糊。一个纯粹的 LINQ 例子如下:

```fs
    int[] numbers = { 1, 2, 3, 4, 5 };
    int sumOfOdds = (from s in numbers where s % 2 == 1 select s).Sum();

```

事实上:

*   `LambdaExpression`类是`System.Linq.Expressions.LambdaExpressions`命名空间的成员。
*   不包含`System.Linq`就不能使用`Sum`等聚合功能。

如前面的例子所示，在命令式语言(如 C#)中，有几种不同的方法来处理函数式编程。