我已经阅读了维基百科上关于过程式编程和函数式编程的文章,但我还是有点困惑。有人能把它归结为核心吗?


当前回答

基本上这两种风格,就像阴阳。一个是有组织的,而另一个是混乱的。在某些情况下,函数式编程是显而易见的选择,而在其他情况下,过程式编程是更好的选择。这就是为什么至少有两种语言最近推出了新版本,包含了这两种编程风格。(Perl 6和d2)

#程序:#

例程的输出并不总是与输入直接相关。 每件事都有特定的顺序。 例程的执行可能有副作用。 倾向于强调以线性方式实现解决方案。

##Perl 6 ##

sub factorial ( UInt:D $n is copy ) returns UInt {

  # modify "outside" state
  state $call-count++;
  # in this case it is rather pointless as
  # it can't even be accessed from outside

  my $result = 1;

  loop ( ; $n > 0 ; $n-- ){

    $result *= $n;

  }

  return $result;
}

2 # # # # D

int factorial( int n ){

  int result = 1;

  for( ; n > 0 ; n-- ){
    result *= n;
  }

  return result;
}

#功能:#

通常递归。 对于给定的输入总是返回相同的输出。 计算的顺序通常是不确定的。 必须是无状态的。即任何手术都不能有副作用。 很适合并行执行 倾向于强调分而治之的方法。 可具有惰性求值的特性。

哈斯克尔# # # # (摘自维基百科);

fac :: Integer -> Integer

fac 0 = 1
fac n | n > 0 = n * fac (n-1)

或者在一行中:

fac n = if n > 0 then n * fac (n-1) else 1

##Perl 6 ##

proto sub factorial ( UInt:D $n ) returns UInt {*}

multi sub factorial (  0 ) { 1 }
multi sub factorial ( $n ) { $n * samewith $n-1 } # { $n * factorial $n-1 }

2 # # # # D

pure int factorial( invariant int n ){
  if( n <= 1 ){
    return 1;
  }else{
    return n * factorial( n-1 );
  }
}

#注:#

阶乘实际上是一个常见的示例,它展示了在Perl 6中创建新的操作符有多么容易,就像创建子例程一样。这个特性在Perl 6中根深蒂固,以至于Rakudo实现中的大多数操作符都是以这种方式定义的。它还允许您将自己的多个候选操作符添加到现有操作符。

sub postfix:< ! > ( UInt:D $n --> UInt )
  is tighter(&infix:<*>)
  { [*] 2 .. $n }

say 5!; # 120␤

这个例子还展示了范围创建(2..$n)和列表缩减元操作符([OPERATOR] list)与数字中缀乘法操作符的结合。(*) 它还表明,您可以在签名中放入——> UInt,而不是在签名后返回UInt。

(你可以用2开始范围,因为乘法“运算符”在不带任何参数的情况下调用时将返回1)

其他回答

在计算机科学中,函数式编程是一种编程范式,它将计算视为数学函数的求值,并避免状态和可变数据。它强调函数的应用,与强调状态变化的过程式编程风格相反。

我从来没有在其他地方看到过这样的定义,但我认为这很好地总结了这里给出的差异:

函数式编程主要关注表达式

过程式编程主要关注语句

表达式有值。函数式程序是一个表达式,其值是由计算机执行的一系列指令。

语句没有值,而是修改一些概念机器的状态。

在纯函数式语言中,没有语句,也就是说没有办法操纵状态(它们可能仍然有一个名为“语句”的语法结构,但除非它操纵状态,否则我不会在这种意义上称其为语句)。在纯程序语言中,没有表达式,一切都是操纵机器状态的指令。

Haskell是纯函数式语言的一个例子,因为没有办法操纵状态。机器代码是纯过程语言的一个例子,因为程序中的所有内容都是操作机器寄存器和内存状态的语句。

令人困惑的部分是,绝大多数编程语言同时包含表达式和语句,允许您混合使用范式。语言可以根据它们鼓励使用语句和表达式的程度被分类为更函数化或更过程化。

For example, C would be more functional than COBOL because a function call is an expression, whereas calling a sub program in COBOL is a statement (that manipulates the state of shared variables and doesn't return a value). Python would be more functional than C because it allows you to express conditional logic as an expression using short circuit evaluation (test && path1 || path2 as opposed to if statements). Scheme would be more functional than Python because everything in scheme is an expression.

你仍然可以在一种鼓励过程范式的语言中以函数式风格编写,反之亦然。只是在语言不鼓励的范式下写作更困难和/或更尴尬。

函数式编程与不使用全局变量的过程式编程相同。

进一步阐述康拉德的评论:

因此,纯函数式程序总是对输入产生相同的值,求值的顺序没有很好的定义;

因此,函数式代码通常更容易并行化。由于函数(通常)没有副作用,并且它们(通常)只是作用于它们的参数,因此许多并发问题都消失了。

当您需要能够证明您的代码是正确的时,也可以使用函数式编程。这在过程式编程中要困难得多(在函数式编程中不容易,但仍然容易)。

免责声明:我已经很多年没有使用函数式编程了,直到最近才开始重新研究它,所以我在这里可能不完全正确。:)

要理解其中的区别,需要理解过程式编程和函数式编程的“教父”范式都是命令式编程。

基本上,过程式编程只是构造命令式程序的一种方式,其中主要的抽象方法是“过程”。(或某些编程语言中的“函数”)。甚至面向对象编程也只是构造命令式程序的另一种方式,其中状态被封装在对象中,成为一个具有“当前状态”的对象,加上这个对象有一组函数、方法和其他东西,可以让程序员操作或更新状态。

现在,关于函数式编程,其方法的要点是它确定要取什么值,以及应该如何传递这些值。(因此没有状态,也没有可变数据,因为它将函数作为第一类值,并将它们作为参数传递给其他函数)。

PS:理解所使用的每一种编程范式应该能澄清它们之间的差异。

PSS:归根结底,编程范式只是解决问题的不同方法。

PSS: quora上的这个答案有一个很好的解释。