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


当前回答

如果你有机会,我建议你买一份Lisp/Scheme,然后用它来做一些项目。最近流行起来的大多数思想都是在几十年前用Lisp表达的:函数式编程、延续(作为闭包)、垃圾收集,甚至XML。

所以这将是一个很好的方法来开始所有这些当前的想法,以及一些其他的,比如符号计算。

您应该知道函数式编程擅长什么,不擅长什么。它并不是什么都好。有些问题最好用副作用来表达,同样的问题会根据提问的时间给出不同的答案。

其他回答

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

如果你有机会,我建议你买一份Lisp/Scheme,然后用它来做一些项目。最近流行起来的大多数思想都是在几十年前用Lisp表达的:函数式编程、延续(作为闭包)、垃圾收集,甚至XML。

所以这将是一个很好的方法来开始所有这些当前的想法,以及一些其他的,比如符号计算。

您应该知道函数式编程擅长什么,不擅长什么。它并不是什么都好。有些问题最好用副作用来表达,同样的问题会根据提问的时间给出不同的答案。

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

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

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

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

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

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

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

Funtional编程

num = 1 
def function_to_add_one(num):
    num += 1
    return num


function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)

#Final Output: 2

过程式编程

num = 1 
def procedure_to_add_one():
    global num
    num += 1
    return num


procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()

#Final Output: 6

Function_to_add_one是一个函数

Procedure_to_add_one是一个过程

即使你运行这个函数5次,每次它都会返回2

如果你运行这个过程五次,在第五次运行结束时,它会给你6。

免责声明:显然,这是对现实的一种超简化的看法。这个答案只是让我们了解了“函数”而不是“过程”。仅此而已。一旦你尝到了这种肤浅而深刻的直觉,开始探索这两种范式,你就会开始清楚地看到它们的区别。

对我的学生有帮助,希望对你们也有帮助。