我知道声明式编程只是传递输入并期望输出而不说明过程是如何完成的。在函数式编程中,是一种编程范式,它接受输入并返回输出。当我检查高阶函数式编程时,我们将一个函数传递给 map/reduce,它没有揭示过程是如何完成的。那么高阶函数式编程和声明式编程是一回事吗?
2 回答
简短的回答:不。
维基百科将声明式编程定义为:
在计算机科学中,声明式编程是一种编程范式——一种构建计算机程序结构和元素的风格——它表达了计算的逻辑而不描述其控制流。
或者说得有点大胆:“说你想要的,而不是你想要的。”。
因此,这与命令式编程语言形成对比,在命令式编程语言中,程序被视为一组依次执行的指令。map
等不透露该过程的事实并不能使其具有声明性:可以使用许多专有的 C 库,并且不允许您检查源代码。然而,这并不意味着这些是声明性的。
另一方面,函数式编程的定义是:
在计算机科学中,函数式编程是一种编程范式——一种构建计算机程序结构和元素的风格——将计算视为对数学函数的评估,并避免改变状态和可变数据。它是一种声明式编程范式,这意味着编程是使用表达式或声明而不是语句来完成的。
基于这些定义,可以说函数式编程是声明式编程的一个子集。然而,在实际意义上,如果我们遵循严格的定义,现在没有一种编程语言是纯粹的、明确的声明式或函数式的。然而,可以说Haskell比 Java更具声明性。
声明式编程通常被认为是“更安全的”,因为人们往往难以管理副作用。许多编程错误是由于没有考虑到所有的副作用。另一方面很难
- 设计一种语言,允许程序员描述他想要的东西,而不需要详细说明如何去做;
- 实现一个编译器,该编译器将基于此类程序生成一个有效的实现;和
- 有些问题具有固有的副作用。例如,如果您使用数据库、网络连接或文件系统,那么读取/写入文件应该会产生副作用。当然可以决定不将这部分作为编程语言(例如,许多约束编程语言不允许这些类型的操作,并且是更大系统中的“子语言”)。
已经有几次尝试设计这种语言。在我看来,最流行的是逻辑编程、函数式编程和约束编程。每个都有其优点和问题。我们还可以在例如数据库(如 SQL)和文本/XML 处理(使用XSLT、XPath、正则表达式...)中观察到这种声明性方法,其中不指定如何解析查询,而只是指定通过例如一个人正在寻找的正则表达式。
然而,编程语言是否是声明性的,是一个有点模糊的讨论。尽管编程语言、建模语言和 Haskell、Prolog、Gecode 等库确实使编程更具声明性,但这些可能不是最严格意义上的声明性。从最严格的意义上讲,人们应该认为无论您如何编写逻辑,编译器总是会得出相同的结果(尽管可能需要更长的时间)。
例如,我们想检查 Haskell 中的列表是否为空。我们可以这样写:
is_empty1 :: [a] -> Bool
is_empty1 [] = True
is_empty1 (_:_) = False
然而,我们也可以这样写:
is_empty2 :: [a] -> Bool
is_empty2 l = length l == 0
对于相同的查询,两者都应该给出相同的结果。但是,如果我们给它一个无限列表,is_empty1 (repeat 0)
它将返回False
而is_empty2 (repeat 0)
将永远循环。所以这意味着我们仍然以某种方式在程序中编写了一些“控制流”:我们已经定义了 - 在某种程度上 - Haskell 应该如何评估它。虽然惰性编程会导致程序员没有真正指定首先应该评估什么,但仍然有规范 Haskell 将如何评估它。
根据某些人的说法,这就是编程和指定之间的区别。我的一位教授曾经说过,根据他的说法,不同之处在于,当你编写某个东西时,你可以以某种方式控制某物的评估方式,而当你指定某物时,你无法控制。但同样,这只是众多定义之一。
不完全是,函数式编程更强调要计算什么而不是如何计算。但是,函数式编程中有一些可用的模式几乎是您通常与声明式编程相关联的控制流模式,例如以下控制流:
let continue = ref true in
while !continue do
...
if cond then continue := false
else
...
done
看起来很眼熟吧?在这里,您可以看到一些声明性构造,但这次我们有更多的控制权。