我已经阅读了维基百科上关于过程式编程和函数式编程的文章,但我还是有点困惑。有人能把它归结为核心吗?
当前回答
函数式编程与不使用全局变量的过程式编程相同。
其他回答
基本上这两种风格,就像阴阳。一个是有组织的,而另一个是混乱的。在某些情况下,函数式编程是显而易见的选择,而在其他情况下,过程式编程是更好的选择。这就是为什么至少有两种语言最近推出了新版本,包含了这两种编程风格。(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)
函数式编程与不使用全局变量的过程式编程相同。
进一步阐述康拉德的评论:
求值的顺序不是 定义良好的
一些函数式语言有所谓的惰性求值。这意味着直到需要该值时才执行函数。在此之前,传递的是函数本身。
过程式语言是步骤1、步骤2、步骤3……如果在第二步你说加2 + 2,它马上就会做。在惰性求值中,你会说2 + 2,但如果结果从未被使用,它就永远不会做加法。
@Creighton:
在Haskell中有一个叫做product的库函数:
prouduct list = foldr 1 (*) list
或者仅仅是:
product = foldr 1 (*)
惯用语的阶乘
fac n = foldr 1 (*) [1..n]
很简单
fac n = product [1..n]
我在这里没有看到真正强调的一点是,现代函数语言(如Haskell)实际上更多地关注流控制的第一类函数,而不是显式递归。您不需要像上面那样在Haskell中递归地定义阶乘。我想是这样的
fac n = foldr (*) 1 [1..n]
是一个完美的惯用结构,在精神上更接近于使用循环,而不是使用显式递归。