# 2.1.3数据意味着什么？

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

我们从 [2.1.1](25) 部分开始了有理数实现，方法是根据三个未指定的函数实现有理数运算`add_rat`，`sub_rat`等。 `make_rat`，`numer`和`denom`。 到那时，我们可以认为运算是根据数据对象（分子，分母和有理数）定义的，其行为由后三个函数指定。

但是_数据_到底是什么意思？ 只说&lt;quote&gt;由给定的选择器和构造函数实现的内容是不够的。&lt;/quote&gt; 显然，并非每个三个函数的任意集合都可以用作有理数实现的适当基础。 我们需要保证，如果我们从一对整数`n`和`d`构造有理数`x`，则提取`x`的`numer`和`denom`并将它们除以应该得到相同的结果 结果是`n`除以`d`。 换句话说，`make_rat`，`numer`和`denom`必须满足以下条件：对于任何整数`n`和任何非零整数`d`，如果`x`为`make_rat(n,d)`，则 &lt;split&gt;![%0A%20%20%20%20%20%20%20%20%20%20%5Cfrac%7B%5Ctexttt%7Bnumer%7D%28%5Ctexttt%7Bx%7D%29%7D%7B%5Ctexttt%7Bdenom%7D%28%5Ctexttt%7Bx%7D%29%7D%0A%20%20%20%20%20%20%20%20%20%20%3D%0A%20%20%20%20%20%20%20%20%20%20%5Cfrac%7B%5Ctexttt%7Bn%7D%7D%7B%5Ctexttt%7Bd%7D%7D%0A%20%20%20%20%20%20%20%20%20%20](img/8810cc08b7959e430ad14479026acf1d.jpg)&lt;/split&gt;

