Skip to article frontmatterSkip to article content

逻辑编程模型同样属于声明式范畴, 但与普通的declarative model不同, 它引入了一种完全不同的编码范式.

逻辑编程模型的核心目标是在一个搜索空间(计算空间/数据空间)中, 找到满足给定约束条件的一组数据.

例如, 假设有若干件衣服, 裤子及其他装饰品, 需搜索(衣服, 裤子, 装饰)的元组, 约束条件为不能出现红配蓝, 且不能同时出现长袖, 短裤和领结, 目标是得到所有可能的搭配😅.

例如, 假设有若干件衣服, 裤子及其他装饰品, 需搜索(衣服, 裤子, 装饰)的元组, 约束条件为不能出现红配蓝, 且不能同时出现长袖, 短裤和领结, 目标是得到所有可能的搭配😅.

在逻辑编程模型中, 我们只需指定搜索空间, 约束条件和目标, 具体的搜索与回溯一般由内置的推理引擎(inference engine)执行.

此外, 还有一种类似的模型称为关系模型, 两者核心均为指定约束条件, 在给定搜索空间中进行搜索. 不同之处在于, 关系模型侧重于数据管理与查询, 常见于数据库领域;而逻辑编程模型更关注逻辑推理, 相较关系模型更为通用. 鉴于两者内在逻辑相近, 后续内容中不再特别区分.

Oz2中的relational Model

Oz2 为了支持relational model, 引入了以下三种特性.

logic_programming.oz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
fun {Soft} 
    % choice代表返回值要么是beige, 要么是coral
    choice beige 
        [] coral
    end
end

fun {Hard}
    % 同理
    choice mauve
        [] ochre
    end
end

proc {Contrast C1 C2}
    % 代表C1, C2的可选范围
    choice C1 = {Soft} C2 = {Hard}
       []  C1 = {Hard} C2 = {Soft}
    end
end

fun {Suit}
    Shirt Pants Socks
in
    % 定义了搜索空间
    {Contrast Shirt Pants}
    {Contrast Pants Socks}
    % 这里使用fail, 显式对搜索进行剪枝
    if Shirt == Socks then fail end
    suit(Shirt Pants Socks)
end

% 调用SolveAll进行求解
{Browse {SolveAll Suit}}

Oz2中搜索穿搭组合的例子

本质上Oz2中的执行会形成一颗搜索树, 从根节点开始, 依次搜索Shirt, Pants, Socks的可能值, 遇到fail就回溯. 直到遍历整棵树, 得到所有的可行解.

本质上Oz2中的执行会形成一颗搜索树, 从根节点开始, 依次搜索Shirt, Pants, Socks的可能值, 遇到fail就回溯. 直到遍历整棵树, 得到所有的可行解.

这里问题建模的方式, 和搜索的先后顺序会影响执行效率, 但不影响正确性, 所以这里就不再展开.

Sql中的relational Model

Sql支持relational model, 可以对同一个问题进行建模. 同样的问题在sql中是这样的.

query_suit.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
-- 准备数据
CREATE TABLE colors (
    color_name VARCHAR(50) PRIMARY KEY,
    color_category VARCHAR(10) CHECK (color_category IN ('Hard', 'Soft'))
);

INSERT INTO colors (color_name, color_category) VALUES
('Mauve', 'Hard'),
('Ochre', 'Hard'),
('Beige', 'Soft'),
('Coral', 'Soft');

SELECT -- "solveAll"
    Shirt.color_name AS shirt_color,
    Pants.color_name AS pants_color,
    Socks.color_name AS socks_color,
    
FROM -- 指定搜索空间
    colors Shirt
CROSS JOIN 
    colors Pants
CROSS JOIN 
    colors Socks

WHERE -- 指定约束条件
    (
      (Shirt.color_category = "Soft" AND Pants.color_category = "Hard")
   OR
      (Shirt.color_category = "Hard" AND Pants.color_category = "Soft")
    )
    AND
    (
      (Pants.color_category = "Soft" AND Socks.color_category = "Hard")
      OR
      (Pants.color_category = "Hard" AND Socks.color_category = "Soft")
    )
    AND
    Shirt.color_name <> Socks.color_name

