54

我已经习惯了命令式编程,这是告诉计算机逐步执行程序以获得最终结果的常用方法。另一方面,声明式编程只是传递输入并期望输出,而不说明它是如何完成的过程。我感到困惑的是函数式编程。我知道函数式编程是一种编程范式,它将计算视为对数学函数的评估,并避免状态和可变数据,并且不是一种声明性语言。但是,我仍然无法理解它是如何工作的。

让我们举一个执行斐波那契数列的例子。

命令式编程:

#include<stdio.h> 
#include<conio.h> 
main() 
{ 
  int n,i,c,a=0,b=1; 
  printf("Enter Fibonacci series of nth term : "); 
  scanf("%d",&n); 
  printf("%d %d ",a,b); 
  for(i=0;i<=(n-3);i++) 
  { 
    c=a+b; 
    a=b; 
    b=c;    
  } 
  printf("%d ",c);
  getch(); 
} 

声明式编程:

Give the nth number and it will return the value of the nth number

功能程序如何工作?

另外,如果我的定义错误,请纠正我。请随意发表评论..

4

5 回答 5

104

您上面的声明式编程示例不是一个实际的程序,所以它不是一个很好的例子。

主要区别在于命令式和声明式之间。函数式是一种特殊的声明式。

C、C++、Java、Javascript、BASIC、Python、Ruby 和大多数其他编程语言都是命令式的。通常,如果它有显式循环(for、while、repeat)在每个循环中通过显式赋值操作来更改变量,那么它是必要的。

SQL 和 XSLT 是声明式编程的两个著名示例。HTML 和 CSS 等标记语言也是声明性的,尽管它们通常不足以描述任意算法。

这是一个示例计算(从合适的数据源按性别对收入求和),首先用命令式语言(Javascript)编写,然后用声明性语言(SQL)编写。

命令式编程

var income_m = 0, income_f = 0;
for (var i = 0; i < income_list.length; i++) {
    if (income_list[i].gender == 'M')
        income_m += income_list[i].income;
    else
        income_f += income_list[i].income;
}

注意:

  • 显式初始化将包含运行总计的变量;
  • 显式循环数据,在每次迭代中修改控制变量 ( ) 和运行总计;i
  • 条件 ( ifs) 仅用于在每次迭代时选择代码路径。

声明式编程

select gender, sum(income)
from income_list
group by gender;

注意:

  • 您声明所需的输出暗示了包含运行总数的内存单元;
  • CPU 需要执行的任何循环(例如,在income_list 表上)都隐含在您声明所需的输出和源数据的结构中;
  • 条件(例如case在 SQL 中)以功能方式用于根据输入值指定您想要的输出值,而不是选择代码路径。

函数式编程

正如我上面提到的,SQL是函数case式编程方式的一个很好的例子,它是声明式编程的一个受限子集,其中所需的计算是通过组合函数来指定的。

函数是接受输入并返回输出的东西case(例如,sum()...)

组合意味着通过指定如何将一个的输出作为下一个的输入(通常通过将一个写入另一个内部)将两个或多个链接在一起。最后,整个组合,它本身仍然是一个大函数,应用于可用的输入以获得所需的输出。

在这个片段中,我通过组合函数 sum()case. 这称为函数式编程:

select 
    sum(case when some_flag = 'X' then some_column
        else some_other_column end)
from
    ...

如果两个或多个函数的组合及其对输入数据的应用是给定语言中唯一可用的构造,则称该语言是纯函数式的。在这些语言中,您会注意到完全没有循环、变量赋值和其他典型的命令式语句。


编辑:我建议观看一些Anjana Vakil关于 Javascript 函数式编程的演讲,以便更好地了解它的内容。

于 2013-03-12T10:41:22.707 回答
5

声称命令式编程与声明式编程的区别在于错误地假设后者缺乏排序,这是一种错误的过度简化。

纯函数式编程并没有阻止表达顺序和实现,而是在操作语义级别上表达随机意外顺序的能力较差。它还具有“不要重复自己”(DRY)的优点,这是一种声明式风格(见下文)。

然而,纯函数式编程并不能保证声明性的高级语义。为此,您需要应用声明式与命令式的正确定义

于 2013-03-12T09:20:39.977 回答
4

我在Pro XAML with C#中找到的另一个有用的解释:

声明式

在声明式编程中,源代码的编写方式表达了代码的期望结果,很少或根本不强调实际实现。

势在必行

命令式编程与声明式编程相反。如果声明式编程可以被认为是声明期望的结果是什么,那么命令式编程可以被视为编写代码行来表示如何实现期望结果的指令。

于 2019-06-06T11:58:02.167 回答
2

在函数式编程中,我们使用纯函数构建不可变程序。这就是纯函数:(我提到纯函数是因为函数式编程基于纯函数)

  • 它仅取决于提供的输入,而不取决于在评估期间或调用之间可能发生变化的任何隐藏或外部状态。
  • 它不会造成超出其​​范围的更改,例如修改全局对象或通过引用传递的参数。

简而言之,函数式编程是指对纯函数的声明式评估,以通过避免外部可观察到的副作用来创建不可变的程序。

简单地说,函数式编程是声明式编程范式,它是一种表达一组操作的范式,而不揭示它们是如何实现的或数据如何流经它们的。命令式编程将计算机程序仅视为一系列自上而下的语句,这些语句更改系统状态以计算结果。

下面是一个命令式编程的例子:我们遍历一个数组,计算每个元素的平方,并将新值存储在同一个数组中。我们正在改变数组。此外,此循环不可重用,对于每个不同的数组,我们将定义一个新的 for 循环。

var array= [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 for(let i= 0; i < array.length; i++) {
     array[i]=Math.pow(array[i], 2);
  }
array; //-> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

在javascript中我会使用 Array.map()

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((i)=>i*i)

在这个方法中,我们不知道 Array.map() 是如何实现的。程序的定义与评估是分开的。更重要的是,这段代码不会改变原始数组,它会创建一个新数组

声明式编程的另一个很好的例子是编写 SQL 查询。我们只是编写了一个简单的 SQL 语句来从数据库中提取数据,但我们不知道幕后发生了什么,所有的魔法都被抽象出来了。

于 2021-03-23T16:44:19.377 回答
1

想想 c 过滤器。您从标准输入读取并写入标准输出的位置。代码可能是命令式的,但程序就像函数一样使用。假设您有一个程序的功能,然后通过管道传递给它:

cat foo |功能 |tee bar

将通过函数过滤 foo 的内容,然后通过过滤器tee写入 stdout 并创建bar。想想grepawk两者中的迭代器都是隐含的,它们像函数一样使用。

于 2012-06-07T15:09:13.417 回答