我们知道逻辑是正确推理的原则的研究,或者说简单点,它是关于什么之后跟着什么的研究。例如,如果两个陈述是真的,那么我们可以从中推断出第三个陈述。
概念
逻辑编程是由两个词组成的:逻辑和编程。逻辑编程是一种程序设计范式,其中的问题通过正式逻辑系统中的事实和规则来表达。就像其他编程范式(如面向对象、函数式、声明式和过程式等)一样,它也是一种特定的编程方法论。
如何使用逻辑编程解决问题
逻辑编程使用事实和规则来解决问题。这就是为什么它们被称为逻辑编程的构建块。在逻辑编程中的每一个程序都需要指定一个目标。为了理解如何在逻辑编程中解决问题,我们需要了解构建块——事实和规则:
事实
实际上,每个逻辑程序都需要事实以便能够达到给定的目标。事实基本上是关于程序和数据的真实陈述。例如,“德里是印度的首都”。
规则
实际上,规则是对问题域的约束,使我们能够得出结论。规则通常被写成逻辑子句来表达各种事实。例如,如果我们正在构建任何游戏,则必须定义所有的规则。
规则对于解决任何逻辑编程中的问题是至关重要的。规则基本上是逻辑结论,可以表达事实。以下是规则的语法:
A∶− B1,B2,...,Bn.
这里,A 是头部而 B1, B2, ... Bn 是身体。
例如:
ancestor(X,Y) :- father(X,Y).
ancestor(X,Z) :- father(X,Y), ancestor(Y,Z).
这可以读作,对于每一对 X 和 Y,如果 X 是 Y 的父亲且 Y 是 Z 的祖先,则 X 是 Z 的祖先。对于每一对 X 和 Y,如果 X 是 Y 的父亲且 Y 是 Z 的祖先,则 X 是 Z 的祖先。
安装有用的软件包
为了开始在 Python 中进行逻辑编程,我们需要安装以下两个软件包:
Kanren
Kanren 提供了一种简化业务逻辑代码编写方式。它让我们能够以规则和事实的形式来表达逻辑。以下命令将帮助你安装 Kanren:
pip install kanren
SymPy
SymPy 是一个 Python 库用于符号数学。它的目标是成为一个功能齐全的计算机代数系统(CAS),同时保持尽可能简单的代码以便易于理解和扩展。以下命令将帮助你安装 SymPy:
pip install sympy
逻辑编程的例子
以下是一些可以通过逻辑编程解决的例子:
匹配数学表达式
实际上,我们可以通过使用逻辑编程有效地找到未知值。以下 Python 代码将帮助你匹配一个数学表达式:
考虑先导入以下包:
from kanren import run, var, fact
from kanren.assoccomm import eq_assoccomm as eq
from kanren.assoccomm import commutative, associative
我们需要定义将要使用的数学运算:
add = 'add'
mul = 'mul'
加法和乘法都是互通的过程。因此,我们需要指定这一点,如下所示:
fact(commutative, mul)
fact(commutative, add)
fact(associative, mul)
fact(associative, add)
必须定义变量;可以这样完成:
a, b = var('a'), var('b')
我们需要将表达式与原始模式相匹配。我们有以下原始模式,即基本上是 (5+a)*b:
original_pattern = (mul, (add, 5, a), b)
我们有两个要与原始模式匹配的表达式:
exp1 = (mul, 2, (add, 3, 1))
exp2 = (add,5,(mul,8,1))
输出可以通过以下命令打印:
print(run(0, (a,b), eq(original_pattern, exp1)))
print(run(0, (a,b), eq(original_pattern, exp2)))
运行此代码后,我们将得到以下输出:
((3,2))
()
第一个输出代表了 a 和 b 的值。第一个表达式匹配了原始模式并返回了 a 和 b 的值,但第二个表达式没有匹配原始模式,因此没有返回任何内容。
检查质数
借助逻辑编程,我们可以从一串数字中找出质数,也可以生成质数。下面的 Python 代码将从一组数字中找出质数,并生成前十个质数。
考虑先导入以下包:
from kanren import isvar, run, membero
from kanren.core import success, fail, goaleval, condeseq, eq, var
from sympy.ntheory.generate import prime, isprime
import itertools as it
现在,我们将定义一个名为 prime_check
的函数,它将根据给定的数字检查质数:
def prime_check(x):
if isvar(x):
return condeseq([(eq,x,p)] for p in map(prime, it.count(1)))
else:
return success if isprime(x) else fail
现在,我们需要声明一个将要使用的变量:
x = var()
print((set(run(0,x,(membero,x,(12,14,15,19,20,21,22,23,29,30,41,44,52,62,65,85)),
(prime_check,x)))))
print((run(10,x,prime_check(x))))
上述代码的输出将如下:
{19, 23, 29, 41}
(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
解决谜题
逻辑编程可以用来解决许多问题,比如八数码问题、斑马谜题、数独、N皇后等。这里我们举一个斑马谜题变体的例子:
有五所房子。 英国人住在红色房子里。 瑞典人有一条狗。 丹麦人喝茶。 绿色的房子紧挨着白色的房子。 他们在绿色的房子里喝咖啡。 抽 Pall Mall 的人养鸟。 黄色的房子里抽 Dunhill。 中间的房子喝牛奶。 挪威人住在第一所房子里。 抽 Blend 的人住在养猫的房子旁边。 在养马的房子旁边的人抽 Dunhill。 抽 Blue Master 的人喝啤酒。 德国人抽 Prince。 挪威人住在蓝色房子旁边。 在抽 Blend 的房子旁边喝水。 我们要用 Python 来解决谁拥有斑马的问题。
考虑导入必要的包:
from kanren import *
from kanren.core import lall
import time
现在,我们需要定义两个函数——left()
和 next()
来检查谁的房子在谁的房子左边或旁边:
def left(q, p, list):
return membero((q,p), zip(list, list[1:]))
def next(q, p, list):
return conde([left(q, p, list)], [left(p, q, list)])
现在,我们将声明一个 house
变量:
houses = var()
我们需要使用 lall
包来定义规则:
rules_zebraproblem = lall(
(eq, (var(), var(), var(), var(), var()), houses),
(membero,('Englishman', var(), var(), var(), 'red'), houses),
(membero,('Swede', var(), var(), 'dog', var()), houses),
(membero,('Dane', var(), 'tea', var(), var()), houses),
(left,(var(), var(), var(), var(), 'green'),
(var(), var(), var(), var(), 'white'), houses),
(membero,(var(), var(), 'coffee', var(), 'green'), houses),
(membero,(var(), 'Pall Mall', var(), 'birds', var()), houses),
(membero,(var(), 'Dunhill', var(), var(), 'yellow'), houses),
(eq,(var(), var(), (var(), var(), 'milk', var(), var()), var(), var()), houses),
(eq,(('Norwegian', var(), var(), var(), var()), var(), var(), var(), var()), houses),
(next,(var(), 'Blend', var(), var(), var()),
(var(), var(), var(), 'cats', var()), houses),
(next,(var(), 'Dunhill', var(), var(), var()),
(var(), var(), var(), 'horse', var()), houses),
(membero,(var(), 'Blue Master', 'beer', var(), var()), houses),
(membero,('German', 'Prince', var(), var(), var()), houses),
(next,('Norwegian', var(), var(), var(), var()),
(var(), var(), var(), var(), 'blue'), houses),
(next,(var(), 'Blend', var(), var(), var()),
(var(), var(), 'water', var(), var()), houses),
(membero,(var(), var(), var(), 'zebra', var()), houses)
)
现在,运行带有前述约束条件的求解器:
solutions = run(0, houses, rules_zebraproblem)
通过以下代码,我们可以从求解器中提取输出:
output_zebra = [house for house in solutions[0] if 'zebra' in house][0][0]
以下代码将帮助打印解决方案:
print ('\n'+ output_zebra + ' owns zebra.')
上述代码的输出将是:
German owns zebra.