可以看出Sql中也有描述搜索空间, 指定约束条件和SolveAll. 只不过相比于Oz2, Sql更适合用来检索大量结构化数据. 而Oz2更适合用来建模一些组合问题.

Prolog中的logic Programming Model

Prolog(Programming in Logic)是一种基于逻辑编程的声明式编程语言. Prolog诞生于上世纪70年代, 并一直作为逻辑编程和关系模型的标杆而存在. 和其他声明式语言一样, prolog也通过描述"what"而不是"how"来解决问题.

关于prolog的语法可以参考这篇cheatsheet.

其基本构成要素有: • 事实(Facts): 描述确定的关系, 如 parent(tom, bob)., parent(alice, tom).规则(Rules): 定义推理逻辑, 如 grandparent(X,Z) :- parent(X,Y), parent(Y,Z).查询(Queries): 提出问题, 如 ?- grandparent(alice, bob).

Prolog有一个代表性的feature, 叫做unification. 即predicate(类似于函数)既能判定真假, 也能够用来求变量的可能值, 它代表一种逻辑关系.

1
2
3
4
5
6
append([1, 2], [3, 4], [1, 2, 3, 4])        % True
append([1, 2], [5], L)                      % L == [1, 2, 5] 
append(L, [3, 4], [1, 2, 3, 4])             % L == [1, 2]
append([1, 2, 3], M, [1, 2, 3, 4])          % M = [4]
append([1, 2], [3], N)                      % N = [1, 2, 3]
append(P, Q, [1,2,3])                       % P = [] Q = [1,2,3]; P = [1] Q = [2, 3]; ...

append(M,N,L)代表三个list: M,N,L之间的关系, M+N==L

譬如在上述例子中, append(M,N,L)

我们试着在prolog中, 重新实现一遍上述求穿搭的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
:suit.pl

% 定义soft colors
soft_color(beige).
soft_color(coral).

% 定义hard colors
hard_color(mauve).
hard_color(ochre).

% 定义判定不同种类的颜色的predicate
different_color_types(Color1, Color2) :-
    (soft_color(Color1), hard_color(Color2));
    (hard_color(Color1), soft_color(Color2)).

% 定义判断不同颜色的predicate
different_colors(Color1, Color2) :-
    Color1 \= Color2.

% Main predicate
valid_combination(Shirt, Pants, Socks) :-
    % 设定搜索空间
    (soft_color(Shirt); hard_color(Shirt)),
    (soft_color(Pants); hard_color(Pants)),
    (soft_color(Socks); hard_color(Socks)),

    % 设定约束
    different_color_types(Shirt, Pants),    % Constraint 1
    different_color_types(Pants, Socks),    % Constraint 2
    different_colors(Shirt, Socks).         % Constraint 3

% 求解
find_all_combinations :-
    findall([Shirt, Pants, Socks], 
            valid_combination(Shirt, Pants, Socks), 
            Solutions),
    write('All valid color combinations:'), nl,
    print_solutions(Solutions).

% 定义打印结果的predicate
print_solutions([]).
print_solutions([[Shirt, Pants, Socks]|Rest]) :-
    format('Shirt: ~w, Pants: ~w, Socks: ~w~n', [Shirt, Pants, Socks]),
    print_solutions(Rest).
.

Prolog是一门有趣且实用的语言, 曾在人工智能, 专家系统, 自然语言处理, 数据库查询和符号推理等领域取得出色表现, 值得深入了解.

什么时候使用logic Programming Model?

毫无疑问, 在解决搜索问题时, 使用逻辑编程模型是最合适的. 虽然该模型也能用于编写通用代码, 但这并非其设计初衷.

目前原生直接支持逻辑变成或关系模型的语言并不多, 但主流语言通常会提供相关的三方包或库, 例如Python中的kanren, Java中的LogicNG等等.

在业务中遇到搜索问题时, 可以尝试采用该模型, 而对于大量结构化数据的查询, 则应优先使用数据库及SQL(或其封装).