【问题标题】:Difference between procedural and functional languages?过程语言和函数语言的区别?
【发布时间】:2015-07-28 18:52:44
【问题描述】:

我最近在一次采访中被问到这个问题。我无法得到正确的答案。

当您在过程语言(如 C)和函数式语言(如 haskell)中重复调用具有相同参数的函数时,在哪种语言中您可能会得到不同的结果?我在 [this] (What is the difference between procedural programming and functional programming?) 上读到纯函数式语言总是会得到相同的答案。为什么函数式语言会这样,而过程式语言则不然?

【问题讨论】:

  • 答案是理论多于实际,但如果您想要一个实际示例,请将共享数据的多线程和竞争条件视为过程范式中可能发生的副作用之一。将设计一个纯函数式编程工具来避免这种情况。您可能会发现此链接很有用:quora.com/Why-does-functional-programming-favor-concurrency
  • 我理解它的方式是函数式语言尝试使用 pure functions 加上它们是声明性的,所以你告诉他们 what 你想做什么,编译器会弄清楚如何和因为它的理念是使用纯函数,所以它这样做没有副作用,而过程语言是必要的,你告诉他们如何做某事,他们只是一步一步地按照说明进行操作,而不在乎是否是纯函数。

标签: c haskell functional-programming procedural


【解决方案1】:

纯函数式语言运行时,相同的输入总是产生相同的输出,纯函数没有副作用。但是,在过程性或非纯函数式语言(例如 C)中,可能会出现副作用,例如指针被修改,或其他外部因素(例如时间或文件 I/O)。甚至 Haskell 也可以进行文件 I/O。因此,如果带 I/O 的 Haskell 是一种纯函数式语言,那么 C and the cpp are purely functional, too

以这个C程序为例:

#ifndef _BSD_SOURCE
#define _BSD_SOURCE
#endif

#include <stdio.h>
#include <time.h>
#include <unistd.h>

int get_num(int n)
{
    usleep(1100000);
    return (time(NULL) - n) % (n / 10);
}

int main(void)
{
    int i;

    for (i = 0; i < 10; i++)
        printf("%d\n", get_num(4242));

    return 0;
}

我们将输入的常量参数4242 传递给get_num。在任意数学之后,正是由于时间因素和睡眠,相同的输入在这种程序语言中不会产生相同的输出。

一次性运行,我们得到:

247
248
249
250
251
253
254
255
256
257

稍后运行,我们得到:

270
271
272
273
274
275
277
278
279
280

在大多数 C 语言中,副作用比比皆是。但是,在纯函数式语言中,这种副作用不会出现或不可能出现。

【讨论】:

    【解决方案2】:

    在命令式编程中,函数是允许有副作用的,比如修改变量的值、写入文件、访问网络等。第二次运行同一个函数,可以检测到之前的副作用并返回不同的东西。一个简单的 C 示例是:

    int i = 0;
    int foo() {
      return i++;
    }
    

    多次调用它会产生不同的数字。再举一个例子:

    int foo(int *p) {
      return (*p)++;
    }
    

    即使你用相同的参数,即相同的指针调用上面的,结果也会因为增量而不同。

    函数式编程中,像i++ 这样的副作用在设计上是被禁止的。这样,函数的输出必须仅取决于其参数的值。例如。如果在 Haskell 中我们有一个 f 类型为 Int -&gt; Int 的函数,我们可以确定 f 3 在每次调用时总是返回相同的整数。

    嗯,以上内容并不完全正确——程序最终必须进行一些 I/O,否则将毫无意义。因此,必须以某种形式提供副作用。在 Haskell 中,实际上允许具有副作用的函数,但它们的类型必须表明这些不是纯函数。例如。如果函数g 的类型为Int -&gt; IO Int,那么它可以执行I/O 并有副作用:运行print =&lt;&lt; g 3 可以每次打印不同的值,就像在命令式编程中一样。

    因此,总而言之,在纯函数式编程中,具有副作用的函数与没有副作用的函数具有不同的类型。通常,在设计良好的函数式程序中,很少需要 I/O 和副作用,而纯函数构成了绝大多数代码。正因为如此,有时我们说“纯函数式编程禁止副作用”,因为大部分代码都是在这个约束下编写的。

    【讨论】:

    • 最佳答案..你用你给出的例子钉牢了。谢谢@chi
    【解决方案3】:

    为了进一步澄清,它并不是一种语言的属性。没有任何命令式语言会强迫您只编写具有副作用的过程。尽管没有明确支持,但也完全可以用纯函数式编写。

    而在函数式语言中,根本没有允许您修改变量、访问全局可见存储等的语言结构。因此说纯 FP 语言“禁止" 不纯函数 - 相反,一开始就没有办法编写一个。请注意,即使是这样的功能:

    printTwice x = do {putStrLn x; putStrLn x}
    

    不纯的。应用 printTwice 会导致 IO 操作。您可以多次这样做,将结果放入列表或元组中并传递它们,这都是纯粹的。具体来说,两者没有区别:

     twoActions = (printTwice "hello", printTwice "hello")
    

     twoActions = (a,a) where a = printTwice "hello"
    

    【讨论】:

    • 需要注意的是,虽然printTwice是一个纯函数,但由于IO类型的构造函数,它可以很容易地做成impure
    猜你喜欢
    • 2013-07-23
    • 2016-11-24
    • 2018-08-12
    • 2011-03-11
    • 2017-09-19
    • 2011-03-29
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多