实际上，这是`make_rat`，`numer`和`denom`必须满足的唯一条件，以便为有理数表示形成合适的基础。 通常，我们可以认为数据是由选择器和构造函数的一些集合定义的，以及这些函数必须满足的指定条件才能成为有效的表示形式。 [[1]](27#footnote-1)

这种观点不仅可以用于定义&lt;quote&gt;高级&lt;/quote&gt;数据对象（例如有理数），还可以用于定义较低级的对象。 考虑一对的概念，我们使用它来定义我们的有理数。 我们从来没有真正说过一对是什么，只是该语言提供了用于对上操作的函数`pair`，`head`和`tail`。 但是，关于这三个操作，我们唯一需要了解的是，如果使用`pair`将两个对象粘合在一起，则可以使用`head`和`tail`来检索对象。 即，操作满足以下条件：对于任何对象`x`和`y`，如果`z`是`pair(x, y)`，则`head(z)`是`x`并且`tail(x)`是`y`。 确实，我们提到过这三个功能在我们的语言中作为原语包括在内。 但是，可以将满足上述条件的任何三重功能用作实现配对的基础。 我们可以在不使用任何数据结构而仅使用函数的情况下实现`pair`，`head`和`tail`，这一事实令人震惊地说明了这一点。 以下是定义：

```js
function pair(x, y) {
    return m =>
           m === 0 
           ? x
             : m === 1 
	       ? y
               : error(m, "Argument not 0 or 1 in pair");
}
function head(z) {
    return z(0);
}
function tail(z) {
    return z(1);
}
```

函数的使用与我们对数据应该是什么的直观概念没有什么不同。 但是，我们需要做的一切都是证明这些函数满足上述条件，这是表示对的一种有效方法。

需要注意的一点是，`pair(x, y)`返回的值是一个函数-即内部定义的函数`dispatch`，该函数采用一个参数，并根据参数是0还是1返回`x`或`y` 相应地，`head(z)`被定义为将`z`应用于0。因此，如果`z`是由`pair(x, y)`形成的函数，则将`z`应用于0将产生`x`。 因此，我们已经表明，`head(pair(x, y))`会根据需要生成`x`。 同样，`tail(pair(x, y))`将`pair(x, y)`返回的函数应用于1，该函数返回`y`。 因此，对的这种功能实现是有效的实现，如果仅使用`pair`，`head`和`tail`访问对，则无法将这种实现与使用&lt;quote&gt;实际&lt;/quote&gt;数据结构的实现区分开。

展示对的功能表示的意义不是我们的语言以这种方式工作（我们将使用数组来表示对），而是它可以以这种方式工作。 功能表示虽然晦涩难懂，但却是表示对的一种完全合适的方法，因为它满足了配对需要满足的唯一条件。 此示例还演示了将功能作为对象进行操作的能力自动提供了表示复合数据的能力。 现在看来这很有趣，但是数据的功能表示将在我们的编程指南中扮演核心角色。 这种编程风格通常称为_消息传递_，在解决建模和仿真问题时，我们将在第3章中将其用作基本工具。

<exercise>Here is an alternative functional representation of pairs. For this representation, verify that `head(pair(x, y))` yields `x` for any objects `x` and `y`.

```js
function pair(x, y) {
    return m => m(x, y);
}
function head(z) {
    return z((p, q) => p);
}
```

What is the corresponding definition of `tail`? (Hint: To verify that this works, make use of the substitution model of section <ref name="sec:substitution-model">[1.1.5](7)</ref>.)<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_27_1_div">Solution</button> <solution>```js
function tail(z) {
    return z((p,q) => q);
}
```</solution></exercise> <exercise>Show that we can represent pairs of nonnegative integers using only numbers and arithmetic operations if we represent the pair ![a](img/070b1af5eca3a5c5d72884b536090f17.jpg) and ![b](img/6872867a863714d15d9a0d64c20734ce.jpg) as the integer that is the product ![2%5Ea%203%5Eb](img/5d39d3e793cfdb3a63779f87dec9df76.jpg). Give the corresponding definitions of the functions `pair`, `head`, and `tail`.<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_27_2_div">Solution</button> <solution>```js
function pair(a, b) {
    return fast_expt(2, a) * fast_expt(3, b);
}
function head(p) {
    return p % 2 === 0
           ? head(p / 2) + 1
           : 0;
}
function tail(p) {
    return p % 3 === 0 
           ? tail(p/3) + 1
           : 0;
}
```</solution></exercise> <exercise>In case representing pairs as functions wasn't mind-boggling enough, consider that, in a language that can manipulate functions, we can get by without numbers (at least insofar as nonnegative integers are concerned) by implementing 0 and the operation of adding 1 as

```js
const zero = f => x => x;
function add_1(n) {
    return f => x => f(n(f)(x));
}
```

This representation is known as _Church numerals_, after its inventor, Alonzo Church, the logician who invented the ![%5Clambda](img/5e8df2ba7e47a784c714d176ed8bbb7a.jpg) calculus. Define `one` and `two` directly (not in terms of `zero` and `add_1`). (Hint: Use substitution to evaluate `add_1(zero)`). Give a direct definition of the addition function `plus` (not in terms of repeated application of `add_1`).<button class="btn btn-secondary solution_btn" data-toggle="collapse" href="#solution_27_3_div">Solution</button> <solution>```js
const one = f => x => f(x);
const two = f => x => f(f(x));

function plus(n, m) {
    return f => x => n(f)(m(f)(x));
}

// testing

const three = plus(one, two);

function church_to_number(c) {
    return c(n => n + 1)(0);
}
church_to_number(three);
```</solution></exercise> 

* * *

[[1]](27#footnote-link-1) Surprisingly, this idea is very difficult to formulate rigorously. There are two approaches to giving such a formulation. One, pioneered by C. A. R. Hoare (1972), is known as the method of _abstract models_. It formalizes the <quote>functions plus conditions</quote> specification as outlined in the rational-number example above. Note that the condition on the rational-number representation was stated in terms of facts about integers (equality and division). In general, abstract models define new kinds of data objects in terms of previously defined types of data objects. Assertions about data objects can therefore be checked by reducing them to assertions about previously defined data objects. Another approach, introduced by Zilles at MIT, by Goguen, Thatcher, Wagner, and Wright at IBM (see <citation>Thatcher, Wagner, and Wright 1978</citation>), and by Guttag at Toronto (see <citation>Guttag 1977</citation>), is called _algebraic specification_. It regards the <quote>functions</quote> as elements of an abstract algebraic system whose behavior is specified by axioms that correspond to our <quote>conditions,</quote> and uses the techniques of abstract algebra to check assertions about data objects. Both methods are surveyed in the paper by Liskov and Zilles (1975).

