首页 文章

程序语言和函数语言之间的区别?

提问于
浏览
-2

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

当您重复调用过程 language(like C)中的函数和函数 language(like haskell)时,使用相同的参数,您可能会得到不同的语言?我在1帖子中读到,纯函数式语言总会产生相同的答案。为什么这样的函数式语言而不是过程语言呢?

3 回答

  • 3

    在命令式编程中,允许函数具有 side-effects,例如修改变量的值,写入文件,访问网络等。第二次运行相同的函数时,它可以检测先前的副作用并返回不同的内容。一个简单的 C 示例是:

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

    多次调用会生成不同的数字。另一个例子:

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

    即使您使用相同的参数调用上述内容,i.e。相同的指针,由于增量,结果会有所不同。

    在纯函数式编程中,设计禁止像i++这样的副作用。这样,函数的输出必须仅依赖于其参数的值。 E.g。如果在 Haskell 中我们有一个Int -> Int类型的函数f,我们可以确定f 3总是会在每次调用时返回相同的整数。

    好吧,上述情况并非完全正确 - 一个程序最终必须做一些 I/O,否则就没有意义。因此,side-effects 必须以某种形式提供。在 Haskell 中,实际上允许带副作用的函数,但它们的类型必须告诉它们不是纯函数。 E.g。如果函数g具有类型Int -> IO Int,则它可以执行 I/O 并且具有 side-effects:运行print =<< g 3可以每次打印不同的值,就像在命令式编程中一样。

    因此,总结一下,在具有副作用的纯函数式编程函数中,与不具有副作用的函数具有不同的类型。通常,在 well-designed 功能程序中,很少需要 I/O 和 side-effects,纯函数构成绝大多数代码。因此,有时我们会说“纯函数编程禁止副作用”,因为大多数代码都是在这种约束条件下编写的。

  • 3

    纯函数式语言的操作使得相同的输入总是产生相同的输出,纯函数没有副作用。但是,在程序性或非纯函数式语言(如 C)中,可能会出现副作用,例如指针被修改,或其他外部因素,如时间或文件 I/O。即使 Haskell 也可以做文件 I/O。因此,如果带有 I/O 的 Haskell 是纯函数式语言,那么C 和 cpp 也是纯粹的功能

    以此 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 中,side-effects 比比皆是。但是,在纯函数式语言中,这样的 side-effects 将不存在或不可能。

  • 1

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

    而在纯函数式语言中,根本没有语言结构允许您修改变量,访问全局可见存储等。因此,有点夸张地说纯 FP 语言“禁止”不纯的函数 - 相反,没有首先写一个的方法。请注意,即使是以下功能:

    printTwice x = do {putStrLn x; putStrLn x}
    

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

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

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

相关问题