Functional programming 逻辑和函数式编程在gcd实现上的差异
我目前正在学习编程语言的概念和语用学,因此我觉得我需要帮助区分声明性语言家族的两个分支 考虑以下分别用Scheme和Prolog编写的代码段:Functional programming 逻辑和函数式编程在gcd实现上的差异,functional-programming,prolog,scheme,logic,clpfd,Functional Programming,Prolog,Scheme,Logic,Clpfd,我目前正在学习编程语言的概念和语用学,因此我觉得我需要帮助区分声明性语言家族的两个分支 考虑以下分别用Scheme和Prolog编写的代码段: ;Scheme (define gcd (lambda (a b) (cond ((= a b) a) ((> a b) (gcd (- a b) b)) (else (gcd (- b a) a))))) 我不明白的是: 这两种不同
;Scheme
(define gcd
(lambda (a b)
(cond ((= a b) a)
((> a b) (gcd (- a b) b))
(else (gcd (- b a) a)))))
我不明白的是: 这两种不同的编程语言是如何工作的 不同
我们在哪里做出区别,以便将它们分类 基于函数或逻辑的编程语言
就我而言,它们做的事情完全一样,调用递归函数直到它终止 从一个例子来看,差别不是很明显。编程语言分为逻辑语言、函数语言、,。。。基于它们所支持的一些特性,因此它们的设计目的是使程序员在每个领域(逻辑、功能…)更容易。例如,命令式编程语言(如c)与面向对象语言(如java、c++)有很大的不同,这里的差异更为明显 更具体地说,在您的问题中,Prolog编程语言采用了逻辑编程的哲学,这对于稍微了解数理逻辑的人来说是显而易见的。Prolog有谓词(而不是基本上几乎相同的函数),它们根据我们定义的“世界”返回true或false,例如我们已经定义了哪些事实和子句,定义了哪些数学事实等等……所有这些都是由数学逻辑继承的(命题和一阶逻辑)。因此,我们可以说,Prolog被用作逻辑模型,使逻辑问题(如游戏、谜题等)更容易解决。此外,Prolog具有通用语言所具有的一些功能。例如,您可以在示例中编写一个程序来计算gcd:
gcd(A, B, G) :- A = B, G = A.
gcd(A, B, G) :- A > B, C is A-B, gcd(C, B, G).
gcd(A, B, G) :- B > A, C is B-A, gcd(C, A, G).
在您的程序中,如果G与a、B的gcd一致,您使用一个谓词gcd返回TRUE,并且您使用多个子句来匹配所有情况。当您查询gcd(2,5,1)时,
将返回TRUE(请注意,在其他语言如shceme中,您不能将结果作为参数给出),而如果您查询gcd(2,5,G).
它将G与A,B的gcd统一起来,并返回1,这就像问Prolog什么应该是G,以便gcd(2,5,G)。
为真。因此,您可以理解,这都是关于谓词何时成功的问题,因此您可以有多个解决方案,而在函数式编程语言中则不能
- 函数语言基于函数,因此总是返回相同的值
结果类型。这并不总是存在于Prolog中。您可以有一个谓词
和查询谓词\u示例(数字,列表)。
返回列表=…(列表)和查询谓词\u示例(5,列表)。
并返回N=…(一个数字)谓词\u示例(数字,[1,2,3])。
- 结果应该是唯一的,在数学中,函数是一种关系 在一组输入和一组允许输出之间,每个输入与一个输出相关
- 应该清楚将返回的变量是什么参数
例如,gcd函数的类型为:
因此获取属于N(自然数)的A、B参数并返回gcd。但是prolog(在程序中做了一些更改)可能返回参数A,因此查询N*N->R
将给出所有可能的A,使得谓词gcd成功,A=1,2,3,4,5gcd(A,5,1)。
- Prolog为了找到gcd,尝试了各种可能的方法 因此,在每一步中,它都会尝试你们所有人的三个条款,并将 找到所有可能的解决方案。函数式编程语言 另一方面,like函数应该具有定义良好的唯一步骤 找到解决办法
想象一下,在Scheme中解决tic-tac-toe、N-queens问题或人-羊-狼-卷心菜问题是多么困难。GCD示例只是略微提到了逻辑编程和函数编程之间的区别,因为它们彼此之间的距离远比命令式编程更近。我将集中讨论Prolog和OCaml,但我相信这很有代表性 逻辑变量和统一: Prolog允许表达部分数据结构,例如,在术语
节点(24,左,右)
中,我们不需要指定左
和右
代表什么,它们可以是任何术语。函数式语言可以插入一个或一个稍后评估的术语,但在创建术语时,我们需要知道要插入什么
逻辑变量也可以统一(即相等)。OCaml中的搜索函数可能类似于:
let rec find v = function
| [] -> false
| x::_ when v = x -> true
| _::xs (* otherwise *) -> find v xs
虽然Prolog实现可以使用统一而不是v=x
:
member_of(X,[X|_]).
member_of(X,[_|Xs]) :-
member_of(X,Xs).
为了简单起见,Prolog版本有一些缺点(请参见下面的回溯部分)
回溯:
Prolog的优势在于可以轻松撤销的连续实例化变量。如果您使用变量尝试上述程序,Prolog将为您返回所有可能的变量值:
?- member_of(X,[1,2,3,1]).
X = 1 ;
X = 2 ;
X = 3 ;
X = 1 ;
false.
当您需要浏览搜索树时,这特别方便,但需要付出一定的代价。如果我们没有指定列表的大小,我们将连续创建满足我们属性的所有列表-在本例中,无限多个:
?- member_of(X,Xs).
Xs = [X|_3836] ;
Xs = [_3834, X|_3842] ;
Xs = [_3834, _3840, X|_3848] ;
Xs = [_3834, _3840, _3846, X|_3854] ;
Xs = [_3834, _3840, _3846, _3852, X|_3860] ;
Xs = [_3834, _3840, _3846, _3852, _3858, X|_3866] ;
Xs = [_3834, _3840, _3846, _3852, _3858, _3864, X|_3872]
[etc etc etc]
这意味着您需要更加小心地使用Prolog,因为终止更难控制。特别是,旧式的方法(cut操作符!)很难正确使用,最近的方法(延迟目标(如dif)、约束算法或其他方法)的优点仍有一些讨论
?- member_of(X,Xs).
Xs = [X|_3836] ;
Xs = [_3834, X|_3842] ;
Xs = [_3834, _3840, X|_3848] ;
Xs = [_3834, _3840, _3846, X|_3854] ;
Xs = [_3834, _3840, _3846, _3852, X|_3860] ;
Xs = [_3834, _3840, _3846, _3852, _3858, X|_3866] ;
Xs = [_3834, _3840, _3846, _3852, _3858, _3864, X|_3872]
[etc etc etc]
?- Xs = [A,B,C], member_of(1,Xs).
Xs = [1, B, C],
A = 1 ;
Xs = [A, 1, C],
B = 1 ;
Xs = [A, B, 1],
C = 1 ;
false.
gcd(A, A, A).
gcd(A, B, G) :- A #> B, C #= A - B, gcd(C, B, G).
gcd(A, B, G) :- B #> A, C #= B - A, gcd(C, A, G).
?- gcd(X, Y, 3).
X = Y, Y = 3 ;
X = 6,
Y = 3 ;
X = 9,
Y = 3 ;
X = 12,
Y = 3 ;
etc.
?- gcd(X, Y, Z).
X = Y, Y = Z ;
Y = Z,
Z#=>X+ -1,
2*Z#=X ;
Y = Z,
_1712+Z#=X,
Z#=>X+ -1,
Z#=>_1712+ -1,
2*Z#=_1712 ;
etc.