函数式编程的目标

时间:2014-04-23 16:18:45

标签: functional-programming immutability

我一直在想这个,但我需要一个确认:函数式编程的目的是使编写的程序成为一个数学证明。一旦程序被正确编写,它就会被“证明”,并且在任何环境中都可以使用相同的程序。这就是为什么在FP中需要不可变的原因,否则就无法保证“证据”的正确性,因为状态变化会引入意想不到的行为。

我在功能编程的基础上是否正确?

1 个答案:

答案 0 :(得分:1)

你离基地不远,但我不会那么说。我认为函数式编程的主要目标是允许程序员更容易地推理他们的代码。我们并不总是能够有正确的正确性,事实上,大多数功能性程序员都没有为他们的程序构建一个。但在我看来,功能性程序仍然更容易推理,而这就是目标。但是如何以及为什么?

静态与动态

静态概念与代码相关,因为它在您在文件中读取时的布局。动态概念与数据如何从一个地方流向另一个地方有关。人们认为静态比动态更好。这个论点是Dijkstra在他的研究论文Go To Statement Considered Harmful中提出的。如果你需要跟踪代码指针如何动态流动,那么很难理解代码。您需要基本上完成代码的执行。这是一个要求至少代码指针以更静态的方式运行的论据。因此,大多数编程语言不实现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]所以我将评估"总计[1,2,3]"得到" 6"。看起来不对,但是大小[1,2,3]"是" 4"。啊!我知道我的bug是在size函数中。所以我会遵循这一点,最终得到" 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;
}

我在这里遇到了与上次相同的错误但现在如何将此问题减少到更简单的问题来调试我的代码?声明&#34; size = size + 1&#34;是我的程序的一个子陈述,但在其上下文之外没有任何意义。我无法确保我的程序的子语句本身正常运行。我需要仔细查看代码,可能使用调试器并跟踪本地状态以观察它的变化。这是一个非常动态的过程。人类静态地想得更好。

<强>可测

功能编程也更容易进行单元测试。以我的平均功能为例。我提到的所有子表达式都像&#34; size [] == 0&#34;和&#34;大小[1,2,3] == 3&#34;会做出完美的单元测试。特别是如果他们是我知道我以前犯过错误的东西!

想象一下,如果我尝试重构此程序或使其更有效。能够自动测试大量较小的部件会很不错。在命令式编程中肯定可以进行单元测试,人们总是这样做,但是如果你有一个接收数据的函数,返回数据,并且总是以相同的方式运行,那么单元测试就没有比这更容易了。

可以使用函数式编程

由于这些原因,我认为即使是命令式语言,人们也应该尽可能多地编写代码,以便更容易测试。没有理由对它做黑与白。它是一种工具,而不是宗教。半途中进行函数式编程可以大大提高代码的可测试性和可维护性。

进一步阅读

关于这个主题的一篇伟大的论文,实际上我在回答你的问题时得到了很多灵感,是约翰·赫格斯Why Functional Programming Matters。此外,如果您想要完成为初学者程序员设计的整个课程,我强烈推荐How to Design Programs。它真正专注于通过单元测试使程序编码更容易,并将程序划分为小的,可理解的子任务。在这样做的过程中,我觉得它突出了功能编程如此强大的原因。