# 4.4.1演绎信息检索

> 译者： [https://sicp.comp.nus.edu.sg/chapters/90](https://sicp.comp.nus.edu.sg/chapters/90)

逻辑编程擅长为数据库提供接口以进行信息检索。 我们将在本章中实现的查询语言就是以此方式设计的。

为了说明查询系统的功能，我们将展示如何使用它来管理Microshaft（波士顿地区一家蓬勃发展的高科技公司）的人事记录数据库。 该语言提供了对人员信息的模式定向访问，并且还可以利用一般规则来进行逻辑推断。

## 样本数据库

Microshaft的人员数据库包含有关公司人员的_断言_。 这是有关常驻计算机向导Ben Bitdiddle的信息：

```js
address(list("Bitdiddle", "Ben"), list("Slumerville", "Ridge Road", 10));
job(list("Bitdiddle", "Ben"), list("computer", "wizard"));
salary(list("Bitdiddle", "Ben"), 60000);
```

&lt;split&gt;每个断言都是一个应用程序，其参数可以是表达式，例如列表。&lt;/split&gt;

作为常驻向导，Ben负责公司的计算机部门，并监督两名程序员和一名技术员。 以下是有关它们的信息：

```js
address(list("Hacker", "Alyssa", "P"), list("Cambridge", "Mass Ave", 78));
job(list("Hacker", "Alyssa", "P"), list("computer", "programmer"));
salary(list("Hacker", "Alyssa", "P"), 40000);
supervisor(list("Hacker", "Alyssa", "P"), list("Bitdiddle", "Ben"));

address(list("Fect", "Cy", "D"), list("Cambridge", "Ames Street", 3));
job(list("Fect", "Cy", "D"), list("computer", "programmer"));
salary(list("Fect", "Cy", "D"), 35000);
supervisor(list("Fect", "Cy", "D"), list("Bitdiddle", "Ben"));

address(list("Tweakit", "Lem", "E"), list("Boston", "Bay State Road", 22));
job(list("Tweakit", "Lem", "E"), list("computer", "technician"));
salary(list("Tweakit", "Lem", "E"), 25000);
supervisor(list("Tweakit", "Lem", "E"), list("Bitdiddle", "Ben"));
```

还有一个程序员培训生，由Alyssa监督：

```js
address(list("Reasoner", "Louis"), list("Slumerville", "Pine Tree Road", 80));
job(list("Reasoner", "Louis"), list("computer", "programmer trainee"));
salary(list("Reasoner", "Louis"), 30000);
supervisor(list("Reasoner", "Louis"), list("Hacker", "Alyssa", "P"));
```

所有这些人都属于计算机部门，以`computer`一词作为其工作说明中的第一项。 本是高级员工。 他的主管本人就是公司的重要人物：

```js
supervisor(list("Bitdiddle", "Ben"), list("Warbucks", "Oliver"));

address(list("Warbucks", "Oliver"), list("Swellesley", "Top Heap Road"));
job(list("Warbucks", "Oliver"), list("administration", "big", "wheel"));
salary(list("Warbucks", "Oliver"), 150000);
```

除了由Ben监督的计算机部门外，该公司还有一个会计部门，由总会计师及其助手组成：

```js
address(list("Scrooge", "Eben"), list("Weston", "Shady Lane", 10));
job(list("Scrooge", "Eben"), list("accounting", list("chief", "accountant"));
salary(list("Scrooge", "Eben"), 75000);
supervisor(list("Scrooge", "Eben"), list("Warbucks", "Oliver"));

address(list("Cratchet", "Robert"), list("Allston", "N Harvard Street", 16));
job(list("Cratchet", "Robert"), list("accounting", "scrivener"));
salary(list("Cratchet", "Robert"), 18000);
supervisor(list("Cratchet", "Robert"), list("Scrooge", "Eben"));
```

大轮子还有一个秘书：

```js
address(list("Aull", "DeWitt"), list("Slumerville", "Onion Square", 5));
job(list("Aull", "DeWitt"), list("administration", "secretary"));
salary(list("Aull", "DeWitt"), 25000);
supervisor(list("Aull", "DeWitt"), list("Warbucks", "Oliver"));
```

该数据库还包含断言，这些事情可以由从事其他工作的人完成。 例如，计算机向导可以完成计算机程序员和计算机技术员的工作：

```js
can_do_job(list("computer", "wizard"), list("computer", "programmer"));
can_do_job(list("computer", "wizard"), list("computer", "technician"));
```

计算机程序员可以填写受训人员：

```js
can_do_job(list("computer", "programmer"),
           list("computer", "programmer", "trainee"));
```

而且，众所周知

```js
can_do_job(list("administration", "secretary"),
           list("administration", "big", "wheel"));
```

## 简单查询

查询语言允许用户通过响应系统提示进行查询来从数据库中检索信息。 例如，找到所有可以说的计算机程序员

```js
// Query input:
job(x, list("computer", "programmer"));
```

系统将响应以下项目：

```js
// Query results:
job(list("Hacker", "Alyssa", "P"), list("computer", "programmer"))
job(list("Fect", "Cy", "D"), list("computer", "programmer"))
```

输入查询指定我们正在数据库中寻找与某个_模式_模式匹配的条目。 在此示例中，模式指定了由三项组成的条目，其中第一项是文字符号`job`，第二项可以是任何东西，第三项是文字列表`list("computer", "programmer")`。 &lt;quote&gt;可以作为匹配列表中第二项的任何&lt;/quote&gt;由_模式变量_和`x`指定。 系统通过显示数据库中与指定模式匹配的所有条目来响应简单查询。

一个模式可以有多个变量。 例如，查询

```js
address(x, y)
```

will list all the employees' addresses.

模式不能有任何变量，在这种情况下，查询仅确定该模式是否是数据库中的条目。 如果是这样，将有一场比赛； 如果没有，将没有匹配项。

相同的模式变量可以在查询中多次出现，并指定在每个位置必须出现相同的&lt;quote&gt;和&lt;/quote&gt;。 这就是变量具有名称的原因。 例如，

```js
supervisor(x, x)
```

finds all people who supervise themselves (though there are no such assertions in our sample data base).

查询

```js
job(x, list("computer",  type));
```

matches all job entries whose third item is a two-element list whose first item is `"computer"`:

```js
job(list("Bitdiddle", "Ben"), list("computer", "wizard"));
job(list("Hacker", "Alyssa", "P"), list("computer", "programmer"));
job(list("Fect", "Cy", "D"), list("computer", "programmer"));
job(list("Tweakit", "Lem", "E"), list("computer", "technician"));
```

_与_不匹配的是同一模式

```js
job(list("Reasoner", "Louis"), list("computer", "programmer", "trainee"));
```

because the third item in the entry is a list of three elements, and the pattern's third item specifies that there should be two elements. If we wanted to change the pattern so that the third item could be any list beginning with `computer`, we could specify

```js
job(x, pair("computer", type))
```

For example,

```js
pair("computer", type)
```

matches the data

```js
list("computer", "programmer", "trainee")
```

with `type` as the list `list("programmer", "trainee")`. It also matches the data

```js
list("computer", "programmer")
```

with type as the list `list("programmer")`, and matches the data

```js
list("computer")
```

with type as the empty list `null`.

我们可以描述查询语言对简单查询的处理，如下所示：

*   系统会在查询模式中找到_满足_模式的变量的所有赋值-也就是说，变量的所有值集，使得如果模式变量被实例化为_（替换为 ）的值，结果在数据库中。_
*   系统通过列出查询模式的所有实例以及满足它的变量分配来响应查询。

注意，如果模式没有变量，则查询将简化为确定该模式是否在数据库中。 如果是这样，则空赋值（不给变量赋值）将满足该数据库的模式。

<exercise>Give simple queries that retrieve the following information from the data base:

1.  由本·比特迪德尔（Ben Bitdiddle）监督的所有人；
2.  会计部门所有人的姓名和职务；
3.  居住在Slumerville的所有人的名字和地址。

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_90_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise>

## 复合查询

简单查询构成查询语言的原始操作。 为了形成复合运算，查询语言提供了组合方式。 使查询语言成为逻辑编程语言的一件事是，组合方式反映了用于形成逻辑表达式的组合方式：`and`，`or`和`not`。 （此处`and`，`or`和`not`不是JavaScript原语，而是内置在查询语言中的操作。）

我们可以使用`and`如下查找所有计算机程序员的地址：

```js
and(job(person, list("computer", "programmer"),
    address(person, where));
```

结果输出是

```js
and(job(list("Hacker", "Alyssa", "P"), list("computer", "programmer")),
    address(list("Hacker", "Alyssa", "P"), list("Cambridge", "Mass Ave", 78)))

and(job(list("Fect", "Cy", "D"), list("computer", "programmer")),
    address(list("Fect", "Cy", "D"), list("Cambridge", "Ames Street", 3)))
```

通常，`and(![%5Ctextit%7Bquery%7D_%7B1%7D](img/8ad061396c2ef94fd4e3d7321f631c63.jpg), ![%5Ctextit%7Bquery%7D_%7B2%7D](img/9b40d8af02b739f5d45c3711b47fd167.jpg), ![%5Cldots](img/9f57fa7487fb5cc87b777f74773ffb74.jpg), ![%5Ctextit%7Bquery%7D_%7Bn%7D%29](img/75354e477ddd90135ec6e29cec0251a0.jpg);`由同时满足![%5Ctextit%7Bquery%7D_%7B1%7D%5Cldots%20%5Ctextit%7Bquery%7D_%7Bn%7D](img/d2d3e3f6fb4092a311fe6a446ee00ca6.jpg)的模式变量的所有值集所满足。

对于简单查询，系统通过查找满足查询的模式变量的所有分配，然后显示具有这些值的查询的实例，来处理复合查询。

构造复合查询的另一种方法是通过`or`。 例如，

```js
or(supervisor(x, list("Bitdiddle", "Ben")),
   supervisor(x, list("Hacker", "Alyssa", "P")));
```

will find all employees supervised by Ben Bitdiddle or Alyssa P. Hacker:

```js
or(supervisor(list("Hacker", "Alyssa", "P"), list("Bitdiddle", "Ben")),
supervisor(list("Hacker", "Alyssa", "P"), list("Hacker", "Alyssa", "P")))

or(supervisor(list("Fect", "Cy", "D"), list("Bitdiddle", "Ben")),
   supervisor(list("Fect", "Cy", "D"), list("Hacker", "Alyssa", "P")))

or(supervisor(list("Tweakit", "Lem", "E"), list("Bitdiddle", "Ben")),
   supervisor(list("Tweakit", "Lem", "E"), list("Hacker", "Alyssa", "P")))

or(supervisor(list("Reasoner", "Louis"), list("Bitdiddle", "Ben")),
   supervisor(list("Reasoner", "Louis"), list("Hacker", "Alyssa", "P")))
```

通常，`or(![%5Ctextit%7Bquery%7D_%7B1%7D](img/8ad061396c2ef94fd4e3d7321f631c63.jpg), ![%5Ctextit%7Bquery%7D_%7B2%7D](img/9b40d8af02b739f5d45c3711b47fd167.jpg), ![%5Cldots](img/9f57fa7487fb5cc87b777f74773ffb74.jpg), ![%5Ctextit%7Bquery%7D_%7Bn%7D](img/3ac42ad86861576451be1b6ce571fd9c.jpg)^);`由满足![%5Ctextit%7Bquery%7D_%7B1%7D%20%5Cldots%20%5Ctextit%7Bquery%7D_%7Bn%7D](img/ae884fa6a00238ada5c579d730a57fa4.jpg)中至少一个的模式变量的所有值的集合来满足。

复合查询也可以使用`not`形成。 例如，

```js
and(supervisor(x, list("Bitdiddle", "Ben")),
    not(job(x, list("computer", "programmer"))));
```

finds all people supervised by Ben Bitdiddle who are not computer programmers. In general, `not(![%5Ctextit%7Bquery%7D_%7B1%7D](img/8ad061396c2ef94fd4e3d7321f631c63.jpg));` is satisfied by all assignments to the pattern variables that do not satisfy ![%5Ctextit%7Bquery%7D_%7B1%7D](img/8ad061396c2ef94fd4e3d7321f631c63.jpg).[[1]](90#footnote-1)

最终的组合形式称为`javascript_value`。 当`javascript_value`是模式的第一个元素时，它指定下一个元素是一个JavaScript谓词，它将作为参数应用于其余（实例化）的元素。 通常，`javascript_value(![%5Ctextit%7Bpredicate%7D](img/28ecfc1c578296d7ed22b45636571912.jpg), ![%5Ctextit%7Barg%7D_%7B1%7D](img/284c115f7917e966b879e49a3bb3732d.jpg), ![%5Cldots](img/9f57fa7487fb5cc87b777f74773ffb74.jpg), ![%5Ctextit%7Barg%7D_%7Bn%7D](img/22cedebe327bac1e9c1473a1546d1ac6.jpg))`将通过分配给模式变量来满足，对于这些模式变量，应用于实例化![%5Ctextit%7Barg%7D_%7B1%7D%2C%20%5Cldots%2C%20%5Ctextit%7Barg%7D_%7Bn%7D](img/4238ded4ca44ca543203d6efe144eb41.jpg)的![%5Ctextit%7Bpredicate%7D](img/28ecfc1c578296d7ed22b45636571912.jpg)为真。 例如，要找到所有薪水高于 &lt;dollar&gt;&lt;/dollar&gt; 30,000的人，我们可以写 [[2]](90#footnote-2)

```js
and(salary(person, amount),
    javascript_value(greater_than, amount, 30000));
```

<exercise>Formulate compound queries that retrieve the following information:

1.  由本·比特迪德尔（Ben Bitdiddle）监督的所有人的名字，以及他们的地址；
2.  所有薪水低于本·比迪德尔（Ben Bitdiddle）的人，以及他们的工资和本·比迪德尔（Ben Bitdiddle）的工资；
3.  由不在计算机部门中的人监督的所有人员，以及主管的姓名和职务。

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_90_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise>

## 规则

除了原始查询和复合查询之外，查询语言还提供了抽象查询的方法。 这些由_规则_给出。 规则

```js
rule(lives_near(person_1, person_2),
     and(address(person_1, pair(town, rest_1)),
         address(person_2, pair(town, rest_2)),
         not(same(person_1, person_2))));
```

specifies that two people live near each other if they live in the same town. The final `not` clause prevents the rule from saying that all people live near themselves. The `same` relation is defined by a very simple rule:[[3]](90#footnote-3)

```js
rule(same(x, x));
```

以下规则声明，如果某人监督某人，而该人又是主管，则该人是组织中的&lt;quote&gt;轮子&lt;/quote&gt;：

```js
rule(wheel(person),
     and(supervisor(middle_manager, person),
         supervisor(x, middle_manager)));
```

规则的一般形式是`rule(![%5Ctextit%7Bconclusion%7D](img/14d542d61f4ae9db733104b8fcf88879.jpg), ![%5Ctextit%7Bbody%7D](img/37455e5f4ca86663530db456cf71bea8.jpg));`，其中![%5Ctextit%7Bconclusion%7D](img/14d542d61f4ae9db733104b8fcf88879.jpg)是模式，![%5Ctextit%7Bbody%7D](img/37455e5f4ca86663530db456cf71bea8.jpg)是任何查询。 [[4]](90#footnote-4) 我们可以将规则视为代表大量（甚至无限）断言的集合，即规则结论的所有实例化都具有满足规则主体的变量赋值。 当我们描述简单查询（模式）时，我们说如果实例化的模式在数据库中，则对变量的分配就满足了模式。 但是该模式不必作为断言在数据库中显式地显示。 它可以是规则暗含的断言。 例如，查询

```js
lives_near(x, list("Bitdiddle", "Ben"))
```

results in

```js
lives_near(list("Reasoner", "Louis"), list("Bitdiddle", "Ben"))            
lives_near(list("Aull", "DeWitt"), list("Bitdiddle", "Ben"))
```

要找到住在Ben Bitdiddle附近的所有计算机程序员，我们可以询问

```js
and(job(x, list("computer", "programmer")),
    lives_near(x, list("Bitdiddle", "Ben")));
```

与复合函数一样，规则可以用作其他规则的一部分（如我们在上面的`lives-near`规则中看到的），甚至可以递归定义。 例如，规则

```js
rule(outranked_by(staff_person, boss),
     or(supervisor(staff_person, boss),
        and(supervisor(staff_person, middle_manager),
            outranked_by(middle_manager, boss))));
```

says that a staff person is outranked by a boss in the organization if the boss is the person's supervisor or (recursively) if the person's supervisor is outranked by the boss.<exercise>Define a rule that says that person 1 can replace person 2 if either person 1 does the same job as person 2 or someone who does person 1's job can also do person 2's job, and if person 1 and person 2 are not the same person. Using your rule, give queries that find the following:

1.  所有可以取代Cy D. Fect的人；
2.  所有可以替代薪水超过其薪水的人的人，以及这两种薪水。

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_90_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Define a rule that says that a person is a <quote>big shot</quote> in a division if the person works in the division but does not have a supervisor who works in the division.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_90_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Ben Bitdiddle has missed one meeting too many. Fearing that his habit of forgetting meetings could cost him his job, Ben decides to do something about it. He adds all the weekly meetings of the firm to the Microshaft data base by asserting the following:

```js
meeting("accounting", list("Monday", "9am"))
meeting("administration", list("Monday", "10am"))
meeting("computer", list("Wednesday", "3pm"))
meeting("administration", list("Friday", "1pm"))
```

Each of the above assertions is for a meeting of an entire division. Ben also adds an entry for the company-wide meeting that spans all the divisions. All of the company's employees attend this meeting.

```js
meeting("whole-company", list("Wednesday", "4pm"))
```

1.  在星期五的早晨，Ben想查询数据库中当天发生的所有会议。 他应该使用什么查询？
2.  Alyssa P. Hacker印象深刻。 她认为，能够通过指定她的名字来要求开会是非常有用的。 因此，她设计了一条规则，规定一个人的会议包括所有`whole-company`会议以及该人部门的所有会议。 填写阿丽莎规则的正文。 `rule(meeting_time(person, day_and_time), ![%5Ctextit%7Brule-body%7D](img/f1e80f60c0672e5d2f8fb7c08b74e2de.jpg));`
3.  阿丽莎（Alyssa）星期三早上上班，她想知道那天她必须参加什么会议。 定义了以上规则后，她应该进行哪些查询才能找到答案？

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_90_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>By giving the query

```js
lives_near(person, list("Hacker", "Alyssa", "P"));
```

Alyssa P. Hacker is able to find people who live near her, with whom she can ride to work. On the other hand, when she tries to find all pairs of people who live near each other by querying

```js
lives_near(person_1, person_2)
```

she notices that each pair of people who live near each other is listed twice; for example,Why does this happen? Is there a way to find a list of people who live near each other, in which each pair appears only once? Explain.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_90_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise>

## 逻辑程序

我们可以将规则视为一种逻辑含义：_如果_为模式变量分配值可以满足人体，_则_可以满足结论。 因此，我们可以认为查询语言具有根据规则执行_逻辑推论_的能力。 例如，请考虑在 [4.4](89) 开头介绍的`append`操作。 正如我们所说，`append`可以通过以下两个规则来表征：

*   对于任何列表`y`，空列表和`y` `append`形成`y`。
*   对于`u`，![%5C%2C](img/4b19862177c4e90d5251360144517e34.jpg) `v`，![%5C%2C](img/4b19862177c4e90d5251360144517e34.jpg) `y`和`z`而言，如果`v`和`y` ] `append`形成`z`。

为了用查询语言表达这一点，我们为关系定义了两个规则

```js
append-to-form("x", "y", "z")
```

which we can interpret to mean <quote>`x` and `y` `append` to form `z`</quote>:

```js
rule(append_to_form(null, y, y));
rule(append_to_form(pair(u, v), y, pair(u, z)),
     append-to-form(v, y, z));
```

第一条规则没有主体，这意味着结论适用于`y`的任何值。 请注意，第二条规则是如何使用点尾符号来命名列表的`head`和`tail`。

给定这两个规则，我们可以制定查询来计算两个列表的`append`：

```js
// Query input:
append-to-form(list("a", "b"), list("c", "d"), z);
// Query results:
append-to-form(list("a", "b"), list("c", "d"), list("a", "b", "c", "d"))
```

更为引人注目的是，我们可以使用相同的规则来问问题&lt;quote&gt;，当`append`编辑为`list("a", "b")`时，哪个列表产生`list("a", "b", "c", "d")`？&lt;/quote&gt; 执行以下操作：

```js
// Query input:
append-to-form(list("a", "b"), y, list("a", "b", "c", "d"));
// Query results:
append-to-form(list("a", "b"), list("c", "d"), list("a", "b", "c", "d"))
```

我们还可以要求将所有`append`形成`list("a", "b", "c", "d")`的列表对：

```js
// Query input:
append-to-form(x, y, list("a", "b", "c", "d"));
// Query results:
append-to-form(null, list("a", "b", "c", "d"), list("a", "b", "c", "d"))
append-to-form(list("a"), list("b", "c", "d"), list("a", "b", "c", "d"))
append-to-form(list("a", "b"), list("c", "d"), list("a", "b", "c", "d"))
append-to-form(list("a", "b", "c"), list("d"), list("a", "b", "c", "d"))
append-to-form(list("a", "b", "c", "d"), null, list("a", "b", "c", "d"))
```

查询系统在使用规则推论以上查询的答案时似乎表现出相当多的智慧。 实际上，正如我们将在下一节中看到的那样，系统在确定规则时遵循了确定的算法。 不幸的是，尽管该系统在`append`情况下表现出色，但一般方法在更复杂的情况下可能会崩溃，正如我们在 [4.4.3](92) 部分中所见。

<exercise>The following rules implement a `next-to` relation that finds adjacent elements of a list:

```js
rule(next_to_in(x, y, pair(x, pair(y, u))),
     and(next_to_in(x, y, pair(v, z)),
         next_to_in(x, y, z));
```

What will the response be to the following queries?

```js
next_to_in(x, y, list(1, list(2, 3), 4));

next_to_in(x, 1, list(2, 1, 3, 1));
```

<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_90_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>Define rules to implement the `last_pair` operation of exercise <ref name="ex:last">[2.17](30#ex_2.17)</ref>, which returns a list containing the last element of a nonempty list. Check your rules on queries such as `last_pair(list(3), x)`, `last_pair(list(1, 2, 3), x)`, and `last_pair(list(2, x, list(3))`. Do your rules work correctly on queries such as `last_pair(x, list(3))`![%5C%2C](img/4b19862177c4e90d5251360144517e34.jpg)?<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_90_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise><exercise>The following data base (see Genesis 4) traces the genealogy of the descendants of Ada back to Adam, by way of Cain:

```js
son("Adam", "Cain");     
son("Cain", "Enoch");
son("Enoch", "Irad");      
son("Methushael", "Lamech");
wife("Lamech", "Ada");
son("Ada", "Jabal");
son("Ada", "Jubal");
```

Formulate rules such as <quote>If _S_ is the son of _F_, and _F_ is the son of _G_, then _S_ is the grandson of _G_</quote> and <quote>If _W_ is the wife of _M_, and _S_ is the son of _W_, then _S_ is the son of _M_</quote> (which was supposedly more true in biblical times than today) that will enable the query system to find the grandson of Cain; the sons of Lamech; the grandsons of Methushael. (See exercise <ref name="ex:great-grandson">[4.60](92#ex_4.60)</ref> for some rules to deduce more complicated relationships.)<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#no_solution_90_1_div">Add solution</button>There is currently no solution available for this exercise. This textbook adaptation is a community effort. Do consider contributing by providing a solution for this exercise, using a Pull Request in [Github](https://github.com/source-academy/sicp).</exercise>

* * *

[[1]](90#footnote-link-1) Actually, this description of `not`. is valid only for simple cases. The real behavior of `not` is more complex. We will examine `not`'s peculiarities in sections <ref name="sec:how-query-works">[4.4.2](91)</ref> and <ref name="sec:math-logic">[4.4.3](92)</ref>.

[[2]](90#footnote-link-2) Such `javascript_value` expressions should be used only to perform an operation not provided in the query language. In particular, it should not be used to test equality (since that is what the matching in the query language is designed to do) or inequality (since that can be done with the `same` rule shown below).

[[3]](90#footnote-link-3) Notice that we do not need `same` in order to make two things be the same: We just use the same pattern variable for each—in effect, we have one thing instead of two things in the first place. For example, see <javascipt>`town`</javascipt> in the <javascipt>`lives_near`</javascipt> rule and <javascipt>`middle-manager`</javascipt> in the `wheel` rule below. The `same` relation is useful when we want to force two things to be different, such as <javascipt>`person_1`</javascipt> and <javascipt>`person_2`</javascipt> in the <javascipt>`"lives-near"`</javascipt> rule. Although using the same pattern variable in two parts of a query forces the same value to appear in both places, using different pattern variables does not force different values to appear. (The values assigned to different pattern variables may be the same or different.)

[[4]](90#footnote-link-4) We will also allow rules without bodies, as in `same`, and we will interpret such a rule to mean that the rule conclusion is satisfied by any values of the variables.

