Functional programming 函数式编程的目标

Functional programming 函数式编程的目标,functional-programming,immutability,Functional Programming,Immutability,我一直这么想,但我需要一个确认:函数式编程的目的是使编写的程序成为数学证明。一旦一个程序被正确地编写,它就被“证明”了,并且在任何环境中都能正常工作。这就是为什么在FP中需要immutable,否则无法保证“证明”的正确性,因为状态更改可能会引入意外行为 基于函数式编程,我说得对吗?你说得离基础不远,但我不会这么说。我认为函数式编程的主要目标是让程序员更容易地对他们的代码进行推理。我们并不总是能够有一个正式的正确性证明,事实上,大多数函数式程序员并不为他们的程序构造一个。但在我看来,功能程序仍然

我一直这么想,但我需要一个确认:函数式编程的目的是使编写的程序成为数学证明。一旦一个程序被正确地编写,它就被“证明”了,并且在任何环境中都能正常工作。这就是为什么在FP中需要immutable,否则无法保证“证明”的正确性,因为状态更改可能会引入意外行为


基于函数式编程,我说得对吗?

你说得离基础不远,但我不会这么说。我认为函数式编程的主要目标是让程序员更容易地对他们的代码进行推理。我们并不总是能够有一个正式的正确性证明,事实上,大多数函数式程序员并不为他们的程序构造一个。但在我看来,功能程序仍然更容易推理,这就是目标。但如何以及为什么

静态vs.动态

静态概念与代码相关,因为您在文件中读取代码时,代码是按布局的。动态概念与数据如何通过程序从一个地方流向另一个地方有关。人们静态思考要比动态思考好得多。Dijkstra在他的seminole论文中提出了这一论点。其理由是,如果需要跟踪代码指针如何动态流动,则很难理解代码。您基本上需要遍历代码的执行过程。这是一个要求至少代码指针以更静态的方式运行的参数。因此,大多数编程语言不实现GOTO,而是允许IF、FOR和WHILE等。这称为结构化编程。这些比较容易推理,因为它们比较静态

函数式程序员的观点是,函数式程序更受限制,更静态,因此更容易推理。在函数式程序中,代码是一个表达式,其计算结果为一个值,而不是一系列语句,这些语句会发生变化,因此需要在头脑中保持跟踪,以便了解它发生了什么,最终会变成什么

我并不是说函数式编程让这变得微不足道,但论点是它更容易。让我们在一个小例子中尝试一下。表达式由子表达式组成,可以对所述子表达式求值,以便在理解整个表达式的值之前先理解其值。让我们试着用一个带有bug的简单函数程序来实现这一点。下面是一些Haskell代码

size l =
    if null l
    then 1
    else 1 + size (tail l)

total l =
    if null l
    then 0
    else (head l) + total (tail l)

average l = total l / size l
假设我评估“平均[1,2,3]”,得到“1.5”。那是不对的!我能做什么?油井“平均[1,2,3]”降至“1.5”。如果我把平均数的定义分解成两个表达式,组成它,“总l”和“大小l”。我知道参数l是[1,2,3],所以我将计算“total[1,2,3]”,得到“6”。这看起来不错,但“大小[1,2,3]”是“4”。啊!!我知道我的错误在大小函数中。因此,我会继续往下看,以“size[]==1”结束,并意识到我的基本情况是不正确的。我应该写“那么0”

相反,这种非常有用的推理策略在命令式编程中不起作用。原因是不能从正在修改状态的进程中提取子语句,以便孤立地理解它。一句话在它所处的环境之外是没有意义的。让我们来看一个具有等价bug的C程序。
int average (int *l) {
    int total = 0;
    int size = 1;
    int i;
    for (i = 0; i < sizeof(l); i++) {
        total = total + l[i];
        size = size + 1;
    }
    return total / size;
}
int平均值(int*l){
int-total=0;
int size=1;
int i;
对于(i=0;i
我这里有和上次一样的bug,但是现在我如何将它简化为一个更简单的问题来调试我的代码呢?语句“size=size+1”是我的程序的一个子语句,但它在上下文之外没有任何意义。我无法确保我的程序的这个子语句本身能够正常运行。我需要仔细地浏览代码,可能会使用调试器并跟踪本地状态以观察其变化。这是一个非常动态的过程。人类静态思维要好得多

可测试性

函数式编程也更容易进行单元测试。以我以前的平均函数为例。我提到的所有子表达式,如“size[]==0”和“size[1,2,3]==3”,都是完美的单元测试。尤其是我知道我以前犯过错误的事情

想象一下,如果我试图重构这个程序或使它更高效。有很多小部件自动测试会很好。在命令式编程中,单元测试当然是可能的,人们总是这样做,但是如果你有一个函数,它接收数据,返回数据,并且总是以相同的方式运行,那么没有什么比这更容易进行单元测试了

尽可能使用函数式编程

出于这些原因,我认为即使使用命令式语言的人也应该以函数式的方式编写尽可能多的代码,以使其更易于测试。没有理由对这件事三缄其口。这是一种工具,不是宗教。中途进行函数式编程可以极大地提高代码的可测试性和可维护性

进一步阅读


关于这个话题的一篇伟大的论文,也是我在回答你的问题时得到很多启发的一篇,是约翰·雨格斯写的。另外,如果你想学习为初学者设计的全部课程,我强烈推荐你。它真正关注的是通过单元测试和将程序划分为小的、可理解的子任务来简化编写代码。通过这样做,我觉得它突出了函数式编程的强大功能。

我认为它的目的是编写有用的程序。不是真的。更多