我已经阅读了维基百科上关于过程式编程和函数式编程的文章,但我还是有点困惑。有人能把它归结为核心吗?
当前回答
函数式语言(理想情况下)允许您编写一个数学函数,即接受n个参数并返回一个值的函数。如果程序被执行,这个函数将根据需要在逻辑上求值
另一方面,过程式语言执行一系列连续的步骤。(有一种将顺序逻辑转换为函数逻辑的方法,称为连续传递样式。)
因此,纯函数式程序总是对输入产生相同的值,求值的顺序没有很好的定义;这意味着像用户输入或随机值这样的不确定值很难用纯函数式语言建模。
就像这个答案中的其他内容一样,这是一种概括。这个属性,在需要计算结果的时候计算,而不是在调用它的时候按顺序计算,被称为“懒惰”。并不是所有的函数式语言都是懒惰的,懒惰也不仅仅局限于函数式编程。相反,这里给出的描述提供了一个“心理框架”,用于思考不同的编程风格,这些风格不是不同的、相反的类别,而是流动的想法。
其他回答
康拉德说:
因此,纯函数式程序总是为输入产生相同的值, 评价的顺序也不明确;这意味着不确定的值,比如 用户输入或随机值很难用纯函数式语言建模。
在一个纯函数式程序中求值的顺序可能很难(或者)解释(尤其是懒惰的人),甚至不重要,但我认为说它没有被很好地定义,听起来就像你根本无法判断你的程序是否会工作!
Perhaps a better explanation would be that control flow in functional programs is based on when the value of a function's arguments are needed. The Good Thing about this that in well written programs, state becomes explicit: each function lists its inputs as parameters instead of arbitrarily munging global state. So on some level, it is easier to reason about order of evaluation with respect to one function at a time. Each function can ignore the rest of the universe and focus on what it needs to do. When combined, functions are guaranteed to work the same[1] as they would in isolation.
... 像用户输入或随机值这样的不确定值很难纯粹地建模 函数式语言。
The solution to the input problem in purely functional programs is to embed an imperative language as a DSL using a sufficiently powerful abstraction. In imperative (or non-pure functional) languages this is not needed because you can "cheat" and pass state implicitly and order of evaluation is explicit (whether you like it or not). Because of this "cheating" and forced evaluation of all parameters to every function, in imperative languages 1) you lose the ability to create your own control flow mechanisms (without macros), 2) code isn't inherently thread safe and/or parallelizable by default, 3) and implementing something like undo (time travel) takes careful work (imperative programmer must store a recipe for getting the old value(s) back!), whereas pure functional programming buys you all these things—and a few more I may have forgotten—"for free".
我希望这听起来不像狂热,我只是想补充一些观点。命令式编程,特别是像c# 3.0这样的强大语言中的混合范式编程,仍然是完成工作的完全有效的方法,并且没有银弹。
[1]…除了内存使用方面(参考Haskell中的foldl和foldl')。
基本上这两种风格,就像阴阳。一个是有组织的,而另一个是混乱的。在某些情况下,函数式编程是显而易见的选择,而在其他情况下,过程式编程是更好的选择。这就是为什么至少有两种语言最近推出了新版本,包含了这两种编程风格。(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)
过程性语言倾向于跟踪状态(使用变量),并倾向于按步骤序列执行。纯函数式语言不跟踪状态,使用不可变值,并倾向于作为一系列依赖项执行。在许多情况下,调用堆栈的状态所保存的信息与过程代码中存储在状态变量中的信息相同。
递归是函数式编程的一个经典例子。
我相信过程式/函数式/目标式编程是关于如何处理问题的。
The first style would plan everything in to steps, and solves the problem by implementing one step (a procedure) at a time. On the other hand, functional programming would emphasize the divide-and-conquer approach, where the problem is divided into sub-problem, then each sub-problem is solved (creating a function to solve that sub problem) and the results are combined to create the answer for the whole problem. Lastly, Objective programming would mimic the real world by create a mini-world inside the computer with many objects, each of which has a (somewhat) unique characteristics, and interacts with others. From those interactions the result would emerge.
每种编程风格都有自己的优点和缺点。因此,做一些诸如“纯编程”(即纯粹的程序设计——顺便说一下,没有人会这样做,这有点奇怪——或纯粹的函数式或纯粹的目标)是非常困难的,如果不是不可能的话,除了一些专门设计来展示编程风格优势的基本问题(因此,我们称那些喜欢纯粹的人为“weenie”:D)。
Then, from those styles, we have programming languages that is designed to optimized for some each style. For example, Assembly is all about procedural. Okay, most early languages are procedural, not only Asm, like C, Pascal, (and Fortran, I heard). Then, we have all famous Java in objective school (Actually, Java and C# is also in a class called "money-oriented," but that is subject for another discussion). Also objective is Smalltalk. In functional school, we would have "nearly functional" (some considered them to be impure) Lisp family and ML family and many "purely functional" Haskell, Erlang, etc. By the way, there are many general languages such as Perl, Python, Ruby.
进一步阐述康拉德的评论:
因此,纯函数式程序总是对输入产生相同的值,求值的顺序没有很好的定义;
因此,函数式代码通常更容易并行化。由于函数(通常)没有副作用,并且它们(通常)只是作用于它们的参数,因此许多并发问题都消失了。
当您需要能够证明您的代码是正确的时,也可以使用函数式编程。这在过程式编程中要困难得多(在函数式编程中不容易,但仍然容易)。
免责声明:我已经很多年没有使用函数式编程了,直到最近才开始重新研究它,所以我在这里可能不完全正确。:)