26.07.2014 Views

Visual Prolog 7 - PDC Download site

Visual Prolog 7 - PDC Download site

Visual Prolog 7 - PDC Download site

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

<strong>Visual</strong> <strong>Prolog</strong> 7.2 语 言 参 考 手 册<br />

本 文 档 描 述 <strong>Visual</strong> <strong>Prolog</strong> 编 程 语 言 的 语 法 与 语 义 。<br />

<strong>Visual</strong> <strong>Prolog</strong> 是 面 向 对 象 的 强 类 型 编 程 语 言 , 它 的 基 础 是 逻 辑 编 程 语 言 <strong>Prolog</strong>。<br />

一 个 <strong>Visual</strong> <strong>Prolog</strong> 程 序 是 由 一 个 目 标 及 一 些 :<br />

• interfaces( 接 口 )<br />

• Class declarations( 类 声 明 ) 及<br />

• Class implementations( 类 实 现 )<br />

构 成 的 , 上 述 这 些 部 件 包 含 有 下 面 这 些 <strong>Prolog</strong> 实 体 的 声 明 与 定 义 :<br />

• Domains ( 域 )<br />

• Constants ( 常 数 )<br />

• Predicates ( 谓 词 )<br />

• Properties ( 属 性 )<br />

• Fact databases ( 事 实 数 据 库 )。<br />

<strong>Visual</strong> <strong>Prolog</strong> 的 实 际 代 码 是 clauses( 子 句 ), 它 在 类 实 现 当 中 , 谓 词 是 由 它 实 现 的 。<br />

基 本 概 念<br />

Types and Subtypes ( 类 型 和 子 类 型 )<br />

Types<br />

<strong>Visual</strong> <strong>Prolog</strong> 的 类 型 分 为 对 象 类 型 与 值 类 型 。 对 象 是 易 变 的 而 值 是 非 易 变 的 。<br />

对 象 的 类 型 是 由 interface( 接 口 ) 定 义 的 。<br />

值 的 类 型 包 括 numerical types( 数 值 类 型 )、strings( 串 )、character types( 字 符 类 型 ) 和 compound<br />

domains( 复 合 域 )。 复 合 域 又 称 为 代 数 数 据 类 型 , 它 的 简 单 形 式 有 结 构 及 枚 举 类 型 , 而 其 复 杂 形 式 会 呈<br />

现 为 树 的 结 构 。<br />

Subtypes<br />

类 型 是 由 子 类 型 体 系 构 成 的 。 子 类 型 用 来 引 入 多 态 包 容 性 : 任 何 期 望 某 类 型 值 的 语 境 也 能 很 好 地 接 受<br />

任 意 子 类 型 的 值 。 也 可 以 反 过 来 说 , 某 个 类 型 的 值 在 需 要 的 时 候 可 以 自 动 地 转 换 成 任 意 父 类 型 加 以 应 用 ,<br />

而 不 需 要 显 式 的 类 型 转 换 。<br />

子 类 型 可 以 由 除 代 数 数 据 类 型 而 外 的 任 何 值 类 型 派 生 出 来 。 代 数 数 据 类 型 派 生 出 的 类 型 是 异 名 类 型 而<br />

不 是 子 类 型 , 也 就 是 说 , 它 们 是 相 同 的 类 型 而 非 子 类 型 。<br />

子 类 型 和 子 集 在 概 念 上 很 相 近 , 但 是 一 定 要 注 意 , 即 便 一 个 类 型 “ 算 术 上 ” 是 另 一 个 类 型 的 子 集 它 也<br />

末 必 就 是 一 个 子 类 型 。 只 有 声 明 了 之 后 一 个 类 型 才 可 以 是 另 一 个 类 型 的 子 类 型 。<br />

domains<br />

t1 = [1..17].<br />

t2 = [5..13].<br />

t3 = t1 [5..13].<br />

1


t1 是 整 数 类 型 , 它 的 值 从 1 到 17( 含 )。 而 t2 的 值 是 5 到 13, 所 以 ,t2 是 t1 的 一 个 子 集 , 但 它<br />

并 非 t1 的 一 个 子 类 型 。 另 一 方 面 ,t3( 它 也 含 有 与 t2 一 样 的 值 ) 则 是 t1 的 一 个 子 类 型 , 因 为 声 明 如 此 。<br />

本 语 言 中 有 几 个 隐 含 的 子 类 型 关 系 , 但 一 般 而 言 子 类 型 关 系 都 必 须 在 类 型 定 义 中 显 式 地 说 明 。<br />

对 象 类 型 也 是 由 子 类 型 体 系 构 成 的 , 根 是 预 定 义 的 对 象 类 型 Object, 也 就 是 说 任 意 对 象 类 型 都 是<br />

Object 的 子 类 型 。 对 象 子 类 型 的 定 义 是 通 过 声 明 说 一 个 接 口 支 持 另 一 个 而 实 现 的 。 如 果 一 个 对 象 具 有 支<br />

持 某 个 其 它 接 口 的 接 口 / 对 象 , 那 么 该 对 象 就 有 了 那 个 类 型 , 可 以 无 冲 突 地 当 作 那 个 对 象 来 使 用 。<br />

参 看 :Universal and Root Types( 通 用 类 型 和 根 类 型 )<br />

对 象 系 统<br />

外 视 图<br />

这 一 节 的 描 述 不 是 对 类 的 一 般 性 的 介 绍 , 而 是 对 <strong>Visual</strong> <strong>Prolog</strong> 中 类 的 概 念 说 明 , 希 望 读 者 已 经 熟 悉<br />

通 常 的 类 概 念 。 描 述 完 全 是 纯 概 念 意 义 上 的 , 不 涉 及 语 法 、 实 现 等 , 也 不 考 虑 操 作 或 编 程 方 面 的 问 题 。 尽<br />

管 引 入 类 、 对 象 的 主 要 原 因 是 程 序 本 质 上 的 要 求 , 但 只 描 述 基 本 观 念 而 不 去 考 虑 程 序 上 的 原 因 还 是 很 有 价<br />

值 的 。<br />

<strong>Visual</strong> <strong>Prolog</strong> 中 类 的 概 念 是 以 如 下 三 个 语 义 实 体 为 基 础 的 :<br />

• objects<br />

• interfaces<br />

• classes<br />

Object<br />

一 个 对 象 , 就 是 一 套 命 名 了 的 对 象 成 员 谓 词 及 一 套 支 持 的 接 口 。<br />

其 实 对 象 也 有 状 态 , 但 这 个 状 态 只 能 通 过 成 员 谓 词 才 能 改 变 和 观 察 。 我 们 称 之 为 状 态 封 装 在 对 象 中 。<br />

这 里 的 封 装 , 意 味 着 对 象 有 隐 藏 其 内 部 数 据 和 方 法 的 能 力 , 使 对 对 象 的 访 问 受 控 。 封 装 和 模 块 化 的 重<br />

要 性 是 众 所 周 知 的 。 封 装 有 助 于 构 建 更 加 结 构 化 和 易 读 的 程 序 , 因 为 对 象 可 以 看 作 为 一 个 黑 盒 子 。 要 解 一<br />

个 复 杂 的 问 题 , 先 找 出 可 以 下 断 言 和 描 述 的 一 部 分 , 把 它 封 装 在 一 个 对 象 里 , 构 造 一 个 接 口 , 接 着 再 往 下 ,<br />

直 到 声 明 了 所 有 子 问 题 。 封 装 了 问 题 对 象 后 , 只 要 保 证 它 们 工 作 正 常 , 就 可 以 从 对 象 中 抽 取 数 据 。<br />

Interface<br />

接 口 是 一 个 对 象 类 型 , 它 有 一 个 名 字 并 定 义 一 组 命 名 了 的 对 象 谓 词 。<br />

接 口 按 支 持 体 系 构 建 ( 这 个 结 构 是 半 格 状 的 , 根 是 interface 对 象 )。 如 果 一 个 对 象 具 有 一 个 接 口 表 示<br />

的 类 型 , 它 也 就 有 所 有 支 持 接 口 的 类 型 。 因 此 , 支 持 体 系 也 是 类 型 体 系 。 一 个 接 口 是 所 有 它 支 持 的 接 口 的<br />

一 个 子 类 型 。 也 可 以 说 对 象 支 持 接 口 , 如 果 接 口 名 为 X, 我 们 可 以 说 这 个 对 象 是 一 个 X, 或 是 一 个 X 对 象 。<br />

Class<br />

类 是 命 名 了 的 对 象 工 厂 , 它 创 建 相 应 于 某 个 接 口 的 对 象 。 任 何 对 象 都 是 由 类 创 建 的 , 如 果 一 个 类 是 用<br />

接 口 C 构 造 对 象 的 , 那 么 这 个 类 创 建 的 所 有 对 象 都 叫 C 对 象 。<br />

由 某 个 类 构 造 的 所 有 对 象 共 享 相 同 的 对 象 成 员 谓 词 定 义 , 但 每 个 对 象 有 其 自 己 的 状 态 。 因 此 , 对 象 成<br />

员 谓 词 实 际 上 是 类 的 一 部 分 , 而 对 象 的 状 态 则 是 对 象 本 身 的 一 部 分 。<br />

2


类 还 含 有 被 称 为 类 成 员 和 类 状 态 的 另 一 组 命 名 了 的 谓 词 和 封 装 的 状 态 。 类 成 员 与 类 状 态 存 在 于 各 个 类<br />

中 , 而 对 象 成 员 与 对 象 状 态 存 在 于 各 个 对 象 中 。 类 成 员 和 对 象 成 员 都 可 以 访 问 类 状 态 。<br />

注 意 ! 一 个 类 所 定 义 的 对 象 成 员 谓 词 组 是 在 该 类 的 接 口 中 声 明 的 谓 词 联 合 体 , 更 明 确 地 说 , 如 果 在 不<br />

同 的 两 个 接 口 中 声 明 了 同 一 个 谓 词 , 类 只 会 提 供 该 谓 词 的 一 个 定 义 。 因 此 , 类 只 是 检 测 实 际 意 义 , 也 就 是<br />

说 只 是 看 这 两 个 继 承 谓 词 的 预 期 语 义 是 否 一 致 。<br />

要 注 意 , 接 口 支 持 必 须 明 确 地 指 定 。 某 个 类 提 供 相 应 某 个 接 口 的 谓 词 并 不 意 味 着 这 个 类 支 持 这 个 接 口 。<br />

Module( 模 块 )<br />

事 实 上 , 类 不 必 非 得 能 产 生 对 象 。 不 产 生 对 象 的 类 只 有 类 成 员 和 类 状 态 , 而 这 样 的 类 当 模 块 看 待 更 恰<br />

当 。<br />

Identity( 标 识 )<br />

每 个 对 象 都 是 唯 一 的 : 对 象 具 有 可 变 的 状 态 , 而 且 对 象 的 状 态 只 能 通 过 它 们 的 成 员 谓 词 观 察 到 , 因 而<br />

对 象 只 与 它 自 己 是 同 一 的 。 这 就 是 说 , 即 使 有 两 个 对 象 的 状 态 是 一 样 的 , 它 们 也 不 是 同 一 的 , 因 为 我 们 可<br />

以 改 变 一 个 对 象 的 状 态 而 不 影 响 另 一 个 对 象 的 状 态 。<br />

我 们 无 法 直 接 访 问 一 个 对 象 的 状 态 , 而 总 是 通 过 对 象 的 引 用 来 访 问 对 象 的 状 态 。 尽 管 对 象 仅 与 自 身 是<br />

同 一 的 , 但 可 以 有 许 多 对 同 一 对 象 的 引 用 。 因 此 , 一 个 对 象 可 以 由 许 多 不 同 的 引 用 来 访 问 。<br />

类 和 接 口 也 是 唯 一 的 , 用 它 们 的 名 字 来 标 识 。 在 一 个 程 序 中 , 两 个 接 口 或 两 个 类 不 能 用 相 同 的 名 字 ,<br />

只 有 当 一 个 类 构 造 一 个 接 口 的 对 象 时 , 这 一 个 类 和 这 一 个 接 口 才 可 以 用 相 同 的 名 字 。<br />

总 之 , 结 构 的 相 同 并 不 意 味 着 同 一 , 对 对 象 、 类 和 接 口 都 是 这 样 的 。<br />

内 视 图<br />

前 一 节 描 述 了 对 象 、 类 及 接 口 的 外 在 表 现 , 这 一 节 描 述 内 部 情 况 。 这 些 内 部 问 题 更 具 程 序 特 性 , 可 以<br />

把 它 们 分 成 声 明 与 实 现 两 个 部 分 来 考 虑 。<br />

从 程 序 的 观 点 , 类 是 核 心 : 代 码 是 包 含 在 类 中 的 。<br />

接 口 主 要 地 是 静 态 特 性 , 实 事 上 , 接 口 只 存 在 于 一 个 程 序 的 文 本 表 示 中 , 运 行 时 没 有 一 个 ( 直 接 的 )<br />

代 表 接 口 的 东 西 。 而 另 一 方 面 , 对 象 主 要 地 是 动 态 特 性 , 它 在 程 序 中 并 不 是 直 接 可 见 的 , 直 到 程 序 实 际 运<br />

行 时 它 才 存 在 。<br />

类 由 声 明 与 实 现 构 成 。 声 明 通 告 了 类 的 public( 公 共 ) 可 访 问 的 部 分 及 它 生 成 的 对 象 。 而 实 现 则 defines<br />

( 定 义 ) 在 类 声 明 中 声 明 了 的 实 体 。 谓 词 的 基 本 实 现 当 然 是 子 句 , 但 谓 词 也 可 以 由 继 承 定 义 , 或 是 由 外<br />

部 的 库 来 确 定 。<br />

在 <strong>Visual</strong> <strong>Prolog</strong> 中 , 类 的 声 明 纯 粹 是 说 明 , 它 只 是 陈 述 有 哪 些 实 体 可 以 访 问 , 但 并 不 涉 及 如 何 访 问 及<br />

实 体 在 哪 儿 实 现 。<br />

类 实 现 可 以 声 明 和 定 义 更 多 的 实 体 ( 域 、 谓 词 等 ), 它 们 只 在 类 的 内 部 是 可 见 的 , 也 就 是 说 , 它 们 是<br />

私 有 的 (private)。<br />

对 象 的 状 态 是 存 储 在 对 象 本 身 的 事 实 中 的 , 这 些 事 实 是 在 类 的 实 现 中 普 通 事 实 ( 数 据 库 ) 段 中 声 明 的 。<br />

事 实 对 每 个 对 象 都 是 局 部 的 ( 就 像 其 它 的 对 象 实 体 ), 而 类 事 实 则 由 同 类 的 所 有 对 象 共 享 。<br />

事 实 只 能 在 类 的 实 现 中 声 明 , 因 而 也 就 不 能 从 类 的 外 部 ( 直 接 ) 访 问 。<br />

类 的 实 现 也 可 以 声 明 它 支 持 除 类 声 明 中 提 到 的 以 外 的 其 它 接 口 。 但 这 个 消 息 也 仅 只 在 实 现 本 身 内 部 是<br />

可 见 的 , 因 而 还 是 私 有 的 。<br />

3


代 码 继 承<br />

<strong>Visual</strong> <strong>Prolog</strong> 的 代 码 继 承 只 能 在 类 的 实 现 中 进 行 , 可 以 有 多 重 继 承 。 通 过 在 类 实 现 中 特 殊 的 inherits<br />

段 标 记 一 个 类 来 得 到 该 类 的 继 承 。 被 继 承 的 类 称 为 父 类 (parent classes) 或 超 类 (super-classes), 子<br />

类 (Child class) 或 亚 类 (sub-class) 与 父 类 是 成 对 的 , 我 们 也 说 子 类 继 承 自 父 类 。 子 类 只 能 通 过 父 类<br />

的 公 共 接 口 访 问 它 的 父 类 , 也 就 是 说 它 与 其 它 使 用 该 父 类 的 类 一 样 , 并 没 有 接 受 额 外 的 特 权 。<br />

Scoping & Visibility( 范 围 与 可 见 性 )<br />

名 称 类 别<br />

<strong>Visual</strong> <strong>Prolog</strong> 中 的 所 有 名 称 ( 标 识 ) 按 语 法 分 成 两 个 大 组 :<br />

• 常 量 名 称 ( 以 小 写 字 母 开 头 )<br />

• 变 量 名 称 ( 以 大 字 字 母 或 下 划 线 开 头 )<br />

常 量 名 称 又 分 为 如 下 几 类 :<br />

• 类 型 名 ( 如 域 和 接 口 );<br />

• 域 载 体 ( 如 类 和 接 口 );<br />

• 不 带 圆 括 号 的 名 称 ( 如 常 数 、 非 函 数 类 型 的 事 实 变 量 及 零 函 子 );<br />

• 返 回 值 的 元 维 为 N 的 名 称 ( 如 函 数 、 函 子 和 函 数 类 型 的 事 实 变 量 );<br />

• 不 返 回 值 的 元 维 为 N 的 名 称 ( 如 谓 词 、 事 实 及 谓 词 类 型 的 事 实 变 量 )。<br />

<strong>Visual</strong> <strong>Prolog</strong> 要 求 在 声 明 阶 段 名 称 就 不 能 有 冲 突 , 因 为 在 使 用 阶 段 它 就 不 可 能 解 决 这 样 的 冲 突 了 。 只<br />

有 在 同 一 范 围 的 名 称 才 可 能 会 有 冲 突 , 而 不 同 的 范 围 因 为 范 围 的 限 定 可 以 解 决 冲 突 。 某 个 类 别 的 名 称 决 不<br />

会 与 另 一 个 类 别 中 的 名 称 有 冲 突 , 但 是 我 们 就 要 看 到 在 单 个 声 明 中 可 以 把 一 个 名 字 放 在 几 个 类 别 中 。<br />

Packages( 包 )<br />

包 , 是 <strong>Visual</strong> <strong>Prolog</strong> 中 一 般 公 认 的 代 码 组 织 基 本 单 位 , 我 们 用 包 来 组 建 东 西 。 包 的 使 用 , 保 证 了 不 同<br />

工 程 间 构 造 原 则 的 一 致 性 。 包 为 工 程 间 构 造 和 共 享 代 码 的 工 具 确 定 了 标 准 。<br />

包 是 成 组 的 接 口 和 类 的 集 合 体 , 它 为 所 有 这 些 接 口 和 类 提 供 了 某 个 共 同 的 名 称 。 包 里 每 个 接 口 的 各 个<br />

声 明 或 实 现 是 放 在 独 立 的 文 件 中 的 , 这 些 文 件 的 文 件 名 与 在 该 文 件 中 声 明 的 或 实 现 的 类 或 接 口 的 名 称 相 对<br />

应 。 所 有 包 文 件 存 放 在 一 个 独 立 的 包 目 录 下 ( 如 果 一 个 包 有 子 包 , 则 子 包 会 放 在 包 目 录 的 子 目 录 中 )。<br />

包 的 概 念 , 用 来 把 一 些 相 关 的 接 口 与 类 组 合 在 一 起 , 它 起 了 一 个 类 库 角 色 的 作 用 。 在 程 序 中 包 可 以 把<br />

使 用 的 接 口 和 类 集 中 起 来 , 而 不 是 直 接 把 这 些 接 口 和 类 放 在 程 序 里 。<br />

在 帮 助 文 件 中 的 VDE 部 分 , 描 述 了 <strong>Visual</strong> <strong>Prolog</strong> 中 可 用 的 包 结 构 及 如 何 将 包 放 到 工 程 中 。( 参 看<br />

Creating a Package in Creating New Project Items)<br />

可 见 性 、 屏 蔽 及 限 定 符<br />

大 多 数 范 围 规 则 上 面 都 说 了 , 这 一 节 最 后 完 善 整 个 内 容 。<br />

接 口 定 义 , 类 声 明 以 及 类 实 现 都 是 范 围 ( 范 围 不 能 嵌 套 )。 实 现 ( 悄 悄 地 ) 扩 展 了 相 应 的 类 声 明 的 范<br />

围 。 在 一 个 范 围 内 , 可 见 性 到 处 都 是 一 样 的 。 这 尤 其 是 指 在 一 个 范 围 内 不 论 某 个 东 西 是 在 哪 儿 声 明 的 , 它<br />

在 整 个 范 围 内 都 是 可 见 的 。<br />

来 自 被 支 持 的 接 口 和 超 类 的 公 用 名 称 , 如 果 它 出 自 何 处 不 模 糊 , 在 一 个 范 围 内 是 直 接 可 用 的 ( 也 就 是<br />

不 需 要 限 定 符 )。 谓 词 调 用 中 的 模 糊 问 题 , 可 以 用 带 类 名 的 谓 词 名 的 修 饰 方 法 ( 如 cc::p) 来 解 决 。<br />

4


这 样 的 限 定 符 , 也 用 于 修 饰 当 前 对 象 对 超 类 的 对 象 成 员 谓 词 的 调 用 中 。<br />

<strong>Visual</strong> <strong>Prolog</strong> 有 两 个 屏 蔽 层 级 :<br />

• 局 部 的<br />

• 超 类 及 开 放 范 围 (opened scopes)<br />

开 放 范 围 的 情 况 与 超 类 的 一 样 , 所 以 我 们 下 面 只 说 超 类 。<br />

层 级 的 意 思 是 , 局 部 声 明 会 屏 蔽 超 类 声 明 , 但 两 个 超 类 间 不 会 有 屏 蔽 , 所 有 超 类 都 有 同 样 的 优 选 权 。<br />

如 果 两 个 或 更 多 个 超 类 包 含 有 冲 突 的 声 明 , 那 这 些 声 明 就 只 能 通 过 限 定 符 来 访 问 。<br />

例 设 有 接 口 aa 及 类 aa_class:<br />

interface aa<br />

predicates<br />

p1 : () procedure ().<br />

p2 : () procedure ().<br />

p3 : () procedure ().<br />

end interface<br />

class aa_class : aa<br />

end class<br />

再 假 设 有 bb_class 类 :<br />

class bb_class<br />

predicates<br />

p3 : () procedure ().<br />

p4 : () procedure ().<br />

end class bb_class<br />

在 上 述 情 况 下 , 来 看 看 类 cc_class 的 实 现 :<br />

implement cc_class inherits aa_class<br />

open bb_class<br />

predicates<br />

p2 : () procedure ().<br />

p5 : () procedure ().<br />

clauses<br />

new() :-<br />

p1(),<br />

% aa_class::p1<br />

p2(),<br />

% cc::p2 ( 屏 蔽 了 aa_class::p2)<br />

aa_class::p2(), % aa_class::p2<br />

p3(),<br />

% 错 误 的 模 糊 调 用 : 可 以 是 aa_class::p3 或 bb_class::p3<br />

aa_class::p3(), % aa_class::p3<br />

bb_class::p3(), % bb_class::p3<br />

p4(),<br />

% bb_class::p4<br />

p5(),<br />

% cc::p5<br />

end implement cc_class<br />

词 汇 元 素<br />

源 文 件 要 经 过 <strong>Visual</strong> <strong>Prolog</strong> 编 译 器 的 编 译 。 一 个 源 文 件 可 以 包 含 其 它 的 源 文 件 , 从 概 念 上 来 讲 插 入 的<br />

这 些 文 件 与 包 含 它 们 的 文 件 组 成 了 一 个 编 译 单 元 。 一 个 编 译 单 元 的 编 译 要 经 过 两 个 概 念 上 的 步 骤 :<br />

• 首 先 , 输 入 被 转 换 成 记 号 的 序 列 ;<br />

5


• 其 次 , 对 这 些 记 号 进 行 语 法 分 析 并 转 换 成 可 执 行 代 码 。<br />

对 程 序 的 词 汇 分 析 把 编 译 单 元 CompilationUnit 再 细 分 成 输 入 元 素 InputElement 的 表 :<br />

CompilationUnit:<br />

InputElement-list<br />

InputElement:<br />

Comment<br />

WhiteSpace<br />

Token<br />

只 有 记 号 (Token) 对 后 面 的 语 法 分 析 有 意 义 。<br />

注 释<br />

<strong>Visual</strong> <strong>Prolog</strong> 的 注 释 可 以 按 以 下 方 法 编 写 :<br />

• 以 /*( 斜 杠 , 星 ) 开 头 , 后 面 是 任 意 字 符 序 列 ( 包 括 换 行 符 ), 结 束 用 */( 星 , 斜 杠 )。 这 样 的 注<br />

释 可 以 有 多 行 , 可 以 嵌 套 。<br />

• 以 %( 百 分 号 ) 开 头 , 后 面 是 任 意 字 符 序 列 。 这 样 的 注 释 在 一 行 的 结 尾 就 结 束 了 , 因 此 通 常 称 之<br />

为 单 行 注 释 。<br />

看 一 下 下 面 的 注 释 :<br />

/* 注 释 1 的 开 头<br />

% 嵌 套 了 注 释 2 */ 这 个 标 记 不 会 结 束 多 行 注 释<br />

因 为 它 插 入 了 一 个 单 行 注 释<br />

这 个 才 是 注 释 1 实 际 结 束 的 标 记 */<br />

空 白<br />

WhiteSpace:<br />

Space<br />

Tab<br />

NewLine<br />

这 里 的 Space 是 空 格 字 符 ,Tab 是 制 表 字 符 而 NewLine 是 换 行 字 符 。<br />

Tokens( 记 号 )<br />

Token:<br />

Identifier( 标 识 )<br />

Keyword( 关 键 字 )<br />

Punctuator( 标 点 )<br />

Operator( 操 作 符 )<br />

Literal( 文 字 )<br />

标 识<br />

Identifier:<br />

LowercaseIdentifier( 小 写 标 识 )<br />

UppercaseIdentifier( 大 写 标 识 )<br />

AnonymousIdentifier( 匿 名 标 识 )<br />

6


Ellipsis( 省 略 号 )<br />

小 写 标 识 是 一 个 以 小 写 字 母 开 头 的 字 母 、 数 字 及 下 划 线 组 成 的 序 列 ; 大 写 标 识 是 一 个 以 大 写 字 母 或 下<br />

划 线 开 头 的 字 母 、 数 字 及 下 划 线 组 成 的 序 列 。 匿 名 标 识 是 一 个 下 划 线 :“_”; 而 省 略 号 则 是 三 个 句 点 :“…”。<br />

关 键 字<br />

关 键 字 分 为 主 关 键 字 和 次 关 键 字 两 类 , 但 这 只 是 表 面 上 的 分 法 , 主 次 关 键 字 并 没 有 形 式 上 的 差 异 。 不<br />

过 我 们 下 面 还 是 用 不 同 颜 色 区 分 它 们 :<br />

Keyword :<br />

MajorKeyword( 主 关 键 字 )<br />

MinorKeyword( 次 关 键 字 )<br />

MajorKeyword : one of<br />

class clauses constants constructors<br />

delegate domains<br />

end<br />

facts<br />

goal guards<br />

implement inherits interface<br />

monitor<br />

namespace<br />

open<br />

predicates<br />

properties<br />

resolve<br />

supports<br />

MinorKeyword : one of<br />

align and anyflow as<br />

bitsize<br />

catch<br />

determ digits div do<br />

else elseif erroneous externally<br />

failure finally foreach from<br />

if<br />

language<br />

mod multi<br />

nondeterm<br />

or<br />

procedure<br />

quot<br />

rem<br />

single<br />

then to try<br />

所 有 关 键 字 除 了 as 和 language 都 是 保 留 字 。<br />

注 意 ,div 和 mod 也 是 保 留 字 , 但 它 们 是 归 类 在 操 作 符 中 的 。<br />

guards 和 monitor 在 语 言 中 并 没 有 使 用 , 但 它 们 是 保 留 给 将 来 使 用 的 。<br />

标 点 符 号<br />

7


标 点 符 号 在 <strong>Visual</strong> <strong>Prolog</strong> 中 对 编 译 器 既 有 语 法 意 义 又 有 语 义 意 义 , 但 它 们 本 身 不 是 能 产 生 值 的 操 作 运<br />

算 。 有 的 标 点 符 号 , 单 独 或 组 合 起 来 也 可 以 是 <strong>Visual</strong> <strong>Prolog</strong> 的 操 作 符 。<br />

操 作 符<br />

标 点 符 号 有 :<br />

PunctuationMarks: one of<br />

; ! , . # [ ] | ( ) :- : ::<br />

操 作 符 规 定 了 对 相 关 的 操 作 数 要 进 行 的 运 算 操 作 。<br />

Operators: one of<br />

+ - / * ^ = div mod quot rem<br />

< > >< = :=<br />

所 有 操 作 符 都 是 二 元 的 , 但 - 和 + 也 可 以 是 一 元 的 操 作 符 。<br />

div 和 mod 是 保 留 字 。<br />

文 字<br />

整 数 文 字<br />

文 字 概 括 了 下 面 的 内 容 : 整 数 、 字 符 、 浮 点 数 、 串 、 二 进 制 数 字 串 和 表 :<br />

Literal:<br />

IntegerLiteral<br />

RealLiteral<br />

CharacterLiteral<br />

StringLiteral<br />

BinaryLiteral<br />

ListLiteral<br />

CompoundDomainLiteral<br />

IntegerLiteral:<br />

UnaryPlus-opt DecimalDigit-list<br />

UnaryMinus-opt DecimalDigit-list<br />

UnaryPlus-opt OctalPrefix OctalDigit-list<br />

UnaryMinus-opt OctalPrefix OctalDigit-list<br />

UnaryPlus-opt HexadecimalPrefix HexadecimalDigit-list<br />

UnaryMinus-opt HexadecimalPrefix HexadecimalDigit-list<br />

UnaryPlus:<br />

+<br />

UnaryMinus:<br />

-<br />

OctalPrefix:<br />

0o<br />

OctalDigit: one of<br />

0 1 2 3 4 5 6 7<br />

DecimalDigit: one of<br />

0 1 2 3 4 5 6 7 8 9<br />

HexadecimalPrefix:<br />

0x<br />

8


HexadecimalDigit: one of<br />

0 1 2 3 4 5 6 7 8 9 A a B b C c D d E e F f<br />

一 个 整 数 文 字 可 以 属 于 整 数 或 无 符 号 数 域 , 它 不 能 超 过 整 数 或 无 符 号 数 的 最 大 和 最 小 值 。<br />

实 数 文 字<br />

RealLiteral:<br />

UnaryMinus-opt DecimalDigit-list FractionOfFloat-opt Exponent-opt<br />

FractionOfFloat:<br />

. DecimalDigit-list<br />

Exponent:<br />

ExponentSymbol ExponentSign-opt DecimalDigit-list<br />

ExponentSymbol: one of<br />

e E<br />

ExponentSign: one of<br />

- +<br />

浮 点 数 文 字 表 示 的 值 也 应 该 不 要 超 过 实 数 所 能 表 示 的 最 大 最 小 值 。<br />

字 符 文 字<br />

CharacterLiteral:<br />

' CharacterValue '<br />

上 面 的 CharacterValue 可 以 是 任 意 可 打 印 字 符 或 一 个 换 码 序 列 :<br />

• \\ 代 表 \<br />

• \t 代 表 制 表 符<br />

• \n 代 表 换 行 符<br />

• \r 代 表 回 车<br />

• \' 表 单 引 号<br />

• \" 代 表 双 引 号<br />

• \uXXXX, 这 里 的 XXXX 应 该 是 四 位 十 六 进 制 数 ,\uXXXX 代 表 相 应 这 个 数 的 Unicode 字 符 。<br />

串 文 字<br />

StringLiteral:<br />

StringLiteralPart-list<br />

StringLiteralPart:<br />

@" AnyCharacter-list-opt "<br />

" CharacterValue-list-opt "<br />

串 文 字 是 由 一 个 或 多 个 StringLiteralPart( 串 文 字 部 件 ) 连 接 构 成 的 。<br />

以 @ 开 头 的 串 文 字 部 件 不 使 用 换 码 序 列 , 除 了 下 述 情 况 :<br />

• "" 代 表 双 引 号 。<br />

而 不 用 @ 开 头 的 串 文 字 部 件 使 用 下 述 换 码 序 列 :<br />

• \\ 代 表 \<br />

• \t 代 表 制 表 符<br />

• \n 代 表 换 行 符<br />

9


• \r 代 表 回 车<br />

• \" 代 表 双 引 号<br />

• \uXXXX, 这 里 的 XXXX 应 该 是 四 位 十 六 进 制 数 , 代 表 相 应 这 个 数 的 Unicode 字 符 。<br />

二 进 制 数 文 字<br />

255。<br />

BinaryLiteral:<br />

$[ ElementValue-comma-sep-list-opt ]<br />

ElementValue:<br />

IntegerLiteral<br />

ElementValue 可 以 是 任 意 整 数 算 术 表 达 式 ( 如 常 数 ), 在 编 译 时 应 该 是 可 计 算 的 , 值 的 范 围 是 0 到<br />

表 文 字<br />

一 个 表 中 的 所 有 元 素 必 须 属 于 同 一 个 域 ( 或 兼 容 域 )。 域 可 以 是 任 何 内 建 (built-in) 的 或 用 户 定 义 域<br />

(user defined domain), 例 如 , 可 以 是 整 数 、 字 符 、 二 进 制 、 复 合 域 等 等 。<br />

ListLiteral:<br />

[ SimpleLiteral-comma-sep-list-opt ]<br />

[ SimpleLiteral-comma-sep-list | ListLiteral ]<br />

这 里 的 SimpleLiteral( 简 单 文 字 ) 可 以 是 :<br />

例 :<br />

复 合 域 文 字<br />

SimpleLiteral:<br />

IntegerLiteral<br />

RealLiteral<br />

CharacterLiteral<br />

StringLiteral<br />

BinaryLiteral<br />

ListLiteral<br />

CompoundDomainLiteral<br />

[] % 空 表<br />

[1,2,3] % 整 数 表<br />

["abc", "defg"] % 串 表<br />

[1,"abc"]<br />

% 这 个 表 不 合 规 定<br />

用 户 定 义 复 合 域 所 有 的 参 数 如 果 都 是 文 字 的 则 也 可 以 当 文 字 看 待 。<br />

编 译 单 元<br />

一 个 程 序 由 若 干 编 译 单 元 构 成 , 编 译 器 对 每 个 编 译 单 元 分 别 进 行 编 译 , 一 个 编 译 结 果 就 是 一 个 目 标 文<br />

件 。 这 些 目 标 文 件 ( 可 能 还 有 其 它 文 件 ) 链 接 在 一 起 , 就 产 生 了 工 程 的 结 果 。 一 个 程 序 必 须 有 一 个 、 也 只<br />

10


能 有 一 个 目 标 段 (goalSection), 它 是 整 个 程 序 的 入 口 。<br />

一 个 编 译 单 元 所 有 的 引 用 名 必 须 是 已 经 在 本 单 元 中 声 明 或 定 义 过 的 , 在 这 个 意 义 上 说 一 个 编 译 单 元 必<br />

须 是 独 立 的 。 接 口 定 义 与 类 声 明 可 以 包 含 在 数 个 编 译 单 元 中 ( 定 义 / 声 明 在 其 所 在 的 包 含 单 元 中 必 须 是 一 致<br />

的 ), 而 类 实 现 ( 定 义 ) 只 能 定 义 在 单 一 的 单 元 中 。 工 程 中 也 必 须 定 义 每 个 声 明 项 , 但 有 些 定 义 也 可 以 在<br />

库 中 , 也 就 是 说 不 一 定 非 要 原 文 定 义 。<br />

一 个 编 译 单 元 ( 其 中 可 能 含 有 #include 指 令 ) 是 一 个 编 译 项 的 序 列 。<br />

CompilationUnit :<br />

CompilationItem-list-opt<br />

一 个 编 译 项 可 以 是 一 个 接 口 、 一 个 类 声 明 、 一 个 类 实 现 、 目 标 段 或 是 一 个 条 件 编 译 项 ( 这 个 要 在 条 件<br />

编 译 中 介 绍 )。<br />

CompilationItem :<br />

Directive<br />

NamespaceEntrance<br />

ConditionalItem<br />

InterfaceDefinition<br />

ClassDeclaration<br />

ClassImplementation<br />

GoalSection<br />

请 参 看 :<br />

• Interfaces 接 口<br />

• Classes 类<br />

• Implementations 实 现<br />

• Namespace Entrances 名 称 空 间 入 口<br />

• Goal Section 目 标 段<br />

• Directives 指 令<br />

• Conditional Item 条 件 项<br />

Interfaces( 接 口 )<br />

一 个 接 口 定 义 规 定 了 一 个 已 经 命 名 对 象 的 类 型 。 接 口 可 以 支 持 其 它 的 接 口 , 详 细 内 容 请 参 看 Supports<br />

限 定 符 。<br />

在 一 个 接 口 中 声 明 的 所 有 谓 词 , 都 是 该 接 口 类 型 对 象 中 的 对 象 成 员 。 接 口 也 是 一 个 可 以 定 义 常 数 和 域<br />

的 全 局 范 围 , 因 此 , 在 一 个 接 口 中 定 义 的 常 数 和 域 , 并 不 是 该 接 口 ( 或 其 对 象 具 有 的 ) 类 型 的 部 件 。<br />

上 述 这 样 的 域 和 常 数 可 以 用 该 接 口 名 限 定 符 interface::constant, 或 是 使 用 open 限 定 符 ( 参 看<br />

Open 限 定 符 ) 在 其 它 范 围 中 引 用<br />

InterfaceDeclaration :<br />

interface IinterfaceName ScopeQualifications Sections end interface<br />

IinterfaceName-opt<br />

InterfaceName :<br />

LowerCaseIdentifier<br />

在 上 述 结 构 中 , 结 尾 处 的 接 口 名 (InterfaceName) 要 是 用 的 话 , 一 定 要 和 结 构 开 始 处 的 相 一 致 。<br />

11


ScopeQualifications 的 类 型 必 须 是 :<br />

• Supports 限 定 符<br />

• Open 限 定 符<br />

Sections 的 类 型 必 须 是 :<br />

• ConstantsSection 常 数 段<br />

• DomainsSection 域 段<br />

• PredicatesSection 谓 词 段<br />

• PredicatesFromInterface 来 自 接 口 的 谓 词<br />

• ConditionalSection 条 件 段<br />

在 条 件 段 中 包 含 的 所 有 段 也 必 须 是 这 些 类 型 的 。<br />

The interface: object( 接 口 : 对 象 )<br />

如 果 一 个 接 口 没 有 显 式 地 支 持 任 何 接 口 , 它 意 味 着 支 持 的 只 有 内 建 接 口 object。<br />

object 是 一 个 空 接 口 , 也 就 是 说 , 它 不 含 有 谓 词 等 。 它 的 主 要 用 途 是 作 为 所 有 对 象 的 一 个 通 用 的 基 础<br />

类 型 。<br />

Open 限 定 符<br />

Open 限 定 符 用 来 使 对 类 级 实 体 的 引 用 更 方 便 。Open 段 使 一 个 范 围 内 的 名 称 进 入 到 另 一 个 范 围 , 因 而<br />

可 以 不 用 限 定 符 地 加 以 引 用 。<br />

Open 对 对 象 成 员 的 名 称 没 有 影 响 , 因 为 它 们 只 能 通 过 对 象 来 访 问 。 但 是 对 类 成 员 、 域 、 函 子 及 常 数<br />

的 名 称 , 可 以 不 加 限 定 符 地 访 问 。<br />

用 这 样 的 方 式 把 名 称 带 入 到 一 个 范 围 时 , 可 能 会 引 起 一 些 名 称 的 混 淆 ( 参 看 Scoping)。<br />

Open 段 只 在 它 们 出 现 的 范 围 内 有 影 响 , 这 尤 其 是 指 在 一 个 类 声 明 中 的 open 段 不 会 影 响 到 类 的 实 现 。<br />

Supports 限 定 符<br />

OpenQualification :<br />

open ScopeName-comma-sep-list<br />

Supports 限 定 符 只 能 用 在 接 口 定 义 (interfaceDefinition) 与 类 实 现 (classImplementation) 中 。<br />

它 有 两 个 作 用 :<br />

• 指 定 用 一 个 接 口 A 扩 展 另 一 个 接 口 B, 因 而 使 A 类 型 的 对 象 是 B 类 型 对 象 的 一 个 子 类<br />

• 声 明 某 个 类 的 对 象 相 对 于 原 来 构 造 类 型 规 定 的 来 说 ,“ 私 下 地 ” 有 了 更 多 的 对 象 类 型<br />

supports 是 可 传 递 的 : 如 果 接 口 A supports 接 口 B, 而 B 又 supports C, 则 A 也 supports C。<br />

如 果 一 个 接 口 没 有 显 式 地 支 持 任 何 接 口 , 则 意 味 着 它 是 支 持 预 定 义 的 接 口 object。<br />

从 功 能 上 说 , 一 个 接 口 支 持 某 个 接 口 一 次 或 多 次 ( 直 接 间 接 都 算 ) 没 有 什 么 差 别 , 但 可 能 会 造 成 对 象<br />

表 示 上 的 差 别 。<br />

在 类 的 实 现 中 使 用 supports 时 , 会 导 致 This 不 仅 可 以 与 构 造 类 型 一 道 使 用 , 也 可 以 用 于 任 意 私 有<br />

支 持 的 对 象 类 型 。<br />

SupportsQualification :<br />

supports InterfaceName-comma-sep-list<br />

注 意 , 如 果 各 接 口 间 有 相 互 冲 突 的 谓 词 , 这 样 的 接 口 就 不 能 使 用 同 一 个 supports 限 定 符 。<br />

12


谓 词 冲 突 , 是 指 来 自 不 同 本 原 接 口 的 谓 词 有 相 同 的 名 称 及 变 元 数 。 而 本 原 接 口 , 是 指 谓 词 被 原 文 声 明<br />

的 接 口 , 它 是 相 对 于 用 supports 限 定 符 间 接 声 明 的 接 口 而 言 的 。<br />

因 此 , 如 果 一 个 接 口 在 supports 链 中 出 现 了 两 次 或 多 次 , 一 定 小 心 不 要 引 起 冲 突 。<br />

例 来 看 一 下 下 面 的 定 义 与 声 明 :<br />

interface aaa<br />

predicates<br />

insert : (integer X).<br />

end interface<br />

interface bbb<br />

supports aaa<br />

predicates<br />

insert : (integer X, string Comment).<br />

end interface<br />

interface cc<br />

supports aaa<br />

predicates<br />

extract : () -> integer.<br />

end interface<br />

interface dd<br />

supports aaa, bbb, cc<br />

predicates<br />

extract : (string Comment) -> integer.<br />

end interface<br />

下 面 是 在 dd 中 找 到 的 全 部 谓 词 的 表 ( 按 深 度 遍 历 ):<br />

predicates<br />

insert : (integer).<br />

% dd -> aaa<br />

insert : (integer).<br />

% dd -> bbb -> aaa<br />

insert : (integer, string). % dd -> bbb<br />

insert : (integer).<br />

% dd -> cc -> aaa<br />

extract : () -> integer. % dd -> cc<br />

extract: : (string) -> integer. % dd<br />

这 里 有 些 谓 词 是 相 同 的 , 因 此 实 际 上 dd 中 含 有 的 成 员 如 下 :<br />

predicates<br />

insert : (integer).<br />

% 本 原 接 口 : aaa<br />

insert : (integer, string). % 本 原 接 口 : bbb<br />

extract : () -> integer. % 本 原 接 口 : cc<br />

extract : (string) -> integer. % 本 原 接 口 : dd<br />

例 考 虑 如 下 接 口 :<br />

interface aaa<br />

predicates<br />

insert : (integer X).<br />

end interface<br />

interface bbb<br />

13


predicates<br />

insert : (integer X).<br />

end interface<br />

interface cc<br />

supports aaa, bbb<br />

end interface<br />

% 有 冲 突 的 接 口<br />

接 口 cc 是 不 合 法 的 , 因 为 在 aaa 中 支 持 的 insert/1 以 aaa 为 本 原 接 口 , 而 在 bbb 中 支 持 的 insert/1<br />

以 bbb 为 本 原 接 口 。<br />

Classes( 类 )<br />

类 声 明 向 外 界 定 义 了 类 的 外 观 , 外 界 可 以 看 到 和 准 确 地 使 用 类 声 明 中 记 述 过 的 实 体 。 我 们 说 , 类 声 明<br />

规 定 了 类 的 公 有 部 分 。<br />

类 声 明 可 以 含 有 常 数 和 域 定 义 , 以 及 谓 词 声 明 。<br />

如 果 一 个 类 说 明 了 一 个 构 造 类 型 ConstructionType, 则 它 可 以 构 造 此 种 类 型 的 对 象 。 可 以 构 造 对<br />

象 的 类 至 少 有 一 个 构 造 器 , 但 也 可 以 声 明 多 个 。 没 有 显 式 声 明 任 何 构 造 器 的 类 , 都 自 动 地 拥 有 缺 省 构 造 器<br />

( 即 new/0)。<br />

对 象 是 通 过 调 用 类 的 构 造 器 构 造 出 来 的 。 在 初 始 化 继 承 的 类 时 也 要 用 构 造 器 。<br />

类 声 明 中 所 提 到 的 一 切 是 属 于 类 的 , 而 不 是 属 于 它 所 构 造 的 对 象 的 。 与 对 象 有 关 的 一 切 , 都 要 在 这 个<br />

类 所 构 造 的 对 象 的 构 造 类 型 中 声 明 。<br />

任 何 类 声 明 ClassDeclaration 都 必 须 伴 有 类 实 现 classImplementation。 在 类 声 明 中 声 明 的 谓 词 的<br />

定 义 / 实 现 是 由 类 实 现 提 供 的 。 同 样 , 由 类 构 造 的 对 象 所 支 持 的 谓 词 定 义 也 是 由 类 实 现 提 供 的 。 这 两 种 谓 词<br />

都 可 以 由 子 句 实 现 , 但 对 象 谓 词 还 可 从 其 它 类 继 承 来 实 现 。<br />

需 要 特 别 指 出 , 类 声 明 对 代 码 继 承 没 有 作 任 何 说 明 。 代 码 继 承 完 全 是 “ 私 事 儿 ”, 只 能 在 类 实 现 中 说<br />

明 ( 这 与 许 多 其 它 面 向 对 象 的 语 言 不 同 , 是 要 在 implementation 中 隐 藏 所 有 的 实 现 细 节 )。<br />

如 果 类 没 有 说 明 构 造 类 型 ConstructionType, 则 这 个 类 不 能 产 生 任 何 对 象 ; 此 时 这 个 类 的 角 色 与<br />

其 说 是 “ 类 ” 还 不 如 说 是 个 模 块 更 恰 当 。<br />

ClassDeclaration :<br />

class ClassName ConstructionType-opt ScopeQualifications Sections end class<br />

ClassName-opt<br />

ConstructionType :<br />

: InterfaceName<br />

ClassName :<br />

LowerCaseIdentifier<br />

类 声 明 结 尾 处 的 ClassName( 如 果 要 写 的 话 ) 必 须 与 开 头 的 那 个 完 全 一 样 。<br />

允 许 用 类 的 名 字 ClassName 指 定 该 类 构 造 类 型 的 接 口 名 字 ConstructionType, 就 是 说 可 以 这 样 :<br />

class interfaceAndClassName : interfaceAndClassName<br />

要 注 意 , 类 和 接 口 都 可 以 声 明 域 和 常 数 , 它 们 之 间 必 须 没 有 冲 突 , 因 为 它 们 都 是 在 相 同 的 名 称 空 间 的<br />

( 它 们 只 能 由 相 同 的 接 口 或 类 的 名 称 来 限 定 了 )。<br />

ScopeQualifications 只 能 用 open 限 定 符 。<br />

14


Sections 必 须 是 下 面 的 种 类 :<br />

• Constants 段<br />

• domains 段<br />

• predicates 段<br />

• constructors 段<br />

• 条 件 段<br />

只 有 当 类 中 说 明 了 ConstructionType 时 ,constructors 段 才 是 合 法 的 。<br />

在 条 件 (conditional) 段 中 的 所 有 段 也 必 须 是 这 些 种 类 的 。<br />

Implementations ( 实 现 )<br />

一 个 类 的 实 现 , 用 于 提 供 在 该 类 声 明 中 声 明 的 谓 词 和 构 造 器 的 定 义 , 以 及 由 这 个 类 构 造 的 对 象 支 持 的<br />

谓 词 的 定 义 。<br />

类 可 以 私 有 地 ( 即 在 其 实 现 中 ) 声 明 和 定 义 比 类 声 明 中 提 到 的 更 多 的 实 体 。 特 别 是 , 在 实 现 里 可 以 声<br />

明 事 实 数 据 库 , 而 事 实 数 据 库 可 以 用 于 承 载 类 和 对 象 的 状 态 。<br />

实 现 是 一 个 混 合 的 范 围 , 它 既 包 含 了 类 的 实 现 , 也 包 含 了 类 产 生 的 对 象 的 实 现 。 类 的 类 部 分 是 由 类 的<br />

所 有 对 象 共 享 的 , 而 对 象 部 分 , 则 仅 属 于 各 个 对 象 。 类 部 分 和 对 象 部 分 都 可 以 含 有 事 实 与 谓 词 , 而 域 、 函<br />

子 和 常 数 则 总 是 属 于 类 部 分 , 即 , 它 们 不 是 属 于 哪 个 对 象 个 体 的 。<br />

缺 省 时 , 所 有 在 一 个 类 实 现 中 声 明 的 谓 词 和 事 实 成 员 是 对 象 成 员 。 要 声 明 一 个 类 成 员 , 必 须 在 段 关 键<br />

字 ( 如 predicates、facts) 前 加 关 键 字 class。 在 这 样 的 段 中 声 明 的 所 有 成 员 是 类 成 员 。<br />

类 成 员 可 以 引 用 类 的 类 部 分 , 但 不 能 引 用 对 象 部 分 。<br />

而 另 一 方 面 , 对 象 成 员 既 可 以 访 问 类 的 类 部 分 也 可 以 访 问 类 的 对 象 部 分 。<br />

在 实 现 中 的 代 码 , 所 有 者 对 象 为 全 体 对 象 谓 词 所 包 容 。 而 包 容 的 所 有 者 对 象 , 可 以 直 接 由 特 殊 的 变 量<br />

This 来 访 问 。<br />

ClassImplementation :<br />

implement ClassName ScopeQualifications Sections end implement<br />

ClassName-opt<br />

类 实 现 结 尾 处 的 ClassName( 如 果 要 写 的 话 ) 必 须 与 类 实 现 开 头 的 一 致 。<br />

ScopeQualifications 必 须 是 如 下 的 种 类 :<br />

• supports 限 定 符<br />

• open 限 定 符<br />

• inherit 限 定 符<br />

• delegate 限 定 符<br />

supports 限 定 符 后 列 出 一 个 接 口 表 , 表 示 由 该 类 实 现 私 有 支 持 的 接 口 。<br />

delegate 限 定 符 委 派 来 自 接 口 的 对 象 谓 词 功 能 给 来 自 对 象 的 谓 词 , 它 可 以 如 同 事 实 变 量 一 样 地 存 储 。<br />

Sections 必 须 是 如 下 种 类 :<br />

• constants 段<br />

• resolve 限 定 符<br />

• delegate 限 定 符<br />

• domains 段<br />

• predicates 段<br />

• constructors 段<br />

• facts 段<br />

15


• clauses 段<br />

• conditional 段<br />

constructors 段 只 有 在 ClassName 类 说 明 了 constructionType 时 才 是 合 法 的 。<br />

说 明 ConstructionType 的 子 句 也 是 对 象 的 构 造 器 , 它 按 规 定 的 构 造 类 型 构 造 对 象 。<br />

例 下 面 这 个 例 子 , 要 说 明 类 事 实 在 类 的 对 象 间 共 享 , 而 对 象 事 实 则 不 是 共 享 的 。<br />

设 有 接 口 aa 和 类 aa_class:<br />

interface aa<br />

predicates<br />

setClassFact : (integer Value).<br />

getClassFact : () -> integer.<br />

setObjectFact : (integer Value).<br />

getObjectFact : () -> integer.<br />

end interface<br />

class aa_class : aa<br />

end class<br />

这 些 谓 词 分 别 从 类 事 实 与 对 象 事 实 中 存 取 值 :<br />

implement aa_class<br />

class facts<br />

classFact : integer := 0.<br />

facts<br />

objectFact : integer := 0.<br />

clauses<br />

setClassFact(Value) :-<br />

classFact := Value.<br />

getClassFact() = classFact.<br />

clauses<br />

setObjectFact(Value) :-<br />

objectFact := Value.<br />

getObjectFact() = objectFact.<br />

end implement aa_class<br />

设 有 目 标 为 :<br />

goal<br />

A1 = aa_class::new(),<br />

A2 = aa_class::new(),<br />

A1:setClassFact(1),<br />

A1:setObjectFact(2),<br />

ClassFact = A2:getClassFact(),<br />

ObjectFact = A2:getObjectFact().<br />

类 事 实 是 由 所 有 对 象 共 享 的 , 所 以 用 A1 设 置 类 事 实 会 影 响 到 由 A2 取 得 的 值 , 因 而 ,ClassFact 的<br />

值 是 用 A1 设 置 的 那 个 值 。<br />

与 此 不 同 的 是 , 对 象 事 实 只 属 于 各 个 对 象 。 因 此 , 在 A1 中 设 置 的 对 象 事 实 不 会 影 响 存 储 在 A2 中 的<br />

值 。 这 样 ,ObjectFact 的 值 是 零 , 这 个 值 是 A2 初 始 化 时 的 值 。<br />

Construction( 构 造 )<br />

16


本 节 描 述 对 象 构 造 , 因 而 也 只 涉 及 那 些 构 造 对 象 的 子 句 。<br />

对 象 是 通 过 调 用 构 造 器 (constructor ) 构 造 出 来 的 。 而 构 造 器 则 是 在 类 的 声 明 和 实 现 中 的<br />

constructors 段 显 式 地 声 明 的 ( 参 见 缺 省 构 造 器 )。<br />

一 个 构 造 器 有 两 个 关 联 的 谓 词 :<br />

• 一 个 返 回 新 构 造 的 对 象 的 类 函 数<br />

• 一 个 用 于 初 始 化 继 承 对 象 的 对 象 谓 词<br />

相 关 的 对 象 谓 词 用 于 进 行 对 象 的 初 始 化 , 这 个 谓 词 只 能 在 本 身 的 类 和 继 承 的 类 中 由 构 造 器 来 调 用 ( 也<br />

就 是 基 于 类 的 初 始 化 )。<br />

相 关 的 类 函 数 是 隐 含 定 义 的 , 也 就 是 说 在 哪 儿 都 没 有 它 的 子 句 。 这 个 类 函 数 分 配 内 存 来 存 放 对 象 、 进<br />

行 对 象 的 初 始 化 并 调 用 对 象 构 造 器 来 创 建 对 象 。 最 后 , 作 为 构 造 器 的 执 行 结 果 , 返 回 构 造 的 对 象 。 因 此 ,<br />

在 构 造 器 的 子 句 被 调 用 之 前 :<br />

• 所 有 具 有 初 始 化 表 达 式 的 对 象 事 实 变 量 要 被 初 始 化<br />

• 所 有 具 有 子 句 的 对 象 事 实 要 从 这 些 子 句 开 始 被 初 始 化<br />

在 构 造 器 的 子 句 被 调 用 之 前 , 这 个 初 始 化 也 会 传 递 地 进 行 到 所 有 继 承 的 子 对 象 。<br />

构 造 器 子 句 必 须 要 :<br />

• 初 始 化 那 些 在 进 入 之 前 没 有 初 始 化 的 single 对 象 事 实 及 对 象 事 实 变 量<br />

• 初 始 化 所 有 子 对 象<br />

构 造 器 子 句 还 可 以 做 些 其 它 的 事 , 但 它 必 须 完 成 上 述 初 始 化 工 作 , 以 确 保 对 象 构 造 后 是 有 效 的 。<br />

注 意 : 在 构 造 期 间 对 象 可 能 是 无 效 的 , 要 避 免 访 问 未 初 始 化 的 对 象 部 分 ( 参 看 构 造 对 象 的 规 则 )。<br />

缺 省 构 造 器<br />

缺 省 构 造 器 不 需 要 输 入 参 数 , 名 为 new/0。 如 果 构 造 对 象 的 类 在 其 类 声 明 中 没 有 声 明 任 何 构 造 器 ,<br />

则 意 味 着 在 类 声 明 部 分 隐 含 地 声 明 了 缺 省 的 构 造 器 ( 即 new/0)。 这 也 意 味 着 每 个 类 至 少 有 一 个 构 造 器 ,<br />

因 此 , 这 样 写 :<br />

class aaa<br />

end class aaa<br />

与 下 面 的 写 法 , 结 果 是 一 样 的 :<br />

class aaa<br />

constructors<br />

new : ().<br />

end class aaa<br />

显 式 地 重 新 声 明 缺 省 构 造 器 也 是 合 法 的 。<br />

不 必 定 义 ( 也 就 是 说 , 实 现 ) 缺 省 构 造 器 , 如 果 不 定 义 , 则 隐 含 地 假 设 有 一 个 没 有 什 么 影 响 的 定 义 。<br />

因 此 , 如 果 有 一 个 构 造 器 aaa, 写 :<br />

implement aaa<br />

end implement aaa<br />

与 写 成 下 面 这 样 是 一 样 的 :<br />

implement aaa<br />

clauses<br />

new().<br />

17


end implement aaa<br />

注 意 , 只 是 在 下 面 这 样 的 情 况 中 , 类 才 有 一 个 缺 省 构 造 器 :<br />

• 这 个 类 没 有 公 开 地 声 明 任 何 构 造 器<br />

• 或 是 声 明 了 一 个 new/0 作 为 构 造 器<br />

也 可 以 反 过 来 说 , 如 果 是 下 述 情 况 , 一 个 类 就 没 有 缺 省 的 构 造 器 :<br />

• 这 个 类 公 开 地 声 明 了 构 造 器<br />

• 它 声 明 的 不 是 以 new/0 作 为 构 造 器<br />

例 设 有 接 口 aa, 考 查 下 面 的 代 码 :<br />

class aa_class : aa<br />

end class<br />

类 aa_class 没 有 声 明 构 造 器 , 所 以 , 它 隐 含 地 声 明 了 缺 省 构 造 器 。 因 而 , 可 以 这 样 创 建 一 个 aa_class<br />

类 的 对 象 :<br />

goal<br />

_A = aa_class::new().<br />

% 隐 含 声 明 的 缺 省 构 造 器<br />

例 实 现 隐 含 声 明 的 aa_class 类 的 缺 省 构 造 器 是 合 法 的 :<br />

implement aa_class<br />

clauses<br />

new() :-<br />

...<br />

end implement<br />

例 bb_class 类 显 式 地 声 明 了 一 个 构 造 器 , 因 为 它 不 是 缺 省 构 造 器 , 这 个 类 就 没 有 缺 省 构 造 器 。<br />

class bb_class : aa<br />

constructors<br />

newFromFile : (file File).<br />

end class<br />

例 cc_class 类 声 明 了 构 造 器 newFromFile/1, 但 同 时 也 声 明 了 缺 省 构 造 器 new/0, 所 以 显 然<br />

它 有 缺 省 构 造 器 new/0:<br />

class cc_class : aa<br />

constructors<br />

new : ().<br />

newFromFile : (file File).<br />

end class<br />

% 缺 省 构 造 器<br />

私 有 构 造 器<br />

在 类 实 现 里 , 还 可 以 声 明 “ 私 有 的 ” 构 造 器 。 在 下 面 的 情 况 中 , 可 能 是 有 这 个 需 要 的 :<br />

1. 当 某 个 谓 词 要 返 回 一 个 construction type 的 对 象 时 , 在 类 实 现 中 就 可 以 声 明 、 实 现 并 调 用<br />

“ 私 有 的 ” 构 造 器 来 创 建 这 样 的 对 象<br />

2. 当 某 个 类 里 声 明 了 几 个 “public” 的 构 造 器 、 而 这 些 构 造 器 中 有 很 多 相 同 部 分 时 , 用 一 个 在<br />

类 实 现 中 声 明 的 “ 私 有 的 ” 构 造 器 来 实 现 这 个 “ 相 同 部 分 ” 也 是 合 理 的 。 所 有 这 些 公 共 的 构<br />

18


造 器 的 子 句 只 需 要 调 用 这 个 私 有 的 构 造 器 来 实 现 “ 相 同 部 分 ”。<br />

注 意 , 在 一 个 可 以 构 造 对 象 的 类 中 , 如 果 在 类 声 明 中 没 有 声 明 任 何 构 造 器 , 则 意 味 着 隐 含 地 声 明 了 缺<br />

省 构 造 器 new/0, 这 和 在 类 实 现 中 是 否 声 明 私 有 构 造 器 无 关 。 这 样 一 来 , 可 以 这 样 做 :<br />

子 对 象 的 构 造<br />

interface aa<br />

end interface<br />

class aa : aa<br />

end class<br />

implement aa<br />

constructors<br />

myCreate : ().<br />

clauses<br />

myCreate() :-<br />

...<br />

end implement<br />

% 程 序 代 码<br />

...<br />

Obj = aa::new(), % 这 是 隐 含 声 明 的 缺 省 的 类 构 造 器<br />

...<br />

所 有 构 造 器 都 要 负 责 把 构 造 的 对 象 初 始 化 成 有 效 的 状 态 。 为 要 得 到 这 样 的 有 效 状 态 , 所 有 子 对 象 ( 即<br />

继 承 的 类 ) 也 必 须 初 始 化 。<br />

子 对 象 的 初 始 化 可 以 用 下 述 二 种 方 法 之 一 进 行 : 程 序 员 调 用 被 继 承 类 的 构 造 器 , 或 是 自 动 地 调 用 缺 省<br />

构 造 器 。 后 者 要 求 被 继 承 类 确 实 有 一 个 缺 省 构 造 器 , 这 个 缺 省 构 造 器 是 显 式 声 明 或 是 隐 含 声 明 的 无 关 紧 要 。<br />

如 果 被 继 承 类 没 有 缺 省 构 造 器 , 就 必 须 显 式 地 调 用 别 的 构 造 器 。 被 继 承 类 构 造 器 的 缺 省 调 用 时 刻 是 紧<br />

接 着 初 始 化 事 实 变 量 和 事 实 的 子 句 之 后 , 在 进 入 到 构 造 器 子 句 之 前 。<br />

被 继 承 类 的 构 造 器 调 用 是 按 不 返 回 值 的 模 式 进 行 的 。 如 果 按 返 回 值 的 版 本 来 做 调 用 , 实 际 上 就 创 建 了<br />

一 个 对 象 , 而 不 是 对 This 调 用 构 造 器 ( 参 看 下 面 的 例 子 )。<br />

例 这 个 bb_class 类 的 实 现 继 承 自 类 aa_class,bb_class 类 的 缺 省 构 造 器 调 用 aa_class 类 的<br />

一 个 构 造 器 , 创 建 一 个 新 的 cc_class 类 的 对 象 :<br />

implement bb_class inherits aa_class<br />

clauses<br />

new() :-<br />

C = cc_class::new(), % 创 建 一 个 cc_class 的 对 象<br />

aa_class::newC(C).<br />

...<br />

end implement<br />

% 对 继 承 的 子 对 象 调 用 构 造 器<br />

例 如 果 基 类 不 是 显 式 构 造 的 , 那 它 就 是 用 缺 省 构 造 器 隐 含 构 造 的 , 这 样 的 话 , 写 :<br />

implement bbb<br />

inherits aaa<br />

clauses<br />

myNew() :-<br />

doSomething().<br />

end implement bbb<br />

和 下 面 的 写 法 是 一 样 的 :<br />

19


implement bbb<br />

inherits aaa<br />

clauses<br />

myNew() :-<br />

aaa::new(),<br />

doSomething().<br />

end implement bbb<br />

如 果 aaa 没 有 缺 省 构 造 器 , 这 当 然 就 会 出 错 。<br />

注 意 , 这 个 规 则 当 然 也 可 以 和 前 面 在 缺 省 构 造 器 讨 论 过 的 规 则 结 合 起 来 , 写<br />

implement bbb<br />

inherits aaa<br />

end implement bbb<br />

与 写 成 下 面 的 样 子 ( 根 据 前 面 缺 省 构 造 器 在 中 讨 论 过 的 规 则 ) 是 完 全 相 同 的 :<br />

implement bbb<br />

inherits aaa<br />

clauses<br />

new().<br />

end implement bbb<br />

与 写 成 下 面 的 样 子 ( 按 刚 讨 论 过 的 规 则 ) 也 是 相 同 的 :<br />

implement bbb<br />

inherits aaa<br />

clauses<br />

new() :-<br />

aaa::new().<br />

end implement bbb<br />

single( 对 象 ) 事 实 的 初 始 化<br />

如 同 所 有 构 造 器 必 须 要 初 始 化 / 构 造 子 对 象 一 样 , 它 也 必 须 要 在 第 一 次 引 用 之 前 初 始 化 一 个 对 象 的 所 有<br />

的 事 实 。<br />

注 意 ,single 类 事 实 只 能 由 子 句 初 始 化 , 因 为 它 们 不 是 与 哪 个 对 象 关 联 的 , 类 事 实 在 第 一 个 对 象 创<br />

建 之 前 就 可 以 访 问 了 。<br />

例 这 个 例 子 表 明 (1) 如 何 用 表 达 式 初 始 化 一 个 事 实 变 量 ;(2) 如 何 用 子 句 初 始 化 一 个 single 事 实<br />

(point);(3) 如 何 在 构 造 器 里 初 始 化 一 个 single 事 实 ;(4) 在 哪 儿 调 用 被 继 承 类 的 缺 省 构 造 器 :<br />

implement bb_class inherits aa_class<br />

facts<br />

counter : integer := 0.<br />

point : (integer X, integer Y) single.<br />

c : (cc C) single.<br />

clauses<br />

point(0, 1).<br />

% 创 建 了 对 象 ,counter 和 point 在 进 入 之 前 初 始 化 了<br />

% 缺 省 构 造 器 aa_class::new/0 在 进 入 之 前 也 被 调 用 了<br />

20


new() :-<br />

C = cc_class::new(),<br />

assert(c(C)).<br />

...<br />

end implement<br />

委 托 构 造<br />

除 了 直 接 在 构 造 器 中 构 造 一 个 对 象 , 这 个 工 作 也 可 以 委 托 给 同 一 个 类 的 其 它 构 造 器 。 这 只 需 要 调 用 其<br />

它 的 构 造 器 ( 不 返 回 值 的 版 本 ) 就 可 以 了 。 委 托 构 造 时 , 必 须 要 确 保 对 象 真 的 被 构 造 了 , 而 且 还 不 能 重 复<br />

构 造 。single 事 实 可 以 多 次 赋 值 , 所 以 不 会 构 造 重 复 了 , 而 继 承 的 类 , 只 能 在 对 象 构 造 时 进 行 一 次 初 始 化 。<br />

例 这 个 例 子 表 明 了 委 托 构 造 的 一 个 典 型 应 用 , 构 造 器 new/0 用 缺 省 值 调 用 另 一 个 构 造 器<br />

newFromC/1。<br />

构 造 对 象 的 规 则<br />

implement aa_class<br />

facts<br />

c : (cc C) single.<br />

clauses<br />

new() :-<br />

C = cc_class::new(),<br />

newFromC(C).<br />

newFromC(C) :-<br />

assert(c(C)).<br />

...<br />

end implement<br />

程 序 员 必 须 保 证 :<br />

• 所 有 子 对 象 只 被 初 始 化 / 构 造 了 一 次<br />

• 所 有 single 事 实 被 初 始 化 了 ( 至 少 一 次 )<br />

• 在 初 始 化 / 构 造 之 前 没 有 子 对 象 被 引 用<br />

• 在 初 始 化 之 前 没 有 single 事 实 被 使 用<br />

在 编 译 时 编 译 器 对 这 些 问 题 的 检 测 是 没 有 什 么 保 证 的 。 编 译 器 可 能 会 给 出 一 个 运 行 时 确 认 , 也 可 能 会<br />

不 安 全 地 忽 略 这 样 的 运 行 时 确 认 。<br />

This<br />

对 象 谓 词 总 是 被 应 用 在 一 个 对 象 上 , 这 个 对 象 带 有 对 象 事 实 并 为 对 象 谓 词 的 实 现 所 包 容 。 对 象 谓 词 可<br />

以 访 问 到 这 个 暗 含 的 对 象 , 我 们 称 这 个 对 象 为 This。 可 以 隐 式 和 显 式 两 种 方 式 访 问 This。<br />

显 式 This<br />

在 每 个 对 象 谓 词 的 每 个 子 句 中 , 变 量 This 都 是 隐 含 定 义 了 的 并 绑 定 于 This, 也 就 是 其 成 员 谓 词 正 在<br />

执 行 的 那 个 对 象 。<br />

隐 式 This<br />

21


在 一 个 对 象 成 员 谓 词 的 子 句 里 , 可 以 直 接 调 用 其 它 对 象 成 员 谓 词 , 因 为 隐 含 地 设 定 了 This 来 进 行 这 种<br />

操 作 。 超 类 的 成 员 也 可 以 直 接 被 调 用 , 只 要 需 要 调 用 的 方 法 是 明 确 的 就 行 ( 参 看 范 围 与 可 见 性 )。 同 样 ,<br />

也 可 以 访 问 存 储 在 This 中 的 对 象 事 实 。<br />

This 与 继 承<br />

[ 注 意 这 一 段 的 内 容 有 可 能 发 生 变 化 , 因 为 关 于 语 言 的 这 个 问 题 正 打 算 作 更 动 。]<br />

This 指 的 对 象 , 总 是 属 于 在 使 用 This 的 那 个 类 的 , 如 果 这 个 类 是 被 其 它 的 类 继 承 的 , 仍 然 如 此 。<br />

设 接 口 aa 声 明 如 下 :<br />

interface aa<br />

predicates<br />

action : ().<br />

doAction : ().<br />

end interface<br />

而 类 aa_class 声 明 如 下 :<br />

其 实 现 是 :<br />

则 如 下 目 标 :<br />

写 出 来 的 是 :<br />

class aa_class : aa<br />

end class<br />

implement aa_class<br />

clauses<br />

action() :-<br />

doAction(),<br />

This:doAction().<br />

doAction() :-<br />

write("aa_class::doAction"), nl.<br />

end implement<br />

goal<br />

A = aa_class::new(),<br />

A:action().<br />

aa_class::doAction<br />

aa_class::doAction<br />

再 来 考 虑 如 下 声 明 的 类 bb_class:<br />

其 实 现 是 :<br />

class bb_class : aa<br />

end class<br />

implement bb_class inherits aa_class<br />

clauses<br />

doAction() :-<br />

22


write("bb_class::doAction"), nl.<br />

end implement<br />

这 样 的 目 标 :<br />

goal<br />

B = bb_class::new(),<br />

B:action().<br />

写 出 来 的 也 是 :<br />

aa_class::doAction<br />

aa_class::doAction<br />

因 为 在 类 aa_class 中 ( 显 式 和 隐 式 ) 的 This 都 指 的 是 类 aa_class 的 对 象 。<br />

现 在 来 看 另 一 个 例 子 :<br />

interface iName<br />

predicates<br />

className : () -> string Class.<br />

name: () -> string Class.<br />

nameThis: () -> string Class.<br />

end interface iName<br />

class aaa : iName<br />

end class aaa<br />

implement aaa<br />

clauses<br />

className() = "aaa".<br />

clauses<br />

name() = className().<br />

clauses<br />

nameThis() = This:className().<br />

end implement aaa<br />

class bbb : iName<br />

end class bbb<br />

implement bbb<br />

inherits aaa<br />

clauses<br />

className() = "bbb".<br />

end implement bbb<br />

goal<br />

OOO = bbb::new(),<br />

Class1 = OOO:name(),<br />

Class2 = OOO:nameThis().<br />

bbb 类 从 aaa 继 承 了 name 和 nameThis 的 定 义 , 但 再 实 现 了 className。 在 目 标 中 我 们 创 建 了<br />

一 个 bbb 对 象 , 在 它 的 基 础 上 我 们 调 用 name 和 nameThis。<br />

name 谓 词 直 接 调 用 className, 而 nameThis 调 用 This:className, 它 们 的 效 果 是 不 同 的 。<br />

name 谓 词 调 用 的 className 是 在 aaa 中 定 义 的 。 但 nameThis 调 用 的 className 则 是 定 义 在 This<br />

23


上 的 , 其 实 就 是 bbb::className, 因 为 This 是 一 个 bbb 对 象 。<br />

总 之 ,Class1 的 绑 定 值 是 “aaa”, Class2 而 则 是 “bbb”。<br />

像 nameThis 那 样 在 父 类 中 调 用 子 类 , 是 在 共 享 父 类 中 实 现 一 般 功 能 的 常 用 方 法 。 而 一 般 功 能 则 要<br />

依 靠 子 类 的 对 非 一 般 功 能 的 提 炼 区 分 。<br />

再 看 一 下 这 个 类 :<br />

interface iTest<br />

predicates<br />

test : () -> string Name.<br />

end interface iTest<br />

class ccc : iTest<br />

end class ccc<br />

implement ccc<br />

inherits aaa<br />

clauses<br />

test() = aaa::nameThis().<br />

end implement ccc<br />

ccc 也 继 承 于 aaa, 因 此 这 也 就 意 味 着 ccc 支 持 接 口 iName( 私 有 地 )。 当 aaa 调 用 This:className<br />

时 , 它 调 用 的 是 ccc 提 供 的 东 西 , 而 这 刚 好 又 是 继 承 自 aaa 的 。<br />

再 看 下 面 这 个 例 子 :<br />

class ddd : name<br />

end class ddd<br />

implement ddd<br />

inherits bbb<br />

clauses<br />

className() = "ddd".<br />

end implement ddd<br />

goal<br />

OOO = ddd::new(),<br />

Class1 = OOO:name(),<br />

Class2 = OOO:nameThis().<br />

ddd 继 承 自 bbb, 而 后 者 是 继 承 自 aaa 的 。 当 调 用 nameThis 时 其 实 调 用 的 是 aaa::nameThis,<br />

而 它 调 用 的 是 This:className。 在 这 里 ,This 是 ddd 对 象 , 因 而 实 际 的 调 用 成 了 ddd::className。<br />

总 之 , 对 This:className 的 调 用 最 后 指 向 支 持 含 有 className 的 接 口 的 子 类 。<br />

Inherits 限 定 符<br />

这 个 限 定 符 用 于 说 明 一 个 实 现 是 继 承 自 一 个 或 多 个 类 的 。 继 承 只 对 类 的 对 象 部 分 有 影 响 。<br />

由 其 它 的 类 继 承 的 目 的 , 是 要 从 那 些 类 中 继 承 行 为 。 当 cc 类 继 承 aa 类 , 意 味 着 cc 类 的 实 现 自 动 隐 含<br />

地 ( 私 有 的 ) 支 持 aa 类 的 构 造 类 型 ( 接 口 )。 当 然 , 如 果 类 aa 的 实 现 已 经 显 式 地 支 持 了 cc 的 构 造 类 型 ,<br />

它 们 之 间 就 没 什 么 分 别 了 。<br />

因 此 , 要 注 意 同 一 个 谓 词 , 比 如 说 p, 不 能 在 cc 类 的 构 造 类 型 接 口 中 声 明 又 在 被 继 承 的 aa 类 构 造 类 型<br />

接 口 中 声 明 ( 编 译 器 能 检 测 出 这 个 问 题 并 产 生 错 误 报 告 说 一 个 谓 词 在 两 个 地 方 作 了 声 明 )。 对 这 个 问 题 再<br />

稍 微 仔 细 地 讨 论 一 下 。 假 设 cc 类 有 一 个 构 造 类 型 接 口 cci, 而 另 一 个 aa 类 有 一 个 构 造 类 型 接 口 aai。 在 aai<br />

和 cci 接 口 中 都 声 明 同 一 个 谓 词 p, 只 要 aa 类 和 cc 类 独 立 的 , 编 译 器 都 不 会 检 测 出 问 题 。 但 一 旦 声 明 cc 类<br />

24


继 承 aa 类 , 则 cc 类 也 开 始 支 持 aai 接 口 。 这 样 一 来 ,cc 类 就 遇 到 了 两 个 p 谓 词 的 声 明 , 这 个 问 题 就 会 产 生<br />

编 译 时 的 错 误 。 要 避 免 谓 词 p 声 明 的 这 种 不 确 定 性 , 只 能 在 cci 接 口 声 明 中 使 用 Predicates from Interface<br />

段 , 如 下 :<br />

interface cci<br />

predicates from aai<br />

p(), ...<br />

end interface cci<br />

对 象 谓 词 可 以 被 继 承 : 如 果 一 个 类 没 有 实 现 某 个 它 的 对 象 谓 词 , 但 它 所 继 承 的 一 个 类 实 现 了 这 个 谓 词 ,<br />

则 这 个 谓 词 就 可 以 用 于 这 个 类 。<br />

继 承 的 类 对 被 继 承 的 类 没 有 什 么 特 权 , 它 也 只 能 通 过 该 构 造 类 型 接 口 访 问 其 中 的 对 象 。<br />

继 承 必 须 是 明 确 的 。 如 果 一 个 类 自 己 定 义 了 谓 词 , 这 不 会 含 糊 , 因 为 这 个 谓 词 定 义 是 显 而 易 见 的 。 如<br />

果 只 有 一 个 被 继 承 类 支 持 这 个 谓 词 , 这 也 不 会 有 问 题 。 但 是 , 如 果 有 两 个 或 多 个 类 支 持 这 个 谓 词 , 是 哪 个<br />

类 提 供 的 定 义 就 模 糊 了 。 此 时 , 必 须 用 一 个 resolve 限 定 符 来 解 决 这 个 模 糊 问 题 ( 参 看 Resolve 限 定 符 )。<br />

在 当 前 类 的 对 象 谓 词 中 可 以 直 接 调 用 来 自 被 继 承 类 的 对 象 谓 词 , 因 为 隐 含 使 用 了 作 为 谓 词 所 有 者 的 内<br />

建 子 对 象 。 类 限 定 符 可 解 决 来 自 被 继 承 类 的 对 象 谓 词 调 用 模 糊 问 题 。<br />

Resolve 限 定 符<br />

InheritsQualification :<br />

inherits ClassName-comma-sep-list<br />

已 经 提 到 过 关 于 调 用 谓 词 的 模 糊 问 题 , 可 以 通 过 使 用 指 定 名 称 来 避 免 。 但 当 有 继 承 情 况 出 现 时 , 这 也<br />

还 有 问 题 。 看 下 面 这 个 例 子 :<br />

interface aa<br />

predicates<br />

p : () procedure ().<br />

...<br />

end interface<br />

class bb_class : aa<br />

end class<br />

class cc_class : aa<br />

end class<br />

class dd_class : aa<br />

end class<br />

implement dd_class inherits bb_class, cc_class<br />

end implement<br />

在 这 种 情 况 下 , 是 类 bb_class 还 是 类 cc_class 来 给 类 dd_class 提 供 aa 的 实 现 是 模 糊 的 ( 注 意 ,<br />

我 们 说 一 个 类 实 现 一 个 接 口 , 就 意 味 着 它 对 在 接 口 里 声 明 的 谓 词 提 供 定 义 )。<br />

当 然 可 以 在 dd_class 的 实 现 中 添 加 子 句 , 这 可 以 有 效 地 解 决 问 题 。 比 如 , 下 面 的 子 句 , 它 会 从<br />

bb_class 中 “ 输 出 ” 谓 词 p:<br />

clauses<br />

p() :- bb_class::p().<br />

25


但 这 样 一 来 , 就 并 非 从 bb 继 承 了 , 其 实 是 我 们 的 类 委 托 任 务 给 bb_class。<br />

为 要 解 决 这 样 的 模 糊 ( 真 的 是 继 承 而 非 委 托 ), 可 以 使 用 一 个 resolve 段 。 这 个 段 包 含 一 些 解 决 方 案 :<br />

ResolveQualification :<br />

resolve Resolution-comma-sep-list<br />

Resolution :<br />

InterfaceResolution<br />

PredicateFromClassResolution<br />

PredicateRenameResolution<br />

PpredicateExternallyResolution<br />

限 定 符 resolve 用 于 从 指 定 的 源 中 提 供 实 现 。<br />

谓 词 解 决 方 案<br />

PredicateFromClassResolution :<br />

PredicateNameWithArity from ClassName<br />

来 自 类 的 谓 词 解 决 方 案 , 由 指 定 的 类 提 供 该 谓 词 的 实 现 。<br />

对 一 个 类 的 谓 词 解 决 方 案 来 说 :<br />

• 该 类 必 须 实 现 了 要 解 决 的 谓 词 , 这 也 意 味 着 该 谓 词 必 须 要 与 被 继 承 的 那 个 谓 词 源 于 同 样 的 接 口<br />

• 该 类 已 经 出 现 在 inherits 段<br />

谓 词 重 命 名 解 决 方 案<br />

谓 词 重 命 名 解 决 方 案 , 是 指 由 另 一 个 不 同 名 称 的 谓 词 提 供 该 谓 词 的 实 现 。 这 个 谓 词 必 须 来 自 一 个 继 承<br />

的 类 , 其 类 型 、 模 式 及 流 样 式 必 须 要 完 全 匹 配 。<br />

接 口 解 决 方 案<br />

PredicateRenameResolution :<br />

PredicateNameWithArity from ClassName :: PredicateName<br />

接 口 解 决 方 案 用 来 从 一 个 继 承 的 类 中 提 供 完 整 的 接 口 。 因 而 , 这 个 解 决 方 案 就 是 简 单 地 说 该 接 口 中 的<br />

所 有 谓 词 应 该 由 相 应 的 类 来 解 决 。<br />

InterfaceResolution :<br />

interface InterfaceName from ClassName<br />

该 类 必 须 公 开 地 支 持 提 供 解 决 方 案 的 接 口 。<br />

如 果 谓 词 解 决 方 案 与 接 口 解 决 方 案 都 覆 盖 了 某 个 谓 词 名 , 则 会 使 用 谓 词 解 决 方 案 , 也 就 是 说 精 细 度 高<br />

的 解 决 方 案 优 先 。 当 数 个 接 口 覆 盖 一 个 谓 词 时 , 只 要 它 们 都 是 由 相 同 的 类 提 供 谓 词 的 解 决 方 案 的 , 也 是 这<br />

样 。 另 一 方 面 , 如 果 谓 词 的 解 决 方 案 来 自 不 同 的 类 , 就 必 须 要 用 谓 词 解 决 方 案 来 克 服 模 糊 问 题 。<br />

注 意 , 解 决 方 案 的 语 法 不 能 解 决 谓 词 对 不 同 类 的 不 同 重 载 问 题 。<br />

例 我 们 可 以 用 接 口 解 决 方 案 来 解 决 前 面 例 子 中 的 问 题 。 在 这 种 情 况 下 , 我 们 选 择 aa_class 的 实 现 继<br />

承 cc_class, 但 从 bb_class 继 承 p。<br />

implement dd_class<br />

26


外 部 解 决 方 案<br />

inherits bb_class, cc_class<br />

resolve<br />

interface aa from cc_class<br />

p from bb_class<br />

end implement<br />

谓 词 的 外 部 解 决 方 案 , 是 指 谓 词 根 本 就 不 在 类 本 身 实 现 , 而 是 使 用 外 部 的 库 。 外 部 解 决 方 案 只 适 用 于<br />

类 谓 词 , 对 象 谓 词 不 能 用 这 个 解 决 方 案 。<br />

重 要 的 是 , 调 用 协 议 、 链 接 名 及 参 数 类 型 要 符 合 实 现 库 的 要 求 。<br />

私 有 与 公 用 谓 词 都 可 以 使 用 外 部 解 决 方 案 。<br />

PredicateExternallyResolution : PredicateNameWithArity externally<br />

动 态 外 部 解 决 方 案<br />

谓 词 的 外 部 解 决 方 案 也 提 供 了 动 态 地 从 DLL 中 加 载 私 有 和 公 用 类 谓 词 的 语 法 , 其 为 :<br />

PredicateExternallyResolutionFromDLL :<br />

PredicateNameWithArity externally from DllNameWithPath<br />

DllNameWithPath :<br />

StringLiteral<br />

如 果 在 DLL DllNameWithPath 中 没 有 谓 词 predicateNameWithArity, 动 态 加 载 会 让 程 序 运 行<br />

下 去 , 直 到 程 序 实 际 调 用 这 个 没 有 找 到 的 谓 词 , 这 时 就 会 出 现 运 行 时 错 误 。DllNameWithPath 是 程 序<br />

运 行 的 机 器 上 的 DLL 的 路 径 , 它 可 以 是 绝 对 的 也 可 以 是 相 对 的 。 例 如 , 如 果 需 要 的 dll 是 在 应 用 程 序 所 在 位<br />

置 的 上 一 级 目 录 中 , 就 可 以 用 “../DllName”。 参 看 MSDN 中 的 动 态 库 搜 索 次 序 。<br />

终 结<br />

对 象 一 旦 无 法 为 程 序 所 用 , 就 可 以 被 终 结 了 。 在 本 语 言 的 语 义 中 没 有 准 确 地 说 对 象 什 么 时 候 被 终 结 ,<br />

能 保 证 的 是 : 只 要 程 序 还 能 访 问 某 个 对 象 , 它 就 不 会 被 终 结 。 实 际 上 对 象 的 终 结 , 是 当 它 被 垃 圾 收 集 器 废<br />

掉 的 时 候 。 终 结 是 构 造 的 对 立 面 , 是 把 对 象 从 内 存 中 清 除 出 去 。<br />

类 也 可 以 实 现 一 个 终 结 器 , 这 是 一 个 谓 词 , 一 个 对 象 终 结 时 调 用 它 ( 在 对 象 被 清 出 内 存 之 前 )。<br />

终 结 器 是 一 个 过 程 , 没 有 参 数 , 没 有 返 回 值 , 其 名 称 是 finalize。 这 个 谓 词 是 隐 含 声 明 的 , 不 能 在 程<br />

序 中 直 接 调 用 。<br />

终 结 器 的 主 要 目 的 是 为 了 能 释 放 外 部 资 源 , 但 是 并 没 有 限 制 它 能 够 做 什 么 。 使 用 终 结 器 要 小 心 , 前 面<br />

说 过 , 调 用 它 们 的 时 间 不 能 完 全 确 定 , 因 而 也 就 很 难 预 测 调 用 它 时 整 个 程 序 的 状 态 。<br />

注 意 , 不 可 能 从 在 终 结 器 中 的 对 象 撤 消 对 象 事 实 , 因 为 终 结 过 程 是 自 动 进 行 的 。<br />

程 序 终 止 前 , 所 有 对 象 都 要 被 终 结 ( 除 非 出 现 像 电 源 故 障 这 样 的 异 常 )。<br />

例 这 个 例 子 中 , 用 一 个 终 结 器 来 保 证 正 确 地 关 闭 数 据 库 的 连 接 :<br />

implement aa_class<br />

facts<br />

connection : databaseConnection.<br />

clauses<br />

27


Delegate 限 定 符<br />

finalize() :-<br />

connection:close().<br />

...<br />

end implement aa_class<br />

delegate 段 包 含 有 一 些 委 托 项 :<br />

DelegateQualification :<br />

delegate Delegation-comma-sep-list<br />

Delegation :<br />

PredicateDelegation<br />

InterfaceDelegation<br />

委 托 限 定 符 用 于 将 对 象 谓 词 的 实 现 委 托 给 指 定 的 源 。<br />

委 托 限 定 符 分 为 两 类 ,Predicate Delegation( 谓 词 委 托 ) 和 Interface Delegation( 接 口 委 托 )。<br />

接 口 委 托 用 于 将 一 个 接 口 中 声 明 的 全 体 对 象 谓 词 的 实 现 委 托 给 另 一 个 对 象 的 实 现 , 存 储 为 事 实 变 量 。 这 就<br />

是 说 , 接 口 委 托 就 是 将 该 接 口 声 明 的 所 有 谓 词 的 实 现 委 托 给 另 一 个 对 象 的 实 现 , 存 储 在 事 实 变 量 中 。<br />

委 托 段 与 相 应 的 ( 谓 词 / 接 口 ) 解 决 方 案 段 相 似 , 只 不 过 需 要 委 托 事 实 变 量 来 保 存 构 造 的 类 的 对 象 ,<br />

而 不 是 从 类 中 继 承 。<br />

谓 词 委 托<br />

对 象 谓 词 的 委 托 , 是 指 该 谓 词 的 功 能 委 托 给 由 事 实 变 量 FactVariable_of_InterfaceType 中 指<br />

定 的 对 象 中 的 谓 词 。<br />

PredicateDelegation :<br />

PredicateNameWithArity to FactVariable_of_InterfaceType<br />

要 委 托 一 个 谓 词 给 一 个 由 事 实 变 量 传 递 的 对 象 :<br />

• 事 实 变 量 FactVariable_of_InterfaceType 必 须 具 有 声 明 谓 词 predicateNameWithArity<br />

的 接 口 的 类 型 ( 或 子 类 型 )<br />

• 对 象 支 持 的 接 口 必 须 已 经 构 造 好 了 , 并 且 已 经 与 FactVariable_of_InterfaceType 事 实 变<br />

量 相 关 联 。<br />

看 一 下 下 面 的 例 子 :<br />

interface a<br />

predicates<br />

p1 : () procedure ().<br />

p2 : () procedure ().<br />

end interface<br />

interface aa<br />

supports a<br />

end interface<br />

class bb_class : a<br />

end class<br />

class cc_class : a<br />

28


end class<br />

class dd_class : aa<br />

constructors<br />

new : (a First, a Second).<br />

end class<br />

implement dd_class<br />

delegate p1/0 to fv1, p2/0 to fv2<br />

facts<br />

fv1 : a.<br />

fv2 : a.<br />

clauses<br />

new(I,J):-<br />

fv1 := I,<br />

fv2 := J.<br />

end implement<br />

后 面 , 它 就 可 以 构 造 a 类 型 的 对 象 , 并 将 其 赋 予 事 实 变 量 fv1 和 fv2, 来 定 义 对 象 , 这 个 对 象 的 类 就 是<br />

我 们 实 际 要 委 托 定 义 p1 和 p2 功 能 的 类 。 来 看 :<br />

goal<br />

O_bb = bb_class::new(),<br />

O_cc = cc_class::new(),<br />

O_dd = dd_class::new(O_bb, O_cc),<br />

O_dd : p1(),<br />

% 这 个 p1 来 自 O_bb 对 象<br />

O_dd : p2().<br />

% 这 个 p2 来 自 O_cc 对 象<br />

其 实 ,<strong>Visual</strong> <strong>Prolog</strong> 的 委 托 与 在 dd_class 的 实 现 中 添 加 子 句 明 确 地 指 出 要 从 哪 个 类 的 对 象 “ 输 出 ”<br />

谓 词 的 功 能 , 效 果 是 一 样 的 。 对 上 面 这 个 例 子 , 也 就 是 在 dd_class 的 实 现 中 添 加 下 面 的 子 句 :<br />

接 口 委 托<br />

clauses<br />

p1() :- fv1:p1().<br />

如 果 需 要 指 定 在 InterfaceName 接 口 中 声 明 的 所 有 谓 词 的 功 能 都 委 托 给 来 自 相 同 继 承 类 的 对 象 谓<br />

词 , 就 可 以 用 接 口 委 托 :<br />

InterfaceDelegation :<br />

interface InterfaceName to FactVariable_of_InterfaceType<br />

接 口 委 托 就 是 将 在 接 口 InterfaceName 中 声 明 的 所 有 谓 词 的 功 能 委 托 给 存 储 为 事 实 变 量<br />

FactVariable_of_InterfaceType 的 对 象 。 对 象 应 该 已 经 与 FactVariable_of_InterfaceType<br />

事 实 变 量 相 关 联 , 而 这 个 事 实 变 量 的 类 型 应 该 是 InterfaceName 的 ( 或 是 其 子 类 型 )。<br />

要 将 接 口 委 托 给 一 个 由 事 实 变 量 传 递 的 对 象 :<br />

• 事 实 变 量 FactVariable_of_InterfaceType 必 须 是 接 口 InterfaceName 的 类 型 ( 或 是 其<br />

子 类 型 )<br />

• 对 象 支 持 的 接 口 必 须 已 经 构 造 , 并 且 已 经 与 事 实 变 量 FactVariable_of_InterfaceType 相<br />

关 联 。<br />

谓 词 委 托 的 优 先 级 高 于 接 口 委 托 。 如 果 一 个 谓 词 两 个 委 托 都 做 了 , 也 就 是 做 了 谓 词 委 托 , 而 它 又 是 在<br />

一 个 接 口 中 声 明 的 , 这 个 接 口 又 做 了 委 托 , 这 时 , 将 实 现 高 优 先 级 的 谓 词 委 托 。<br />

29


Namespaces( 名 称 空 间 )<br />

名 称 空 间 可 以 用 于 避 免 名 称 冲 突 , 不 需 要 使 用 长 的 冷 僻 名 称 。 在 不 同 名 称 空 间 的 名 字 是 不 会 产 生 冲 突<br />

的 , 但 可 能 需 要 用 名 称 空 间 来 指 定 引 用 以 解 决 模 糊 问 题 。<br />

名 称 空 间 的 声 明 与 定 义 隐 含 地 使 用 NamespaceEntrance:<br />

NamespaceEntrance:<br />

namespace NamespaceIdentifier<br />

NamespaceIdentifier: one of<br />

LowercaseIdentifier<br />

LowercaseIdentifier \ NamespaceIdentifier<br />

简 单 地 说 ,NamespaceIdentifier 是 一 系 列 用 反 斜 杠 分 隔 的 小 写 标 识 。<br />

Namespace 的 入 口 与 区 域<br />

名 称 空 间 入 口 把 源 文 件 分 割 成 一 些 namespace regions( 名 称 空 间 区 域 )。 一 个 名 称 空 间 入 口 标<br />

志 着 一 个 名 称 空 间 区 域 的 起 始 , 这 个 区 域 结 束 于 下 一 个 名 称 空 间 入 口 或 是 文 件 的 结 尾 。 每 个 文 件 开 始 于<br />

root namespace( 根 名 称 空 间 )。<br />

名 称 空 间 区 域 不 会 受 #include 指 令 的 影 响 , 也 就 是 说 , 在 一 个 #include- 文 件 中 的 名 称 空 间 入 口 不<br />

会 改 变 包 括 文 件 的 名 称 空 间 区 域 。<br />

任 何 文 件 都 起 始 于 根 名 称 空 间 ( 即 使 它 是 被 包 括 在 另 一 个 文 件 中 的 内 部 名 称 空 间 也 是 这 样 )。<br />

任 何 在 一 个 名 称 空 间 区 域 中 汇 合 的 接 口 、 类 和 实 现 , 属 于 那 个 名 称 空 间 。<br />

例<br />

class aaa<br />

end class aaa<br />

namespace xxx<br />

class bbb<br />

end class bbb<br />

namespace xxx\yyy<br />

class ccc<br />

end class ccc<br />

这 个 文 件 分 成 了 三 个 区 域 ( 假 设 这 是 一 个 完 整 的 文 件 )。 第 一 个 区 域 是 根 名 称 空 间 (\), 第 二 个 区<br />

域 是 属 于 xxx 名 称 空 间 的 , 而 第 三 个 区 域 是 属 于 xxx\yyy 名 称 空 间 的 。 相 应 地 , 类 aaa 属 于 根 名 称 空 间 ,<br />

类 bbb 属 于 xxx 名 称 空 间 而 类 ccc 属 于 xxx\yyy 名 称 空 间 。<br />

namespaces 中 的 引 用 名<br />

如 果 ccc 是 在 名 称 空 间 xxx\yyy 中 的 一 个 类 , 那 么 ccc 的 全 称 (full name) 就 是 \xxx\yyy\ccc。<br />

30


最 前 面 的 反 斜 杠 表 示 是 从 根 名 称 空 间 开 始 的 。 类 / 接 口 总 是 可 以 用 全 称 来 唯 一 性 地 引 用 。<br />

Open namespaces<br />

全 称 有 时 并 不 方 便 , 我 们 可 以 用 开 放 的 名 称 空 间 来 使 用 简 短 的 名 称 。<br />

ScopeQualification: one of<br />

OpenQualification<br />

...<br />

OpenQualification: one of<br />

open NamespaceIdentifier \<br />

...<br />

开 放 的 名 称 空 间 是 由 结 尾 的 反 斜 杠 与 开 放 的 类 / 接 口 相 区 别 的 。<br />

例<br />

class aaa<br />

open xxx\yyy\<br />

...<br />

end class aaa<br />

名 称 空 间 xxx\yyy 在 aaa 中 是 开 放 的 。<br />

当 一 个 名 称 空 间 是 开 放 的 时 , 其 全 称 部 分 就 可 以 省 掉 。 如 , 全 称 是 \xxx\yyy\zzz\ccc::ddd 的 一<br />

个 域 , 可 以 在 aaa 中 用 zzz\ccc::ddd 来 引 用 , 因 为 xxx\yyy 是 开 放 的 。<br />

注 意 , 略 写 的 名 称 不 是 用 反 斜 杠 开 头 的 , 用 反 斜 杠 开 头 的 名 称 总 是 全 称 。<br />

属 于 某 个 范 围 ( 如 接 口 / 类 / 实 现 ) 的 名 称 空 间 , 在 那 个 范 围 内 部 是 ( 隐 含 地 ) 开 放 的 。<br />

Program Sections( 程 序 段 )<br />

以 下 各 段 用 于 在 范 围 内 声 明 和 定 义 实 体 。<br />

Section :<br />

ConstantsSection 常 数 段<br />

DomainsSection 域 段<br />

PredicatesSection 谓 词 段<br />

ConstructorsSection 构 造 器 段<br />

PropertiesSection 属 性 段<br />

FactsSection 事 实 段<br />

ClausesSection 子 句 段<br />

ConditionalSection 条 件 段<br />

上 面 的 段 并 不 会 都 出 现 在 所 有 类 型 的 范 围 内 , 详 细 情 况 请 参 考 接 口 、 类 声 明 和 类 实 现 。<br />

条 件 段 在 条 件 编 译 一 节 中 叙 述 。<br />

Domains( 域 )<br />

Domains 段<br />

31


域 段 在 当 前 范 围 中 定 义 了 一 组 域 ( 参 看 接 口 、 类 声 明 和 类 实 现 )。<br />

Domain 定 义<br />

DomainsSection:<br />

domains DomainDefinition-dot-term-list-opt<br />

域 定 义 在 当 前 范 围 中 对 一 个 命 名 了 的 域 作 详 细 说 明 。<br />

DomainDefinition:<br />

DomainName FormalTypeParameterList-opt = TypeExpression<br />

如 果 右 边 的 域 表 示 一 个 接 口 或 一 个 复 合 域 , 则 定 义 的 域 与 类 型 表 达 式 是 同 义 的 ( 等 价 的 )。 否 则 , 定<br />

义 的 域 就 是 类 型 表 达 式 表 示 的 域 的 子 类 型 。 这 里 , 域 名 DomainName 必 须 是 小 写 标 识 。<br />

有 的 地 方 必 须 用 域 名 而 不 是 类 型 表 达 式 :<br />

• 作 为 形 式 参 数 类 型 的 声 明 时 ;<br />

• 作 为 常 数 或 事 实 变 量 的 类 型 时 ;<br />

• 作 为 表 域 的 类 型 时 。<br />

类 型 表 达 式<br />

类 型 名<br />

类 型 表 达 式 表 示 一 种 类 型 。<br />

TypeExpression:<br />

TypeName<br />

CompoundDomain<br />

ListDomain<br />

PredicateDomain<br />

IntegralDomain<br />

RealDomain<br />

TypeVariable<br />

TypeApplication<br />

类 型 名 是 一 个 接 口 的 名 字 或 是 一 个 值 域 的 名 字 。 值 域 是 指 其 元 素 是 非 易 变 的 ( 不 可 更 改 的 ) 域 。<br />

这 里 我 们 可 以 说 , 对 象 是 属 于 相 应 于 接 口 名 的 域 , 具 有 易 变 的 状 态 , 而 其 它 的 域 则 是 非 易 变 的 。 因 此 ,<br />

实 际 上 值 类 型 就 是 除 了 对 象 类 型 而 外 的 所 有 的 东 西 。 类 型 名 , 显 而 易 见 是 表 示 相 应 于 已 有 域 的 名 称 的 类 型<br />

的 。<br />

TypeName:<br />

InterfaceName<br />

DomainName<br />

ClassQualifiedDomainName<br />

InterfaceName:<br />

LowercaseIdentifier<br />

DomainName:<br />

LowercaseIdentifier<br />

32


ClassQualifiedDomainName:<br />

ClassName::DomainName<br />

ClassName:<br />

LowercaseIdentifier<br />

这 里 ,InterfaceName 是 接 口 名 ,DomainName 是 值 域 名 而 ClassName 是 类 名 。<br />

例<br />

domains newDomain1 = existingDomain. newDomain2 = myInterface.<br />

在 上 面 这 个 例 子 中 , 分 别 用 域 名 existingDomain 和 接 口 名 myInterface 定 义 了 两 个 新 的 域 。<br />

复 合 域<br />

复 合 域 ( 也 称 为 代 数 数 据 类 型 ) 用 于 表 示 表 、 树 及 其 它 树 状 结 构 的 值 。 它 的 简 单 形 式 可 以 表 示 结 构 和<br />

枚 举 值 。 复 合 域 可 以 递 归 定 义 , 可 以 互 递 归 或 间 接 递 归 。<br />

CompoundDomain:<br />

Alignment-opt FunctorAlternative-semicolon-sep-list><br />

Alignment:<br />

align IntegralConstantExpression<br />

这 里 的 IntegralConstantExpression 是 一 个 表 达 式 , 在 编 译 时 必 须 能 计 算 得 出 整 数 值 。<br />

复 合 域 声 明 宣 布 了 一 个 函 子 选 项 表 , 还 带 有 可 选 的 alignment, 它 必 须 是 1、2 或 4。<br />

如 果 复 合 域 包 含 一 个 函 子 选 项 , 则 可 以 视 其 为 结 构 , 它 的 表 示 方 法 与 C 语 言 的 相 应 结 构 是 二 进 制 兼 容<br />

的 。<br />

FunctorAlternative:<br />

FunctorName FunctorName ( FormalArgument-comma-sep-list-opt )<br />

这 里 的 FunctorName 是 函 子 选 项 名 称 , 应 该 用 小 写 标 识 。<br />

FormalArgument:<br />

TypeExpression ArgumentName-opt<br />

这 里 的 ArgumentName 可 以 是 任 意 大 写 标 识 。 编 译 器 会 忽 略 它 。<br />

复 合 域 对 其 它 任 何 域 都 没 有 子 类 型 的 关 系 。 如 果 一 个 域 定 义 和 一 个 复 合 域 的 相 同 , 则 这 两 个 域 是 同 义<br />

类 型 的 而 不 是 子 类 型 。 也 就 是 说 , 这 两 个 域 是 不 同 名 称 相 同 类 型 的 。<br />

例<br />

domains<br />

t1 = ff(); gg(integer, t1).<br />

t1 是 一 个 有 两 个 选 项 的 复 合 域 , 第 一 个 选 项 是 零 元 函 子 ff, 而 第 二 个 选 项 是 二 元 函 子 gg, 它 带 有 一<br />

个 integer 和 一 个 域 项 t1 本 身 作 为 参 数 。 因 此 , 域 t1 是 递 归 定 义 的 。<br />

下 面 这 些 表 达 式 都 是 域 t1 的 项 :<br />

33


ff()<br />

gg(77, ff())<br />

gg(33, gg(44, gg(55, ff())))<br />

例<br />

domains<br />

t1 = ff(); gg(t2).<br />

t2 = hh(t1, t1).<br />

t1 是 有 两 个 选 项 的 复 合 域 , 第 一 个 选 项 是 零 元 函 子 ff, 而 第 二 个 选 项 是 一 元 函 子 gg, 它 以 域 项 t2 为<br />

参 数 。t2 也 是 一 个 复 合 域 , 有 一 个 选 项 函 子 hh, 这 个 函 子 以 两 个 t1 项 为 参 数 。 因 而 , 域 t1 和 t2 是 互 递 归<br />

的 。 下 面 这 些 项 都 是 域 t1 的 :<br />

ff()<br />

gg(hh(ff(), ff()))<br />

gg(hh(gg(hh(ff(), ff())), ff()))<br />

ggg(hh(ff(), g(hh(ff(), ff()))))<br />

gg(hh(gg(hh(ff(), ff())), gg(hh(ff(), ff()))))<br />

例 这 个 例 子 中 ,t1 和 t2 是 同 义 类 型 。<br />

domains<br />

t1 = f(); g(integer).<br />

t2 = t1.<br />

通 常 没 有 必 要 在 零 元 函 子 后 使 用 空 的 圆 括 号 。 但 在 域 定 义 中 , 如 果 仅 有 一 个 零 元 函 子 , 就 必 须 用 空 的<br />

圆 括 号 来 与 同 义 / 子 类 型 定 义 相 区 分 。<br />

例 t1 是 一 个 仅 有 一 个 零 元 函 子 的 复 合 域 , 而 t2 是 定 义 为 与 t1 同 义 的 。<br />

domains<br />

t1 = f().<br />

t2 = t1.<br />

List Domains( 表 域 )<br />

表 域 表 示 某 个 域 中 的 一 个 序 列 值 。 因 此 , 所 有 在 表 T 中 的 元 素 都 必 须 是 T 类 型 的 。<br />

ListDomain:<br />

TypeName *<br />

T* 是 T 元 素 表 的 类 型 。<br />

下 面 一 些 语 法 应 用 于 表 :<br />

ListExpression:<br />

[ Term-comma-sep-list-opt ]<br />

[ Term-comma-sep-list | Tail ]<br />

Tail:<br />

34


Term<br />

这 里 Tail 是 其 值 为 ListDomain 类 型 的 项 。 每 个 Term 都 应 该 是 typeName 类 型 的 。<br />

实 际 上 , 表 只 不 过 是 有 两 个 函 子 的 复 合 域 : 表 示 空 表 的 [] 和 表 示 表 头 HD 与 表 尾 TL 的 混 合 固 定 函 子<br />

[HD|TL]。 表 头 必 须 是 基 本 元 素 类 型 , 而 表 尾 必 须 是 相 应 类 型 的 表 。<br />

表 不 过 是 语 法 便 利 化 的 结 果 。<br />

[E1, E2, E3, ..., En | L ] 是 [E1 |[ E2 |[ ...[ En | L ]...]]] 的 简 化 。<br />

[E1, E2, E3, ..., En] 是 [E1, E2, E3, ..., En |[]] 的 简 化 , 而 后 者 又 是 [E1 |[ E2 |[ ...[ En |<br />

[] ]...]]] 的 简 化 。<br />

Predicate Domains( 谓 词 域 )<br />

谓 词 域 的 值 是 带 有 相 同 特 征 的 谓 词 , 而 “ 相 同 特 征 ” 指 的 是 相 同 的 参 数 与 返 回 类 型 、 相 同 的 流 样 式 和<br />

相 同 ( 或 更 强 的 ) 谓 词 模 式 。<br />

返 回 值 的 谓 词 称 为 function( 函 数 ), 而 不 返 回 值 的 谓 词 有 时 也 称 为 原 态 ( 普 通 ) 谓 词 , 以 强 调 其<br />

不 是 一 个 函 数 。<br />

PredicateDomain:<br />

( FormalArgument-comma-sep-list-opt ) ReturnArgument-opt<br />

PredicateModeAndFlow-list-opt CallingConvention-opt<br />

FormalArgument:<br />

PredicateArgumentType VariableName-opt<br />

Ellipsis<br />

ReturnArgument:<br />

-> FormalArgument<br />

PredicateArgumentType:<br />

TypeName<br />

AnonymousIdentifier<br />

VariableName:<br />

UpperCaseIdentifier<br />

谓 词 域 可 以 用 Ellipsis( 省 略 号 ) 参 数 作 为 FormalArgument-comma-sep-list 中 的 最 后 的<br />

FormalArgument。<br />

谓 词 域 可 以 用 AnonymousIdentifier 作 为 一 个 predicateArgumentType, 表 示 这 个 参 数 可 以 是<br />

任 意 类 型 的 。<br />

目 前 , 带 省 略 号 的 谓 词 域 只 能 用 在 谓 词 声 明 中 。<br />

在 域 定 义 中 的 谓 词 域 最 多 只 能 声 明 一 个 流 样 式 。<br />

PredicateModeAndFlow:<br />

PredicateMode-opt<br />

FlowPattern-list-opt<br />

Predicate Mode( 谓 词 模 式 )<br />

规 定 的 谓 词 模 式 适 用 于 其 后 流 样 式 表 中 的 各 个 成 员 。<br />

PredicateMode: one of<br />

35


erroneous<br />

failure procedure<br />

determ multi<br />

nondeterm<br />

谓 词 模 式 可 以 用 以 下 集 合 来 描 述 :<br />

erroneous = {}<br />

failure = {Fail}<br />

procedure = {Succeed}<br />

determ = {Fail, Succeed}<br />

multi = {Succeed, BacktrackPoint}<br />

nondeterm = {Fail, Succeed, BacktrackPoint}<br />

集 合 中 有 Fail 就 意 味 着 这 个 谓 词 可 以 失 败 , 有 succeed 意 味 着 可 以 成 功 , 有 BacktrackPoint 意<br />

味 着 可 以 由 有 效 的 回 溯 点 返 回 。<br />

如 果 一 个 集 合 ( 比 如 说 failure) 是 另 一 个 集 合 ( 比 如 说 nondeterm) 的 子 集 , 则 称 其 模 式 比 另 一<br />

个 强 , 也 就 是 failure 强 于 nondeterm。<br />

谓 词 域 实 际 上 包 含 了 所 有 规 定 的 ( 或 更 强 的 ) 模 式 的 谓 词 ( 带 有 合 适 的 类 型 和 流 样 式 )。<br />

对 构 造 器 声 明 谓 词 模 式 是 非 法 的 , 构 造 器 的 模 式 永 远 是 procedure。<br />

缺 省 的 谓 词 模 式 是 procedure。<br />

缺 省 流 样 式 表 示 所 有 参 数 都 用 于 输 入 。<br />

注 意 , 与 <strong>Visual</strong> <strong>Prolog</strong> v. 5.x 不 同 , 实 现 内 部 ( 即 对 一 个 局 部 谓 词 ) 要 求 的 流 及 模 式 不 是 源 于 谓 词<br />

的 用 法 。 现 在 , 在 对 局 部 谓 词 的 声 明 中 省 略 谓 词 模 式 表 示 是 procedure 而 省 略 流 样 式 表 示 所 有 参 数 是 输<br />

入 。<br />

如 果 在 一 个 实 现 内 部 需 要 明 确 地 规 定 一 个 谓 词 使 用 的 所 有 可 能 的 流 样 式 , 仍 然 可 以 把 这 个 责 任 交 给 编<br />

译 器 。 对 类 实 现 内 这 个 任 务 , 可 以 使 用 特 殊 的 流 样 式 anyflow。 在 此 情 况 下 , 编 译 时 编 译 器 会 按 照 谓 词<br />

的 用 法 得 到 需 要 的 流 样 式 。 但 如 果 在 流 样 式 前 anyflow 的 可 选 的 谓 词 模 式 缺 省 了 , 则 总 是 假 设 为<br />

procedure 模 式 。<br />

Flow Pattern( 流 样 式 )<br />

流 样 式 定 义 参 数 的 输 入 / 输 出 流 向 。 参 数 可 以 与 函 子 域 相 结 合 , 使 得 单 个 的 参 数 一 部 分 是 输 入 而 另 一<br />

部 分 是 输 出 。<br />

流 样 式 由 一 个 流 序 列 构 成 , 每 个 流 对 应 于 一 个 参 数 ( 第 一 个 流 对 应 第 一 个 参 数 ,… 等 等 )。<br />

FlowPattern:<br />

( Flow-comma-sep-list-opt ) AnyFlow<br />

Flow: one of<br />

i<br />

o<br />

FunctorFlow<br />

ListFlow<br />

Ellipsis<br />

省 略 流 必 须 与 省 略 参 数 相 对 应 , 因 而 它 只 能 是 流 样 式 中 的 最 后 一 个 流 。<br />

Ellipsis:<br />

...<br />

36


函 子 流 FunctorFlow 说 明 一 个 函 子 及 其 各 成 分 的 流 。 当 然 , 函 子 必 须 在 相 应 参 数 的 域 中 。<br />

FunctorFlow:<br />

FunctorName ( Flow-comma-sep-list-opt )<br />

函 子 流 声 明 中 不 能 含 有 省 略 流 。<br />

表 流 与 函 子 流 相 似 , 只 不 过 是 采 用 了 与 表 域 相 同 的 语 法 加 工 。<br />

ListFlow:<br />

[ Flow-comma-sep-list-opt ListFlowTail-opt]<br />

ListFlowTail:<br />

| Flow<br />

表 流 也 不 能 含 有 省 略 流 。<br />

声 明 谓 词 时 流 样 式 可 以 省 略 。 在 实 现 内 部 ( 即 对 局 部 谓 词 ) 需 要 的 流 样 式 得 自 于 谓 词 的 使 用 方 法 。 在<br />

接 口 或 类 声 明 内 部 ( 即 对 公 有 谓 词 ) 省 略 流 样 式 表 示 所 有 参 数 都 是 输 入 。<br />

特 殊 流 样 式 anyflow 只 能 在 局 部 谓 词 的 声 明 ( 即 在 类 实 现 内 部 的 谓 词 声 明 ) 中 使 用 。 它 表 示 准 确 的<br />

流 样 式 在 编 译 时 确 定 。 如 果 其 前 可 选 的 谓 词 模 式 省 略 了 , 表 示 谓 词 模 式 是 procedure。<br />

例<br />

domains<br />

pp1 = (integer Argument1).<br />

pp1 是 一 个 谓 词 域 。 这 个 具 有 pp1 类 型 的 谓 词 有 一 个 integer 参 数 。 因 为 没 有 写 出 流 样 式 , 表 示 这 个<br />

参 数 是 输 入 , 又 因 为 没 有 写 出 谓 词 模 式 , 表 示 谓 词 是 procedure。<br />

例<br />

domains<br />

pp2 = (integer Argument1) -> integer ReturnType.<br />

pp2 类 型 的 谓 词 有 一 个 integer 参 数 并 返 回 integer 类 型 的 值 。 因 而 ,pp2 是 一 个 函 数 域 , 具 有 pp2<br />

类 型 的 谓 词 实 际 上 是 函 数 。 因 为 没 有 说 明 流 样 式 , 所 以 参 数 是 输 入 的 , 谓 词 模 式 也 没 提 , 故 谓 词 是<br />

procedure 的 。<br />

例<br />

predicates<br />

ppp : (integer Argument1, integer Argument2) determ (o,i) (i,o) nondeterm (o,o).<br />

谓 词 ppp 有 两 个 integer 参 数 , 它 有 三 种 不 同 的 流 样 式 :(o,i) 和 (i,o) 是 determ 的 ,(o,o) 是<br />

nondeterm 的 。<br />

调 用 约 定<br />

调 用 约 定 确 定 了 参 数 等 如 何 传 递 给 谓 词 , 它 也 确 定 了 如 何 由 谓 词 名 得 到 链 接 名 。<br />

37


CallingConvention:<br />

language CallingConventionKind<br />

CallingConventionKind: one of<br />

c stdcall apicall prolog<br />

如 果 没 有 说 明 调 用 约 定 , 则 使 用 prolog 约 定 , 它 是 <strong>Prolog</strong> 谓 词 使 用 的 标 准 约 定 。<br />

c 调 用 约 定 遵 从 C/C++ 的 标 准 调 用 约 定 。 谓 词 的 链 接 名 是 谓 词 名 前 加 下 划 线 (_)。<br />

的 实 现 :<br />

stdcall 调 用 约 定 使 用 c 的 链 接 名 策 略 , 但 参 数 与 栈 的 处 理 规 则 有 不 同 。 下 表 说 明 了 stdcall 调 用 约 定<br />

特 征<br />

参 数 传 递 顺 序<br />

参 数 传 递 约 定<br />

栈 的 维 护 职 责<br />

名 字 的 装 饰<br />

大 小 写 转 换 约 定<br />

从 右 到 左<br />

实 现<br />

除 复 合 域 项 传 递 , 都 是 传 递 的 值 。 因 此 , 它 不 能 应 用 于 参 数 数 量 可 变 的 谓 词<br />

被 调 用 的 谓 词 从 栈 中 弹 出 自 己 的 参 数<br />

在 谓 词 名 前 的 一 个 下 划 线 (_)<br />

不 做 谓 词 名 的 大 小 写 转 换<br />

apicall 调 用 约 定 使 用 与 stdcall 一 样 的 参 数 和 栈 处 理 规 则 , 但 为 了 方 便 MS Windows API 函 数 的 调<br />

用 使 用 了 大 多 数 MS Windows API 函 数 调 用 所 用 的 命 名 约 定 。 根 据 该 约 定 , 谓 词 的 链 接 名 构 造 方 法 如 下 :<br />

• 在 谓 词 名 前 加 下 划 线 (_);<br />

• 谓 词 名 的 首 字 母 改 为 大 写 ;<br />

• 如 果 参 数 或 返 回 类 型 是 ANSI 的 , 加 后 缀 “A”, 如 果 是 Unicode 的 , 加 后 缀 “W”, 中 性 谓 词<br />

什 么 都 不 加 ;<br />

• 加 “@” 后 缀 ;<br />

• 后 面 附 加 推 入 栈 中 的 字 节 数 。<br />

例<br />

predicates<br />

predicateName : (integer, string) language apicall<br />

这 个 谓 词 的 参 数 类 型 表 明 它 是 一 个 Unicode 谓 词 ( 因 为 string 是 Unicode 串 域 的 )。 一 个 integer 和 一<br />

个 string 各 占 调 用 栈 的 4 字 节 , 因 而 , 它 的 链 接 名 就 是 :<br />

_PredicateNameW@8<br />

如 果 apicall 与 “as” 构 造 一 并 使 用 , 在 “as” 构 造 中 说 明 的 名 称 使 用 与 上 面 相 同 的 方 法 。<br />

apicall 只 能 直 接 使 用 在 谓 词 声 明 中 , 不 能 用 在 谓 词 域 定 义 中 。 谓 词 域 定 义 中 必 须 替 代 使 用 stdcall。<br />

下 表 比 较 了 c、apicall 及 stdcall 调 用 约 定 的 实 现 (prolog 调 用 约 定 有 特 殊 的 实 现 , 这 里 没 有 讨 论 ):<br />

类 型 清 栈 职 责 谓 词 名 大 小 写 转 换 链 接 名 构 造 方 法<br />

c 调 用 谓 词 从 栈 中 弹 出 参 数 不 转 换 谓 词 名 前 加 下 划 线 (_)<br />

stdcall 被 调 用 谓 词 从 栈 中 弹 出 自 己 不 转 换<br />

谓 词 名 前 加 下 划 线 (_)<br />

的 参 数<br />

apicall 被 调 用 谓 词 从 栈 中 弹 出 自 己<br />

的 参 数<br />

谓 词 名 首 字 母 转 换<br />

为 大 写<br />

谓 词 名 前 加 下 划 线 (_), 首 字 母 换 为<br />

大 写 , 加 “A” 或 “W” 后 缀 , 加 “@”<br />

后 缀 , 加 参 数 字 节 数 ( 十 进 制 )<br />

38


Visaul <strong>Prolog</strong> 谓 词 域 的 概 念 覆 盖 了 类 和 对 象 的 成 员 。 类 成 员 的 处 理 是 直 接 向 前 的 , 但 对 象 成 员 的 处<br />

理 需 要 注 意 。 对 象 谓 词 的 调 用 将 “ 回 退 ” 到 拥 有 该 成 员 的 对 象 关 联 中 。<br />

例<br />

设 有 如 下 声 明 :<br />

interface actionEventSource<br />

domains<br />

actionListener = (actionEventSource Source) procedure (i).<br />

predicates<br />

addActionListener : (actionListener Listener) procedure (i).<br />

...<br />

end interface<br />

再 假 设 有 一 个 button_class 类 支 持 actionEventSource。 点 击 按 钮 时 传 递 事 件 。 在<br />

myDialog_class 类 中 实 现 一 个 对 话 框 , 创 建 一 个 按 钮 并 监 听 它 的 动 作 事 件 以 作 出 反 应 :<br />

implement myDialog_class<br />

clauses<br />

new() :-<br />

OkButton = button_class::new(...),<br />

OkButton:addActionListener(onOk),<br />

...<br />

facts<br />

okPressed : () determ.<br />

predicates<br />

onOk : actionListener.<br />

clauses<br />

onOk(Source) :-<br />

assert(okPressed()).<br />

end implement<br />

在 这 个 例 子 中 重 要 的 是 onOk 是 一 个 对 象 成 员 , 而 且 , 当 按 了 按 钮 后 , 注 册 了 的 onOk 的 调 用 会 将 我<br />

们 带 回 给 拥 有 onOk 的 对 象 。 也 就 是 说 , 我 们 能 访 问 对 象 事 实 okPressed, 这 样 我 们 才 能 插 入 事 实 。<br />

Format Strings( 格 式 串 )<br />

对 谓 词 的 格 式 参 数 , 可 以 用 formatstring 属 性 标 记 成 格 式 串 。 格 式 串 中 可 以 有 原 态 字 符 , 这 样 的 字<br />

符 是 不 加 修 饰 地 原 样 打 印 的 , 还 有 以 百 分 号 (%) 开 头 的 格 式 域 。 如 果 % 后 面 的 是 未 知 字 符 ( 不 是 格 式 符 ),<br />

则 百 分 号 和 这 个 字 符 也 会 原 样 打 印 。<br />

例<br />

predicates<br />

writef : (string Format [formatstring], ...) procedure (i,...).<br />

格 式 域 的 构 成 是 :<br />

[-][0][width][.precision][type]<br />

上 述 所 有 组 份 都 是 可 选 的 。<br />

连 字 符 [-] 表 示 这 个 域 是 右 对 齐 的 ; 缺 省 是 左 对 齐 。 如 果 宽 度 值 [width] 没 有 设 置 , 或 是 实 际 字 符 数 比<br />

宽 度 值 还 要 大 , 则 没 有 效 果 。<br />

宽 度 前 的 零 [0] 表 示 对 值 要 添 加 零 直 到 达 到 最 小 宽 度 。 如 果 零 和 连 字 符 一 同 出 现 , 零 就 被 忽 略 了 。<br />

39


宽 度 [width] 是 正 的 十 进 制 数 , 表 示 域 段 的 最 小 宽 度 。 如 果 实 际 字 符 数 小 于 这 个 值 , 就 要 在 值 的 前 面<br />

( 若 设 置 了 ‘-’ 则 是 在 后 面 ) 添 加 空 格 字 符 。 如 果 实 际 字 符 数 比 这 个 值 大 , 就 没 有 什 么 改 变 。<br />

点 之 后 带 无 符 号 十 进 制 数 [.precision] 表 示 浮 点 数 精 度 , 也 可 以 表 示 从 一 个 串 里 最 多 打 印 出 的 字 符 数 。<br />

[type] 规 定 了 缺 省 之 外 其 它 一 些 格 式 。 如 , 在 该 域 可 以 用 一 个 规 定 符 表 示 一 个 整 数 要 格 式 化 为 一 个 无<br />

符 号 数 。 可 用 值 为 :<br />

f 将 实 数 格 式 化 为 定 点 十 进 制 数 , 如 123.4,0.004321。 这 是 实 数 的 缺 省 格 式 。<br />

e 将 实 数 格 式 化 为 指 数 表 示 法 , 如 1.234e+002,4.321e-003。<br />

g 将 实 数 格 式 化 为 f 和 e 格 式 中 较 短 的 那 种 , 但 当 指 数 值 小 于 -4 或 大 于 等 于 精 度 时 总 是 用 e 格<br />

式 。 尾 零 都 要 被 截 去 。<br />

d 或 D 格 式 化 为 有 符 号 十 进 制 数 。<br />

u 或 U 格 式 化 为 无 符 号 整 数 。<br />

x 或 X 格 式 化 为 十 六 进 制 数 。<br />

o 或 O 格 式 化 为 八 进 制 数 。<br />

c 格 式 化 为 一 个 字 符 。<br />

B 格 式 化 为 <strong>Visual</strong> <strong>Prolog</strong> 的 二 进 制 类 型 。<br />

R 格 式 化 为 数 据 库 索 引 数 。<br />

P 格 式 化 为 过 程 参 量 。<br />

s 格 式 化 为 串 。<br />

Integral Domains( 整 数 域 )<br />

整 数 域 用 于 表 示 整 数 , 它 分 成 两 个 主 要 类 型 : 有 符 号 数 和 无 符 号 数 。 整 数 域 也 有 不 同 的 表 示 范 围 , 预<br />

定 义 的 域 integer 和 unsigned 表 示 有 符 号 的 和 无 符 号 的 整 数 , 是 处 理 器 架 构 ( 如 32 位 机 器 上 的 32 位 , 等 )<br />

的 固 定 表 示 长 度 。<br />

IntegralDomain:<br />

DomainName-opt IntegralDomainProperties<br />

如 果 在 IntegralDomainPropertie 前 面 有 DomainName, 那 么 它 本 身 必 须 是 整 数 域 的 , 接 下<br />

来 的 域 是 这 个 域 的 一 个 子 类 型 。 此 时 ,IntegralDomainProperties 不 能 违 反 作 为 一 个 子 类 型 的 可 能<br />

性 , 也 就 是 说 , 数 值 表 示 不 能 超 范 围 , 不 能 改 变 比 特 位 数 。<br />

IntegralDomainProperties:<br />

IntegralSizeDescription IntegralRangeDescription-opt<br />

IntegralRangeDescription IntegralSizeDescription-opt<br />

IntegralSizeDescription:<br />

bitsize DomainSize<br />

DomainSize:<br />

IntegralConstantExpression<br />

IntegralSizeDescription( 整 数 尺 度 描 述 ) 说 明 该 整 数 域 的 DomainSize 大 小 , 以 比 特 为 单 位 。 编<br />

译 器 会 以 不 小 于 这 个 规 定 的 比 特 位 数 实 现 这 个 整 数 域 。DomainSize 的 值 应 该 是 正 数 并 且 不 能 超 过 编 译<br />

器 支 持 的 最 大 值 ( 对 v 6.x 来 说 是 32 位 )。 如 果 省 略 了 整 数 尺 度 描 述 , 就 使 用 和 父 域 一 样 的 尺 度 。 如 果 没<br />

有 父 域 , 则 使 用 处 理 器 的 本 身 的 尺 度 。<br />

IntegralRangeDescription:<br />

40


[ MinimalBoundary-opt .. MaximalBoundary-opt ]<br />

MinimalBoundary:<br />

IntegralConstantExpression<br />

MaximalBoundary:<br />

IntegralConstantExpression<br />

IntegralRangeDescription( 整 数 界 限 描 述 ) 说 明 该 整 数 域 最 小 值 MinimalBoundary 和 最 大 值<br />

MaximalBoundary。 如 果 省 略 了 , 就 使 用 父 域 的 界 限 。 如 果 没 有 父 域 , 就 使 用 DomainSize 来 确 定<br />

相 应 的 最 小 值 和 最 大 值 。<br />

注 意 , 最 小 值 不 能 大 于 最 大 值 , 也 就 是 :<br />

MinimalBoundary


这 里 的 RealConstantExpression 是 一 个 表 达 式 , 在 编 译 时 它 被 计 算 出 一 个 浮 点 值 。 也 就 是 说 ,<br />

在 编 译 时 该 实 数 域 的 精 度 和 界 限 必 须 是 可 计 算 的 。<br />

RealRangeDescription( 实 数 界 限 描 述 ) 说 明 该 实 数 域 最 小 值 和 最 大 值 。 如 果 省 略 了 , 就 使 用 父 域<br />

的 界 限 。 如 果 没 有 父 域 , 就 使 用 最 大 可 用 精 度 。<br />

注 意 , 最 小 值 不 能 大 于 最 大 值 , 也 就 是 :<br />

MinimalBoundary


常 数<br />

Constants 段<br />

constants 段 在 当 前 范 围 内 定 义 一 组 常 数 。<br />

常 数 定 义<br />

ConstantsSection :<br />

constants ConstantDefinition-dot-term-list-opt<br />

常 数 定 义 规 定 命 名 了 的 常 数 , 它 的 类 型 , 它 的 值 。<br />

ConstantDefinition: one of<br />

ConstantName = ConstantValue<br />

ConstantName : TypeName = ConstantValue<br />

ConstantName:<br />

LowerCaseIdentifier<br />

ConstantValue 应 该 为 表 达 式 , 在 编 译 时 应 该 能 计 算 得 到 值 并 且 具 有 相 应 域 的 类 型 。<br />

ConstantName 应 该 是 小 写 标 识 。<br />

只 有 对 下 列 内 建 的 域 才 可 以 省 略 类 型 名 :<br />

1. 数 值 ( 即 整 数 或 实 数 ) 常 数 。 在 这 种 情 况 下 , 对 常 数 使 用 相 应 匿 名 数 字 域 ( 详 细 请 参 见 数 字 域 )。<br />

2. 二 进 制 常 数 。<br />

3. 串 常 数 。<br />

4. 字 符 常 数 。<br />

例<br />

constants<br />

my_char = 'a'.<br />

true_const : boolean = true.<br />

binaryFileName = "mybin".<br />

myBinary = #bininclude(binaryFileName).<br />

Predicates( 谓 词 )<br />

Predicates 段<br />

谓 词 段 在 当 前 范 围 内 声 明 一 组 对 象 或 类 谓 词 。<br />

PredicatesSection :<br />

class-opt predicates PredicateDeclaration-dot-term-list-opt<br />

关 键 字 class 只 能 用 在 类 实 现 中 。 对 于 谓 词 , 在 接 口 中 声 明 的 总 是 对 象 谓 词 , 而 在 类 声 明 中 声 明 的 ,<br />

总 是 类 谓 词 。<br />

43


Predicate Declarations( 谓 词 声 明 )<br />

谓 词 声 明 用 于 在 一 个 范 围 内 声 明 谓 词 , 在 这 个 范 围 内 , 该 谓 词 是 可 见 的 。 当 谓 词 在 接 口 定 义 中 声 明 时 ,<br />

就 意 味 着 相 应 类 型 的 对 象 必 须 支 持 这 些 谓 词 。 当 谓 词 在 类 声 明 中 声 明 时 , 就 意 味 着 该 类 公 有 地 支 持 所 声 明<br />

的 谓 词 。 而 如 果 谓 词 是 在 类 实 现 中 声 明 的 , 则 意 味 着 谓 词 是 局 部 的 。 不 管 在 哪 种 情 况 下 , 必 须 有 谓 词 的 相<br />

应 定 义 。<br />

PredicateDeclaration :<br />

PredicateName : PredicateDomain LinkName-opt<br />

PredicateName : PredicateDomainName LinkName-opt<br />

LinkName :<br />

as StringLiteral<br />

PredicateName :<br />

LowerCaseIdentifier<br />

这 里 ,predicateDomainName( 谓 词 域 名 ) 是 在 域 段 中 声 明 的 谓 词 域 的 名 称 。<br />

谓 词 声 明 规 定 了 谓 词 的 名 称 、 它 的 类 型 、 模 式 、 流 ( 参 见 谓 词 域 ) 及 可 选 的 链 接 名 。<br />

只 有 类 谓 词 才 可 以 有 链 接 名 , 如 果 没 有 说 明 链 接 名 则 会 依 据 调 用 约 定 由 谓 词 名 产 生 相 应 的 链 接 名 。 如<br />

果 调 用 约 定 是 apicall, 则 这 个 链 接 名 还 要 由 as 子 句 加 工 。 如 果 不 做 这 个 加 工 , 就 使 用 替 代 的 stdcall 调 用<br />

约 定 。<br />

链 接 名 的 缀 饰 加 工<br />

有 时 链 接 名 必 须 有 _...@N 的 缀 饰 , 但 apicall 来 的 缺 省 名 则 是 错 的 。 在 这 样 的 缀 饰 中 , 后 缀 的 A 和 后 缀<br />

的 W 可 以 用 于 控 制 缀 饰 加 工 :<br />

predicates<br />

myPredicate : (string X) language stdcall as decorated.<br />

在 这 种 情 况 下 , 链 接 名 会 是 “_MyPredicate@4”, 而 apicall 会 使 它 成 为 “_MyPredicateW@4”。<br />

predicates<br />

myPredicate : (string X) language stdcall as decoratedA.<br />

在 这 种 情 况 下 , 链 接 名 会 是 “_MyPredicateA@4”, 而 apicall 会 使 它 成 为 “_MyPredicate@4”。<br />

predicates<br />

myPredicate : (string X) language stdcall as decoratedW.<br />

在 这 种 情 况 下 , 链 接 名 会 是 “_MyPredicateW@4”, 而 apicall 会 使 它 成 为 “_MyPredicate@4”。<br />

上 述 所 有 情 况 中 , 都 是 把 名 称 由 xxxx 变 为 _Xxxx, 并 加 上 后 缀 @N。 第 一 种 情 况 不 用 后 缀 的 字 母 , 第<br />

二 种 情 况 总 是 后 缀 A, 而 第 三 种 情 况 总 是 后 缀 W。 这 就 是 说 , 编 程 者 负 责 确 定 使 用 需 要 的 后 缀 , 不 过 不 需<br />

要 操 心 计 算 参 数 的 字 节 数 和 起 始 的 “_X”( 加 下 划 线 和 把 首 字 母 改 为 大 写 )。<br />

Constructors 段<br />

constructors 段 声 明 一 组 构 造 器 。 构 造 器 属 于 constructors 段 所 在 的 范 围 ( 参 见 类 声 明 和 类 实<br />

44


现 )。<br />

ConstructorsSection :<br />

constructors ConstructorDeclaration-dot-term-list-opt<br />

构 造 器 段 只 能 在 构 造 对 象 的 类 的 声 明 和 实 现 中 出 现 。<br />

Constructor 声 明<br />

构 造 器 声 明 说 明 类 的 命 名 了 的 构 造 器 。<br />

一 个 构 造 器 实 际 上 有 两 个 相 关 的 谓 词 :<br />

• 一 个 类 函 数 , 这 个 函 数 能 返 回 一 个 新 构 造 的 对 象 ,<br />

• 一 个 对 象 谓 词 , 该 谓 词 用 来 初 始 化 继 承 的 对 象 。<br />

相 关 的 构 造 器 对 象 谓 词 用 于 进 行 对 象 的 初 始 化 。 这 个 谓 词 只 能 在 该 类 本 身 由 该 构 造 器 调 用 , 或 是 由 继<br />

承 这 个 类 的 一 个 类 的 构 造 器 调 用 ( 即 基 类 初 始 化 )。<br />

ConstructorDeclaration :<br />

ConstructorName : PredicateDomain<br />

不 能 对 构 造 器 说 明 谓 词 模 式 , 构 造 器 总 是 procedure 模 式 的 。<br />

例 看 一 下 如 下 的 类 :<br />

class test_class : test<br />

constructors<br />

new : (integer Argument).<br />

end class test_class<br />

相 关 的 类 谓 词 有 如 下 特 征 :<br />

class predicates<br />

new : (integer) -> test.<br />

而 相 关 的 对 象 谓 词 特 征 如 下 :<br />

predicates<br />

new : (integer).<br />

再 来 看 一 下 如 下 的 实 现 :<br />

implement test2_class inherits test_class<br />

clauses<br />

new() :-<br />

test_class::new(7),<br />

% 在 "This" 上 调 用 基 类 构 造 器<br />

p(test_class::new(8)). % 创 建 基 类 一 个 新 对 象 并 把 它 传 递 给 p(...)<br />

...<br />

对 test_class::new 的 第 一 个 调 用 并 不 返 回 一 个 值 , 因 此 它 是 一 个 对 构 造 器 的 非 函 数 对 象 版 本 的 调<br />

用 , 也 就 是 说 , 它 是 在 This 上 的 基 类 构 造 器 的 一 个 调 用 。<br />

而 第 二 个 调 用 则 确 实 返 回 一 个 值 , 因 此 它 是 对 构 造 器 类 函 数 版 本 的 一 个 调 用 , 也 就 是 说 , 它 创 建 一 个<br />

新 的 对 象 。<br />

45


Predicates from Interface( 来 自 接 口 的 谓 词 )<br />

一 个 接 口 可 以 支 持 其 它 接 口 的 一 个 子 集 , 这 是 通 过 在 谓 词 from 段 声 明 谓 词 实 现 的 。predicates<br />

from 段 列 出 接 口 及 所 有 支 持 的 谓 词 。 对 谓 词 的 说 明 可 以 是 谓 词 的 名 称 或 名 称 加 元 维 。<br />

一 个 接 口 支 持 另 外 接 口 的 子 集 时 , 它 既 不 是 这 个 另 外 的 接 口 的 子 类 , 也 不 是 它 的 超 类 。<br />

对 predicates from 段 来 说 , 重 要 的 一 点 是 : 所 有 列 出 的 谓 词 都 保 留 在 原 来 的 接 口 中 , 因 此 :<br />

• 来 自 原 来 接 口 的 谓 词 不 会 有 支 持 冲 突 ;<br />

• 它 们 可 以 作 为 谓 词 由 原 来 的 接 口 中 继 承 。<br />

PredicatesFromInterface :<br />

predicates from InterfaceName PredicateNameWithArity-comma-sep-list-opt<br />

PredicatesFromInterface 只 能 用 在 接 口 定 义 中 。<br />

例<br />

interface aaa<br />

predicates<br />

ppp : ().<br />

qqq : ().<br />

end interface aaa<br />

interface bbb<br />

predicates from aaa<br />

ppp<br />

predicates<br />

rrr : ().<br />

end interface bbb<br />

interface ccc supports aaa, bbb<br />

end interface ccc<br />

尽 管 aaa 和 bbb 都 各 声 明 了 一 个 谓 词 ppp,ccc 还 是 可 以 无 冲 突 地 支 持 这 两 者 , 因 为 在 所 有 情 况 下<br />

ppp 都 有 aaa 作 为 源 接 口 。<br />

例<br />

interface aaa<br />

predicates<br />

ppp : ().<br />

qqq : ().<br />

end interface aaa<br />

interface bbb<br />

predicates from aaa<br />

ppp<br />

predicates<br />

rrr : ().<br />

end interface bbb<br />

class aaa_class : aaa<br />

end class aaa_class<br />

46


class bbb_class : bbb<br />

end class bbb_class<br />

implement aaa_class inherits bbb_class<br />

clauses<br />

qqq().<br />

end implement aaa_class<br />

aaa_class 可 以 由 bbb_class 类 继 承 ppp, 因 为 ppp 在 两 个 类 中 有 aaa 作 为 源 接 口 。<br />

Arity( 元 维 )<br />

一 个 谓 词 带 有 N 个 参 数 时 , 就 称 其 是 N 元 (N-ary) 的 , 或 说 元 维 是 N(arity N) 的 。 不 同 元 维 的 谓<br />

词 是 不 同 的 谓 词 , 即 使 它 们 的 名 称 相 同 也 是 这 样 。<br />

在 大 多 数 情 况 下 , 谓 词 的 元 维 是 清 楚 的 , 可 以 从 上 下 文 中 看 出 来 。 但 有 些 时 候 就 不 明 显 了 , 比 如 在 谓<br />

词 的 from 段 里 以 及 在 使 用 resolve 限 定 符 时 就 可 能 会 出 现 这 样 的 问 题 。<br />

为 了 能 够 区 分 谓 词 的 不 同 元 维 , 在 predicates from 段 里 及 使 用 resolve 限 定 符 时 , 可 以 选 用 谓 词<br />

名 称 加 元 维 的 方 法 来 说 明 。 可 以 使 用 如 下 的 方 法 :<br />

• Name/N 表 示 元 维 是 N 的 本 原 谓 词 Name( 即 不 是 一 个 函 数 ),<br />

• Name/N-> 表 示 元 维 是 N 的 函 数 Name,<br />

• Name/N... 表 示 带 有 N 个 参 数 后 跟 一 个 Ellipsis( 省 略 符 ) 参 数 ( 即 参 数 数 量 可 变 ) 的 本 原 谓<br />

词 Name。( 省 略 号 可 以 在 谓 词 及 谓 词 域 的 声 明 中 作 为 最 后 一 个 形 式 参 数 使 用 , 此 时 它 表 示 所 声<br />

明 的 谓 词 ( 谓 词 域 ) 有 可 变 数 量 的 参 数 。 省 略 流 必 须 与 省 略 参 数 相 匹 配 , 故 此 也 只 能 是 流 样 式 中<br />

的 最 后 一 个 流 。)<br />

• Name/N...-> 表 示 一 个 带 有 N 个 参 数 后 跟 一 个 省 略 参 数 的 函 数 Name。<br />

PredicateNameWithArity :<br />

PredicateName Arity-opt<br />

Arity : one of<br />

/ IntegerLiteral Ellipsis-opt<br />

/ IntegerLiteral Ellipsis-opt -><br />

在 Name/0... 和 Name/0...-> 的 情 况 中 , 零 是 可 以 省 去 的 , 因 而 也 可 以 分 别 写 成 Name/... 和<br />

Name/...->。<br />

Properties( 属 性 )<br />

属 性 段 声 明 在 当 前 范 围 内 对 象 或 类 的 属 性 。<br />

声 明<br />

PropertyDeclaration :<br />

PropertyName : PropertyType FlowPattern-list-opt.<br />

FlowPattern: one of<br />

(i)<br />

47


(o)<br />

属 性 的 声 明 类 似 于 single 事 实 。<br />

如 果 流 样 式 省 略 了 , 就 认 为 即 可 以 是 (i) 又 可 以 是 (o)。 例 如 :<br />

interface ip<br />

properties<br />

duration : integer.<br />

initialized : boolean.<br />

scale : string.<br />

end interface<br />

属 性 用 起 来 与 实 现 中 的 single 事 实 是 一 样 的 。 可 以 用 范 围 名 称 或 对 象 来 修 饰 限 制 属 性 。<br />

设 X 是 一 个 支 持 接 口 ip 的 对 象 :<br />

X:duration := 5,<br />

if X:initialized = true then ... else ... end if,<br />

X:scale := "meter",<br />

....<br />

在 ip 的 实 现 内 部 , 可 以 如 同 它 们 是 事 实 一 样 来 访 问 它 们 :<br />

duration := 5,<br />

if initialized = true then ... else ... end if,<br />

scale := "meter",<br />

....<br />

Implementation( 实 现 )<br />

属 性 是 通 过 定 义 一 个 获 取 值 的 函 数 及 一 个 适 当 的 设 置 值 的 谓 词 来 实 现 的 。<br />

如 :<br />

clauses<br />

duration() = duration_fact * scale.<br />

duration(D):- duration_fact := D/ scale.<br />

或 是 把 一 个 相 同 名 字 的 事 实 用 作 属 性 :<br />

facts<br />

initialized : boolean := false.<br />

不 能 让 设 置 和 获 取 谓 词 与 事 实 同 名 。 如 果 想 用 一 个 事 实 来 存 放 一 个 属 性 的 值 ( 我 们 经 常 这 样 做 ), 就<br />

必 须 给 事 实 另 一 个 名 字 , 如 :<br />

properties<br />

duration : integer.<br />

facts<br />

duration_fact : integer.<br />

clauses<br />

duration() = duration_fact.<br />

clauses<br />

duration(D) :-<br />

OldDuration = duration_fact,<br />

duration_fact := D,<br />

48


OldDuration D,<br />

!,<br />

sendChanged().<br />

duration(_D).<br />

不 能 把 duration 当 谓 词 ( 这 不 奇 怪 , 因 为 它 们 不 是 声 明 为 谓 词 而 是 声 明 为 属 性 的 ; 它 只 是 实 现 属 性 的<br />

存 和 取 的 方 法 的 )。 但 是 , 谓 词 的 名 称 它 占 用 了 , 所 以 不 能 用 duration\1 或 duration\0-> 再 声 明 谓 词 。<br />

实 现 的 细 节<br />

当 访 问 一 个 在 接 口 中 声 明 的 属 性 时 , 关 于 它 的 实 现 不 能 做 任 何 假 设 , 因 而 也 就 不 得 不 认 为 它 是 用 谓 词<br />

实 现 的 。 如 果 程 序 员 是 把 这 个 属 性 当 事 实 定 义 的 , 编 译 器 就 必 须 产 生 适 当 的 谓 词 , 当 通 过 一 个 接 口 来 访 问<br />

这 个 属 性 时 , 就 使 用 它 们 。 而 在 实 现 内 部 , 定 义 为 事 实 的 属 性 就 可 以 当 成 事 实 来 访 问 。<br />

限 制 读 写<br />

有 的 时 候 , 想 让 一 个 属 性 只 能 读 或 只 能 写 。 假 设 我 们 用 i/o 范 式 声 明 它 们 :<br />

duration : integer (o). % 一 个 只 读 的 属 性<br />

duration : integer (i). % 一 个 只 写 的 属 性<br />

duration : integer (o) (i). % 一 个 正 常 的 属 性 , 可 以 读 也 可 以 写 , 与 什 么 都 不 说 明 一 样<br />

duration : integer. % 与 上 面 声 明 的 一 样 。.<br />

如 果 属 性 声 明 为 只 读 , 就 可 以 只 声 明 取 (get) 谓 词 , 同 样 , 如 果 是 声 明 为 只 写 就 可 以 只 声 明 设 置 (set)<br />

谓 词 。<br />

当 然 , 如 果 属 性 是 当 成 事 实 来 实 现 的 , 在 实 现 内 部 就 可 以 把 它 当 成 一 个 普 通 的 事 实 来 使 用 。<br />

Properties from Interface( 来 自 接 口 的 属 性 )<br />

一 个 接 口 可 以 支 持 另 一 个 接 口 的 子 集 , 这 是 通 过 在 属 性 from 段 声 明 属 性 实 现 的 。properties from<br />

段 列 出 接 口 及 所 有 支 持 的 属 性 。<br />

一 个 接 口 支 持 另 外 接 口 的 子 集 时 , 它 既 不 是 这 个 另 外 的 接 口 的 子 类 , 也 不 是 它 的 超 类 。<br />

对 properties from 段 来 说 , 重 要 的 一 点 是 : 所 有 列 出 的 属 性 都 保 留 在 原 来 的 接 口 中 , 因 此 :<br />

• 它 们 可 以 作 为 属 性 由 原 来 的 接 口 中 继 承 。<br />

PropertiesFromInterface :<br />

properties from InterfaceName PpropertyName-comma-sep-list-opt<br />

PropertiesFromInterface 只 能 用 在 接 口 定 义 中 。<br />

例<br />

interface aaa<br />

properties<br />

pp : integer.<br />

qq : boolean.<br />

end interface aaa<br />

interface bbb<br />

properties from aaa<br />

49


pp<br />

properties<br />

rr : string.<br />

end interface bbb<br />

interface ccc supports aaa, bbb<br />

end interface ccc<br />

尽 管 aaa 和 bbb 都 各 声 明 了 一 个 属 性 ppp,ccc 还 是 可 以 无 冲 突 地 支 持 这 两 者 , 因 为 在 所 有 情 况 下<br />

ppp 都 有 aaa 作 为 源 接 口 。<br />

例<br />

interface aaa<br />

properties<br />

pp : integer.<br />

qq : boolean.<br />

end interface aaa<br />

interface bbb<br />

properties from aaa<br />

pp<br />

properties<br />

rr : string.<br />

end interface bbb<br />

class aaa_class : aaa<br />

end class aaa_class<br />

class bbb_class : bbb<br />

end class bbb_class<br />

implement aaa_class inherits bbb_class<br />

facts<br />

pp_fact(): integer.<br />

clauses<br />

pp()= pp_fact-3.<br />

clauses<br />

pp(D):- pp_fact:=D+3.<br />

end implement aaa_class<br />

aaa_class 可 以 由 bbb_class 类 继 承 pp, 因 为 pp 在 两 个 类 中 有 aaa 作 为 源 接 口 。<br />

Facts<br />

facts 段 声 明 一 个 事 实 数 据 库 , 它 是 由 一 些 事 实 构 成 的 。 事 实 数 据 库 和 事 实 是 属 于 当 前 范 围 的 。<br />

事 实 数 据 库 可 是 是 类 一 级 的 , 也 可 以 是 对 象 一 级 的 。<br />

事 实 数 据 库 只 能 在 类 实 现 中 声 明 。<br />

如 果 事 实 数 据 库 有 名 称 , 也 就 隐 含 地 附 加 定 义 了 一 个 复 合 域 。 这 个 域 具 有 与 事 实 段 相 同 的 名 称 , 并 具<br />

有 与 事 实 段 中 的 事 实 相 对 应 的 函 子 。<br />

如 果 facts 段 有 名 称 , 这 个 名 字 就 表 示 了 内 建 域 factDB 的 一 个 值 。save 和 consult 谓 词 可 以 接 纳 这<br />

50


个 域 的 值 。<br />

FactsSection :<br />

class-opt facts FactsSectionName-opt FactDeclaration-dot-term-list-opt<br />

FactsSectionName :<br />

- LowerCaseIdentifier<br />

Fact 声 明<br />

事 实 声 明 说 明 一 个 事 实 数 据 库 的 事 实 。 事 实 声 明 可 以 是 一 个 事 实 变 量 , 也 可 以 是 一 个 函 子 事 实 。<br />

FactDeclaration :<br />

FactVariableDeclaration<br />

FactFunctorDeclaration<br />

FactFunctorDeclaration :<br />

FactName : ( Argument-comma-sep-list-opt ) FfactMode-opt<br />

FactName :<br />

LowerCaseIdentifier<br />

事 实 函 子 的 声 明 , 缺 省 时 具 有 nondeterm 的 事 实 模 式 。<br />

事 实 函 子 可 以 经 子 句 段 初 始 化 。 这 种 情 况 下 , 子 句 中 的 值 应 该 是 表 达 式 , 在 编 译 时 应 能 得 到 确 切 的 值 。<br />

FactMode : one of<br />

determ nondeterm single<br />

如 果 模 式 是 single, 则 这 个 事 实 总 是 有 一 个 且 只 能 有 一 个 值 ,assert 谓 词 会 把 旧 值 以 新 值 替 代 掉 。<br />

谓 词 retract 不 适 用 于 single 事 实 。<br />

如 果 模 式 是 nondeterm, 则 该 事 实 可 以 有 0、1 或 任 意 个 值 。 如 果 模 式 是 determ, 则 事 实 只 能 有 0<br />

或 1 个 值 。 如 果 事 实 有 0 个 值 , 则 任 何 对 它 的 读 访 问 都 会 失 败 。<br />

事 实 变 量 的 声 明<br />

一 个 事 实 变 量 , 就 如 同 只 有 一 个 参 数 的 single 函 子 事 实 。 不 过 , 语 法 上 它 是 一 个 可 变 量 ( 即 可 以 赋 值 )。<br />

FactVariableDeclaration :<br />

FactVariableName : Domain InitialValue-opt<br />

InitialValue :<br />

:= ConstantValue<br />

:= erroneous<br />

FactVariableName :<br />

LowerCaseIdentifier<br />

常 数 值 ConstantValue 应 该 是 (Domain 类 型 的 ) 一 个 项 , 它 在 编 译 时 应 该 能 得 到 确 切 的 值 。<br />

只 有 当 事 实 变 量 在 一 个 构 造 器 里 被 初 始 化 时 , 常 数 值 才 可 以 省 去 。<br />

类 事 实 变 量 总 是 要 有 初 始 常 数 值 。<br />

注 意 , 关 键 字 erroneous 可 以 当 作 一 个 值 赋 给 一 个 事 实 变 量 。 下 面 两 种 情 况 都 是 允 许 的 :<br />

51


facts<br />

thisWin : vpiDomains::windowHandle := erroneous.<br />

clauses<br />

p() :- thisWin := erroneous.<br />

赋 值 成 erroneous, 是 想 要 在 发 生 了 代 码 错 误 地 使 用 了 没 有 初 始 化 的 事 实 变 量 时 , 给 出 明 确 的 运 行<br />

时 错 误 。<br />

Facts<br />

事 实 只 能 在 一 个 类 的 实 现 中 声 明 , 因 而 , 也 只 能 在 这 个 实 现 中 引 用 。 因 此 , 事 实 的 范 围 是 它 的 声 明 所<br />

在 的 实 现 。 但 是 , 对 象 事 实 的 生 命 期 是 它 所 属 的 对 象 的 生 命 期 , 同 样 , 类 事 实 的 生 命 期 是 从 程 序 的 开 始 一<br />

直 到 程 序 结 束 。<br />

例 下 面 的 类 声 明 了 一 个 对 象 事 实 objectFact 和 一 个 类 事 实 classFact:<br />

implement aaa_class<br />

facts<br />

objectFact : (integer Value) determ.<br />

class facts<br />

classFact : (integer Value) determ.<br />

...<br />

end implement aaa_class<br />

Clauses( 子 句 )<br />

Clauses 段<br />

clauses 段 由 一 些 子 句 构 成 , 它 包 含 了 谓 词 的 实 现 或 事 实 的 初 始 化 值 。<br />

一 个 子 句 段 可 以 包 含 多 个 谓 词 及 事 实 的 子 句 。 另 一 方 面 , 一 个 ( 相 同 名 称 及 元 维 ) 谓 词 / 事 实 的 所 有<br />

子 句 在 一 个 子 句 段 中 必 须 集 中 在 一 起 , 不 能 间 隔 有 别 的 谓 词 / 事 实 的 子 句 。<br />

Clauses<br />

ClausesSection :<br />

clauses Clause-dot-term-list-opt<br />

子 句 用 于 定 义 谓 词 。 一 个 谓 词 由 一 组 子 句 来 定 义 , 每 个 子 句 依 次 执 行 , 直 到 它 们 中 的 一 个 成 功<br />

(succeeds), 或 是 直 到 再 也 没 有 其 它 剩 下 的 子 句 。 如 果 没 有 子 句 成 功 , 那 么 这 个 谓 词 就 失 败 (fails)<br />

了 。<br />

如 果 一 个 子 句 成 功 了 , 而 此 时 在 该 谓 词 中 还 剩 有 其 它 相 关 的 子 句 , 程 序 控 制 就 可 以 在 后 面 再 回 溯<br />

(backtrack) 到 这 个 谓 词 的 这 些 子 句 上 以 寻 求 其 它 的 解 。 因 此 , 一 个 谓 词 可 以 失 败 , 可 以 成 功 , 甚 至<br />

可 以 成 功 多 次 。<br />

一 个 子 句 有 头 部 及 可 选 的 体 部 。<br />

当 一 个 谓 词 被 调 用 时 , 它 的 子 句 依 次 ( 从 上 到 下 ) 被 检 测 。 对 每 个 子 句 来 说 , 头 部 要 与 调 用 参 数 进 行<br />

合 一 , 如 果 合 一 成 功 , 有 体 部 时 才 执 行 体 部 。 只 有 在 头 部 和 体 部 都 成 功 时 , 子 句 才 成 功 , 否 则 就 失 败 了 。<br />

子 句 是 由 头 部 及 一 个 可 选 的 体 部 构 成 的 。<br />

52


Clause :<br />

ClauseHead ReturnValue-opt ClauseBody-opt .<br />

ClauseHead :<br />

LowercaseIdentifier ( Term-comma-sep-list-opt )<br />

ReturnValue :<br />

= Term<br />

ClauseBody :<br />

:- Term<br />

Goal Section( 目 标 段 )<br />

goal 段 是 程 序 的 入 口 。 程 序 开 始 时 就 从 goal 开 始 执 行 ,goal 执 行 完 , 程 序 就 退 出 了 。<br />

GoalSection :<br />

goal Term.<br />

goal 段 是 由 子 句 体 构 成 的 , 这 个 段 定 义 了 自 己 的 范 围 , 因 此 所 有 的 调 用 都 应 包 括 类 限 定 符 。<br />

通 常 , 目 标 必 须 是 procedure 模 式 的 。<br />

Terms 项<br />

本 节 描 述 项 以 及 项 和 子 句 的 执 行 / 计 算 是 如 何 进 行 的 。<br />

从 语 义 上 说 , 有 两 类 项 : 公 式 (formulas) 和 表 达 式 (expressions)。<br />

表 达 式 代 表 值 , 如 数 字 7。<br />

公 式 代 表 逻 辑 说 明 , 如 “ 数 字 7 大 于 数 字 3”。<br />

而 依 语 法 来 看 , 这 两 类 其 实 有 很 大 的 重 叠 , 因 此 语 法 上 把 这 两 类 合 并 成 项 (terms)。<br />

下 面 对 Term 的 定 义 是 简 化 了 的 , 因 为 按 它 包 含 的 语 法 构 造 来 讲 有 些 是 不 合 法 的 。 比 如 ,! + ! 就 是 不<br />

合 法 的 写 法 。 不 过 , 这 种 简 洁 的 语 法 描 述 与 对 语 言 概 念 、 类 型 系 统 及 下 面 描 述 的 算 子 层 次 的 直 觉 理 解 相 结<br />

合 , 还 是 非 常 有 用 的 。<br />

Term:<br />

( Term )<br />

Literal<br />

Variable<br />

Identifier<br />

MemberAccess<br />

PredicateCall<br />

PredicateExpression<br />

UnaryOperator Term<br />

Term BinaryOperator Term<br />

Cut<br />

Ellipsis<br />

FactvariableAssignment<br />

Backtracking( 回 溯 )<br />

53


<strong>Prolog</strong> 程 序 的 求 值 就 是 对 目 标 的 一 个 解 的 搜 寻 。 对 解 的 搜 索 的 每 一 步 都 有 可 能 成 功 或 失 败 。 程 序 执 行<br />

过 程 中 的 某 个 点 上 , 对 搜 索 解 会 有 多 种 可 能 , 在 这 种 选 择 点 上 就 会 创 建 一 个 回 溯 点 (backtrack point)。<br />

回 溯 点 是 一 种 记 录 , 它 包 含 有 程 序 的 状 态 及 还 未 执 行 的 选 择 的 指 针 。 如 果 先 前 的 一 个 选 择 没 有 能 够 得 到 解<br />

( 也 就 是 失 败 了 ), 程 序 就 会 回 溯 到 记 录 的 回 溯 点 上 来 , 因 而 可 以 恢 复 程 序 的 状 态 再 追 寻 其 它 的 选 择 。 后<br />

面 会 要 描 述 这 个 机 理 并 用 例 子 来 详 细 说 明 。<br />

Literals( 文 字 )<br />

文 字 具 有 通 用 类 型 。<br />

Literal:<br />

IntegerLiteral<br />

RealLiteral<br />

CharacterLiteral<br />

StringLiteral<br />

BinaryLiteral<br />

ListLiteral<br />

CompoundDomainLiteral<br />

参 看 词 汇 元 素 中 的 文 字 。<br />

Variables( 变 量 )<br />

<strong>Visual</strong> <strong>Prolog</strong> 中 的 变 量 其 实 是 不 可 变 的 : 一 经 绑 定 为 一 个 值 时 它 们 就 一 直 保 有 这 个 值 , 但 回 溯 可 以<br />

为 变 量 解 除 绑 定 以 恢 复 程 序 的 先 前 状 态 。<br />

一 个 变 量 ( 在 合 一 和 匹 配 时 ) 可 以 被 绑 定 , 如 果 已 经 被 绑 定 了 , 就 用 它 绑 定 的 值 进 行 解 算 。<br />

变 量 名 以 一 个 大 写 字 母 或 一 个 下 划 线 开 头 , 后 跟 字 母 ( 可 以 是 大 写 也 可 以 是 小 写 )、 数 字 和 下 划 线 序<br />

列 ( 称 为 大 写 标 识 ):<br />

Variable:<br />

UppercaseIdentifer<br />

下 面 这 些 都 是 合 法 的 变 量 名 :<br />

My_first_correct_variable_name<br />

_<br />

_Sales_10_11_86<br />

而 下 面 这 些 则 是 不 合 法 的 :<br />

1stattempt<br />

second_attempt<br />

名 字 是 一 个 下 划 线 ( 即 _) 的 变 量 称 为 匿 名 变 量 , 它 用 于 形 式 上 需 要 但 具 体 的 值 我 们 并 不 关 心 的 场<br />

合 。 每 次 出 现 的 匿 名 变 量 都 是 独 立 的 , 也 就 是 说 , 哪 怕 在 一 个 子 句 中 使 用 了 多 次 匿 名 变 量 , 它 们 之 间 也 是<br />

没 有 关 系 的 。<br />

名 字 用 一 个 下 划 线 开 头 的 变 量 并 不 是 匿 名 变 量 , 但 它 的 值 仍 是 我 们 不 关 心 而 忽 略 的 。 如 果 它 的 值 实 际<br />

上 没 有 被 忽 略 , 编 译 器 会 给 出 一 个 警 告 。<br />

<strong>Prolog</strong> 的 变 量 是 局 限 于 它 所 在 的 子 句 的 , 即 : 如 果 两 个 子 句 都 含 有 变 量 X, 这 两 个 X 变 量 是 独 立 不 相<br />

关 的 。<br />

54


当 一 个 变 量 还 没 有 与 一 个 项 联 系 在 一 起 时 , 称 这 个 变 量 是 自 由 的 ; 而 当 它 与 一 个 项 合 一 后 , 则 称 其<br />

为 绑 定 的 或 实 例 化 了 的 。<br />

<strong>Visual</strong> <strong>Prolog</strong> 编 译 器 对 名 称 不 区 分 除 首 字 母 而 外 的 大 小 写 ,SourceCode 和 SOURCECODE 这 两<br />

个 变 量 名 指 的 是 同 一 个 变 量 。<br />

Identifier( 标 识 )<br />

Identifier:<br />

MemberName<br />

GlobalScopeMembername<br />

ScopeQualifiedMemberName<br />

MemberName:<br />

LowerCaseIdentifier<br />

标 识 用 于 指 称 命 名 了 的 实 体 ( 如 类 、 接 口 、 常 数 、 域 、 谓 词 、 事 实 , 等 等 )。<br />

标 识 只 能 是 小 写 标 识 ( 也 就 是 一 个 小 写 字 母 后 跟 字 母 、 数 字 和 下 划 线 字 符 等 的 序 列 )。<br />

许 多 实 体 可 以 有 相 同 的 名 称 , 因 此 , 可 能 需 要 限 定 小 写 标 识 名 的 特 定 的 影 响 范 围 , 或 说 明 名 称 是 全 局<br />

的 。<br />

例 下 面 这 些 是 无 限 定 的 标 识 :<br />

全 局 实 体 的 访 问<br />

integer<br />

mainExe<br />

myPredicate<br />

在 <strong>Visual</strong> <strong>Prolog</strong> 中 , 全 局 实 体 只 有 内 建 的 域 、 谓 词 和 常 数 。 全 局 名 称 在 任 何 范 围 内 都 是 可 以 直 接 访<br />

问 的 , 但 也 存 在 全 局 名 称 被 一 个 局 部 名 称 或 引 入 名 称 屏 蔽 的 情 况 。 此 时 , 全 局 实 体 可 以 用 一 个 双 冒 号 “::”<br />

( 不 需 要 前 缀 类 / 接 口 名 称 ) 来 修 饰 。 双 冒 号 可 以 用 在 任 何 地 方 , 但 最 重 要 的 使 用 场 合 是 接 口 名 称 用 于 形 式 参<br />

数 类 型 说 明 符 时 。<br />

GlobalScopeMemberName:<br />

:: MemberName<br />

例 内 建 域 是 定 义 为 全 局 范 围 的 , 为 避 免 含 糊 或 强 调 这 个 特 殊 的 域 , 也 可 以 使 用 其 全 局 范 围 成 员 名 称 :<br />

::integer<br />

类 / 接 口 成 员 的 访 问<br />

类 与 接 口 的 静 态 成 员 的 访 问 是 通 过 带 类 名 称 ( 及 可 选 的 名 称 范 围 前 缀 ) 的 限 定 符 进 行 的 :<br />

ScopeQualifiedMemberName<br />

NamespacePrefix-opt ScopeName :: MemberName<br />

NamespacePrefix:<br />

NamespaceIdentifier-opt \<br />

ScopeName:<br />

LowercaseIdentifier<br />

55


ScopeName 是 定 义 / 声 明 该 名 称 的 类 或 接 口 的 名 字 。<br />

Namespace 的 前 缀 在 Referencing names in namespaces 中 说 明 。<br />

有 些 名 称 的 访 问 可 以 不 要 限 定 符 , 参 看 范 围 与 可 见 性 。<br />

Predicate Call( 谓 词 调 用 )<br />

谓 词 调 用 的 形 式 是 :<br />

PredicateCall:<br />

Term ( Term-comma-sep-list-opt )<br />

第 一 项 必 须 是 一 个 可 以 求 得 谓 词 类 型 的 值 的 表 达 式 。 通 常 , 它 是 一 个 类 中 一 个 谓 词 的 名 称 , 或 是 对 一<br />

个 对 象 的 谓 词 成 员 求 值 的 表 达 式 。<br />

要 注 意 , 有 的 谓 词 是 返 回 值 的 , 而 有 些 则 不 会 返 回 值 。 返 回 值 的 谓 词 是 一 个 表 达 式 , 这 样 的 谓 词 调 用<br />

常 称 为 一 个 函 数 调 用 。 常 规 地 , 谓 词 是 返 回 值 的 。<br />

谓 词 的 调 用 是 通 过 应 用 参 数 于 该 谓 词 来 进 行 的 。 谓 词 必 须 具 有 与 参 数 自 由 / 绑 定 状 态 相 匹 配 的 流 样 式 。<br />

大 多 数 谓 词 是 由 一 组 子 句 定 义 的 , 但 有 的 谓 词 是 由 语 言 内 建 的 , 还 有 的 是 在 外 部 的 DLL 中 定 义 的 ( 可<br />

能 还 是 另 外 的 编 程 语 言 )。<br />

当 调 用 谓 词 时 , 各 个 子 句 依 次 执 行 直 到 其 中 的 一 个 成 功 , 或 是 再 没 有 可 执 行 的 子 句 。 如 果 没 有 子 句 成<br />

功 则 谓 词 就 失 败 了 。<br />

如 果 一 个 子 句 成 功 了 而 还 剩 有 子 句 , 就 会 有 一 个 回 溯 点 创 建 在 下 一 个 相 关 的 子 句 上 。<br />

这 样 , 谓 词 可 以 失 败 , 成 功 , 甚 至 成 功 多 次 。<br />

每 个 子 句 有 头 部 及 可 选 的 体 部 。<br />

调 用 谓 词 时 , 子 句 被 依 次 ( 从 上 到 下 ) 求 值 。 每 个 子 句 在 头 部 的 参 数 与 调 用 的 参 数 进 行 合 一 。 如 果 合<br />

一 成 功 , 有 体 部 时 才 会 执 行 体 部 。 只 有 头 部 成 功 且 体 部 也 成 功 , 子 句 才 成 功 , 否 则 就 失 败 了 。<br />

例<br />

clauses<br />

ppp() :- qqq(X), write(X), fail.<br />

qqq(1).<br />

qqq(2).<br />

qqq(3).<br />

调 用 ppp 时 它 接 着 会 调 用 qqq。 当 qqq 被 调 用 时 , 它 首 先 在 第 二 个 子 句 上 创 建 一 个 回 溯 点 , 然 后 执 行<br />

第 一 个 子 句 。 这 时 ppp 中 的 自 由 变 量 X 与 数 字 1 匹 配 , 这 样 就 将 X 绑 定 为 1。<br />

接 着 ,ppp 的 X( 也 就 是 1) 被 写 出 , 然 后 fail 导 致 回 溯 到 回 溯 点 。 因 而 程 序 控 制 会 转 到 qqq 的 第 二 个<br />

子 句 , 而 程 序 的 状 态 也 会 回 到 第 一 次 进 入 qqq 时 的 状 态 , 即 ppp 中 的 X 又 是 未 绑 定 的 了 。<br />

在 实 际 执 行 qqq 中 的 第 二 个 子 句 之 前 , 会 先 在 第 三 个 子 句 上 创 建 一 个 回 溯 点 。 接 下 来 的 过 程 与 1 的 一<br />

样 。<br />

Unification( 合 一 )<br />

当 调 用 一 个 谓 词 时 , 调 用 的 参 数 会 与 各 子 句 的 头 部 中 的 项 进 行 合 一 。<br />

所 谓 合 一 , 就 是 变 量 绑 定 的 过 程 , 它 使 两 个 项 相 等 , 并 且 尽 量 少 做 绑 定 ( 也 就 是 尽 可 能 多 地 留 下 以 后<br />

绑 定 的 余 地 )。<br />

56


变 量 可 以 绑 定 于 任 何 种 类 的 项 , 包 括 变 量 或 含 有 变 量 的 项 。 而 合 一 可 以 是 可 能 的 , 也 可 以 是 不 可 能 的 ,<br />

也 就 是 说 , 可 以 成 功 , 或 是 失 败 。<br />

变 量 及 其 要 绑 定 的 项 是 有 类 型 的 , 一 个 变 量 只 能 和 与 它 同 类 型 的 或 是 其 子 类 型 的 项 做 绑 定 。 如 果 两 个<br />

变 量 是 互 相 绑 定 的 , 则 它 们 必 须 是 相 同 的 类 型 。<br />

合 一 出 现 在 一 个 谓 词 调 用 和 子 句 的 头 部 间 。 当 两 个 项 进 行 相 等 比 较 时 也 会 出 现 合 一 。<br />

例 考 虑 两 个 项 ( 同 一 类 型 的 ):<br />

T1 = f1(g(), X, 17, Y, 17)<br />

T2 = f1(Z, Z, V, U, 43)<br />

我 们 来 试 试 把 这 两 个 项 从 左 到 右 进 行 合 一 。T1 和 T2 都 是 f1/5 项 , 是 匹 配 的 。 因 此 , 我 们 来 试 着 由 T1<br />

的 参 数 合 一 T2 相 应 的 参 数 。 首 先 要 合 一 Z 和 g(), 这 只 要 把 Z 绑 定 为 g() 就 可 以 了 。 目 前 为 止 还 不 错 , 我 们<br />

得 到 了 第 一 个 绑 定 :<br />

Z = g()<br />

接 下 来 第 二 个 参 数 是 X 和 Z, 而 后 者 已 经 绑 定 为 g() 了 。 只 要 让 X 绑 定 为 g(), 这 两 个 参 数 也 是 可 以 合<br />

一 的 , 这 样 我 们 有 了 第 二 个 绑 定 :<br />

X = Z = g()<br />

再 接 下 来 , 必 须 把 V 绑 定 为 17, 还 要 把 Y 绑 定 为 U。 现 在 已 经 绑 定 的 有 :<br />

X = Z = g()<br />

V = 17<br />

Y = U<br />

两 个 合 一 的 项 现 在 等 价 于 下 面 的 项 :<br />

T1 = f1(g(), g(), 17, Y, 17)<br />

T2 = f1(g(), g(), 17, Y, 43)<br />

但 是 我 们 还 没 有 合 一 最 后 两 个 参 数 , 这 两 个 参 数 是 17 和 43。 没 有 什 么 变 量 绑 定 能 使 这 两 个 项 相 等 ,<br />

所 以 最 终 合 一 失 败 了 。T1 和 T2 不 能 合 一 。<br />

上 面 这 个 例 子 中 ,T1 可 以 是 一 个 谓 词 调 用 而 T2 可 以 是 一 个 子 句 的 头 部 。 当 然 , 它 们 也 可 以 是 两 个 用<br />

“=” 比 较 的 项 。<br />

Matching( 匹 配 )<br />

匹 配 只 有 一 点 与 合 一 不 同 , 这 就 是 匹 配 时 变 量 只 能 绑 定 于 根 基 (grounded) 项 。 所 谓 根 基 项 , 就 是<br />

不 含 任 何 未 绑 定 变 量 的 项 。<br />

正 是 说 明 谓 词 的 流 样 式 , 使 得 我 们 可 以 用 匹 配 而 不 用 全 面 的 合 一 成 为 可 能 。<br />

例<br />

clauses<br />

ppp(Z, Z, 17).<br />

qqq() :-<br />

ppp(g(), X, 17).<br />

57


调 用 ppp 时 合 一 ppp 的 子 句 , 只 要 使 用 下 面 的 绑 定 , 是 可 行 的 :<br />

Z = X = g()<br />

如 果 ppp 的 流 是 (i,o,i), 合 一 就 只 是 一 个 匹 配 :<br />

• g() 是 个 输 入 , 作 为 第 一 个 参 数 , 它 绑 定 于 Z,<br />

• 子 句 中 第 二 个 参 数 因 此 也 可 以 绑 定 并 可 以 输 出 给 X, 这 样 X 就 变 成 绑 定 于 g(),<br />

• 最 后 , 第 三 个 参 数 是 17, 它 是 一 个 输 入 , 这 个 数 只 是 简 单 地 与 子 句 中 的 第 三 个 参 数 作 比 较 。<br />

可 以 看 到 , 正 是 流 样 式 使 得 有 可 能 预 知 子 句 并 不 需 要 实 际 进 行 合 一 。<br />

嵌 套 的 函 数 调 用<br />

要 合 一 或 匹 配 的 项 可 以 包 含 子 项 , 而 子 项 是 表 达 式 或 函 数 调 用 , 它 们 需 要 在 合 一 / 匹 配 完 成 之 前 进 行<br />

求 值 。<br />

这 样 的 子 项 求 值 是 按 需 要 进 行 的 。<br />

在 谓 词 调 用 时 , 所 有 输 入 参 数 在 谓 词 调 用 前 做 求 值 , 所 有 输 出 参 数 是 变 量 , 不 需 要 求 值 。<br />

子 句 头 部 也 可 以 包 含 需 要 求 值 的 项 , 在 合 一 / 匹 配 前 确 定 下 来 。<br />

• 在 求 值 进 行 之 前 , 先 做 不 需 要 做 求 值 处 理 的 合 一 / 匹 配 ;<br />

• 然 后 相 应 于 输 入 的 参 数 从 左 到 右 逐 个 求 值 。 求 值 一 个 就 与 对 应 的 输 入 比 较 一 个 ;<br />

• 接 着 对 子 句 的 体 求 值 ;<br />

• 再 从 左 到 右 对 输 出 参 数 求 值 ;<br />

• 最 后 , 如 果 谓 词 是 函 数 , 就 对 返 回 值 求 值 。<br />

如 果 上 述 过 程 中 出 现 任 何 失 败 , 就 不 进 行 其 它 的 求 值 。<br />

总 之 , 基 本 原 则 是 :<br />

• 输 入 求 值 先 于 体 部 求 值 ;<br />

• 体 部 求 值 之 后 是 输 出 求 值 ;<br />

• 求 值 从 左 向 右 进 行 。<br />

匿 名 谓 词<br />

匿 名 谓 词 是 对 一 个 谓 词 的 值 进 行 计 算 的 表 达 式 , 这 个 值 可 以 绑 定 给 一 个 变 量 , 可 以 作 为 参 数 传 递 或 作<br />

为 结 果 返 回 , 但 这 个 值 没 有 任 何 类 、 接 口 或 实 现 的 名 称 。<br />

匿 名 谓 词 可 以 在 表 达 式 出 现 的 关 联 中 获 取 值 , 可 以 用 它 来 避 免 一 些 冗 长 难 懂 的 代 码 。<br />

语 法<br />

匿 名 谓 词 是 项 :<br />

Term : one of<br />

...<br />

AnonymousPredicate<br />

...<br />

匿 名 谓 词 是 一 个 在 大 括 号 中 的 无 名 子 句 , 某 些 部 分 是 可 选 的 , 形 式 如 下 :<br />

AnonymousPredicate : one of<br />

{ ( Arg-comma-sep-list ) = Term }<br />

58


{ ( Arg-comma-sep-list ) = Term :- Term }<br />

{ ( Arg-comma-sep-list ) :- Term }<br />

{ = Term }<br />

{ = Term :- Term }<br />

{ :- Term }<br />

省 略 参 数 表 意 味 着 “ 需 要 数 量 的 参 数 ”, 用 在 不 需 要 实 际 使 用 参 数 时 。<br />

语 义<br />

匿 名 谓 词 表 达 式 对 一 个 谓 词 求 值 。 看 下 面 的 代 码 :<br />

clauses<br />

run() :-<br />

Inc = { (X) = X+1 },<br />

A = Inc(4),<br />

B = Inc(23),<br />

stdio::writef("A = %, B = %", A, B).<br />

Inc 成 了 一 个 增 量 谓 词 , 程 序 的 结 果 是 :<br />

A = 5, B = 24<br />

上 面 的 代 码 相 当 于 这 样 :<br />

clauses<br />

run() :-<br />

Inc = inc,<br />

A = Inc(4),<br />

B = Inc(23),<br />

stdio::writef("A = %, B = %", A, B).<br />

class predicates<br />

inc : (integer X) -> integer R.<br />

clauses<br />

inc(X) = X+1.<br />

这 里 , 最 后 一 行 可 以 看 到 子 句 (X) = X+1, 在 这 里 它 是 一 个 命 名 了 的 谓 词 。<br />

在 匿 名 谓 词 之 外 ( 即 匿 名 谓 词 出 现 之 前 ) 绑 定 的 变 量 可 以 在 匿 名 谓 词 中 使 用 , 变 量 的 值 将 被 匿 名 谓 词<br />

获 取 。 而 在 匿 名 谓 词 中 绑 定 的 变 量 是 该 匿 名 谓 词 的 局 部 变 量 。<br />

获 取 关 联<br />

匿 名 谓 词 可 以 获 取 关 联 , 也 就 是 说 它 可 以 引 用 关 联 中 定 义 的 东 西 , 尤 其 是 从 子 句 中 引 用 事 实 和 变 量 。<br />

获 取 变 量<br />

匿 名 谓 词 是 出 现 在 子 句 中 的 , 而 这 个 子 句 可 能 含 有 变 量 。 对 那 些 在 匿 名 谓 词 出 现 之 前 绑 定 的 变 量 , 在<br />

匿 名 谓 词 中 可 以 使 用 。 下 面 的 代 码 说 明 了 如 何 获 取 变 量 的 :<br />

domains<br />

pred = (integer) -> integer.<br />

59


class predicates<br />

createAdder : (integer A) -> pred Adder.<br />

clauses<br />

createAdder(A) = { (X) = X+A }.<br />

clauses<br />

run() :-<br />

Add17 = createAdder(17),<br />

A = Add17(4),<br />

B = Add17(20),<br />

stdio::writef("A = %, B = %", A, B).<br />

用 17 做 参 数 调 用 createAdder, 这 时 createAdder 子 句 中 的 A 是 17, 因 而 结 果 是 { (X) = X+17 }。<br />

我 们 说 匿 名 谓 词 获 取 了 变 量 A。Add17 是 一 个 把 17 加 到 自 身 参 数 的 谓 词 , 输 出 的 结 果 就 是 :<br />

获 取 省 略 符<br />

A = 21, B = 37<br />

匿 名 谓 词 可 以 获 取 省 略 符 变 量 :<br />

clauses<br />

ppp(...) :-<br />

W = { () :- stdio::write(...) },<br />

qqq(W).<br />

W 获 取 了 省 略 符 变 量 。qqq 收 到 一 个 0 元 维 的 谓 词 , 当 这 个 谓 词 被 调 用 时 , 获 取 的 省 略 符 变 量 就 会 写<br />

到 标 准 输 出 设 备 上 去 。<br />

获 取 事 实<br />

匿 名 谓 词 可 以 获 取 事 实 。 如 果 匿 名 变 量 是 由 类 谓 词 创 建 的 , 它 就 可 以 访 问 类 事 实 ; 如 果 是 由 对 象 谓 词<br />

创 建 的 , 它 就 既 可 以 访 问 对 象 事 实 又 可 以 访 问 类 事 实 。 下 面 的 代 码 获 取 的 是 类 事 实 :<br />

class facts<br />

count : integer := 0.<br />

clauses<br />

seq() = { () = count :- count := count+1 }.<br />

clauses<br />

run() :-<br />

A = seq(),<br />

B = seq(),<br />

stdio::writef("A1 = %, ", A()),<br />

stdio::writef("B1 = %, ", B()),<br />

stdio::writef("A2 = %, ", A()),<br />

stdio::writef("B2 = %", B()).<br />

A 和 B 都 会 使 类 事 实 计 数 增 加 , 所 以 结 果 是 :<br />

A1 = 1, B1 = 2, A2 = 3, B2 = 4<br />

对 象 谓 词 中 可 以 获 取 对 象 事 实 。 假 设 seq 是 myClass 中 的 对 象 谓 词 , 下 面 的 代 码 说 明 了 获 取 对 象 事 实 :<br />

60


facts<br />

count : integer := 0.<br />

clauses<br />

seq() = { () = count :- count := count+1 }.<br />

clauses<br />

run() :-<br />

A = myClass::new():seq(),<br />

B = myClass::new():seq(),<br />

stdio::writef("A1 = %, ", A()),<br />

stdio::writef("B1 = %, ", B()),<br />

stdio::writef("A2 = %, ", A()),<br />

stdio::writef("B2 = %", B()).<br />

这 里 ,A 和 B 来 自 不 同 的 对 象 , 它 们 有 各 自 的 计 数 , 所 以 输 出 是 :<br />

A1 = 1, B1 = 1, A2 = 2, B2 = 2<br />

技 术 上 来 说 , 类 版 本 的 匿 名 谓 词 其 实 获 取 不 了 什 么 东 西 , 它 很 少 有 权 使 用 事 实 。 同 样 地 , 对 象 版 的 其<br />

实 也 没 有 获 取 事 实 , 它 获 取 的 是 This 并 通 过 This 访 问 了 对 象 事 实 。<br />

获 取 This<br />

嵌 套<br />

如 上 所 述 , 可 以 获 取 This 并 取 得 对 对 象 事 实 的 访 问 权 。 同 样 的 机 制 可 以 调 用 对 象 谓 词 :<br />

clauses<br />

seq() = { () = count :- inc() }.<br />

clauses<br />

inc() :- count := count+1.<br />

This 也 可 以 直 接 使 用 :<br />

clauses<br />

ppp() = { () = aaa::rrr(This) }.<br />

匿 名 谓 词 可 以 嵌 套 :<br />

clauses<br />

run() :-<br />

P = { (A) = { (B) = A+B } },<br />

Q = P(3300),<br />

R = P(2200),<br />

stdio::writef("Q(11) = %, ", Q(11)),<br />

stdio::writef("R(11) = %", R(11)).<br />

要 得 到 Q 用 3300 调 用 P, 这 样 A 是 3300 而 Q 就 是 { (B) = 3300+B} }, 同 样 R 是 { (B) =<br />

2200+B } }, 结 果 是 :<br />

Q(11) = 3311, R(11) = 2211<br />

Syntactic Sugar( 便 利 化 的 语 法 )<br />

61


如 果 不 需 要 参 数 , 可 以 把 它 们 略 过 。 下 面 这 个 代 码 片 断 :<br />

P = { (_) :- succeed },<br />

Q = { (_, _) = 0 },<br />

R = { (_, _, _) = _ :- fail }<br />

可 以 简 化 成 这 样 :<br />

P = { :- succeed },<br />

Q = { = 0 },<br />

R = { = _ :- fail }<br />

注 意 , 参 数 部 分 完 全 被 略 去 了 。 如 果 写 () 则 意 味 着 是 零 参 数 , 而 略 去 所 有 参 数 意 思 是 “ 有 适 当 的 ” 参<br />

数 。<br />

实 际 使 用 举 例<br />

这 节 内 容 显 示 了 匿 名 谓 词 的 便 利 性 。 例 子 中 假 定 PFC 中 的 core、std、stdio、list 和 string 等 范 围 是 开<br />

放 的 。<br />

哑 谓 词<br />

适 配<br />

用 匿 名 谓 词 来 创 建 哑 谓 词 值 是 很 方 便 的 :<br />

ppp( { = true } ), % 不 过 滤 (boolean)<br />

qqq( { :- succeed } ), % 不 过 滤 (determ)<br />

rrr( { = 17 } ), % 所 有 行 高 必 须 为 17<br />

有 的 时 候 要 用 一 个 谓 词 , 而 已 经 有 了 一 个 差 不 多 合 适 的 , 这 时 可 以 用 匿 名 谓 词 来 配 合 以 满 足 需 要 。<br />

适 配 索 引<br />

看 一 下 谓 词 write3:<br />

class predicates<br />

write3 : (function{integer, string} Indexer).<br />

clauses<br />

write3(Indexer) :-<br />

foreach I = std::fromTo(0,2) do<br />

write(Indexer(I), "\n")<br />

end foreach.<br />

Indexer 提 供 了 一 个 串 的 “ 数 组 ”,write3 可 以 写 出 以 索 引 号 为 0、1 和 2 的 三 个 串 。 因 此 ,write3<br />

设 定 “ 数 组 ” 索 引 号 是 从 0 开 始 的 。 不 过 , 我 们 想 用 一 个 索 引 号 从 1 开 始 的 “ 数 组 ”:<br />

class predicates<br />

myArray : (integer N) -> string Value.<br />

clauses<br />

62


参 数 适 配<br />

myArray(1) = "First" :- !.<br />

myArray(2) = "Second" :- !.<br />

myArray(3) = "Third" :- !.<br />

myArray(_) = _ :-<br />

raiseError().<br />

用 一 个 匿 名 谓 词 , 就 可 以 轻 而 易 举 地 使 索 引 号 从 1 开 始 的 数 组 满 足 索 引 号 从 0 开 始 的 使 用 要 求 :<br />

% myArray is 0-based, write3 requires 1-based<br />

Arr = { (N) = myArray(N+1) },<br />

write3(Arr)<br />

这 样 , 我 们 就 可 以 得 到 期 望 的 输 出 :<br />

First<br />

Second<br />

Third<br />

在 下 面 的 代 码 中 ,listChildren 将 为 每 一 个 “C 是 P 的 孩 子 ” 数 据 对 调 用 谓 词 ChildWriter:<br />

class predicates<br />

listChildren :(predicate{string,string} ChildWriter).<br />

clauses<br />

listChildren(CW) :-<br />

CW("Son1", "Father"),<br />

CW("Son2", "Father").<br />

不 过 , 现 在 我 们 想 用 谓 词 wParent 写 出 “P 是 C 的 父 母 ” 的 列 表 来 :<br />

class predicates<br />

wParent : (string Parent, string Child).<br />

clauses<br />

wParent(P, C) :-<br />

writef("% is the parent of %\n", P, C).<br />

wParent 所 用 的 参 数 刚 好 顺 序 是 相 反 的 , 可 以 很 容 易 地 用 一 个 匿 名 谓 词 来 调 整 它 :<br />

Swap = { (A,B) :- wParent(B,A) },<br />

listChildren(Swap)<br />

输 出 就 是 我 们 想 要 的 了 :<br />

Father is the parent of Son1<br />

Father is the parent of Son2<br />

我 们 还 可 以 丢 掉 不 需 要 的 参 数 , 比 如 调 用 谓 词 时 我 们 只 想 要 “Child”:<br />

class predicates<br />

wKnowParent : (string Child).<br />

clauses<br />

wKnowParent(C) :-<br />

writef("We know a parent of %\n", C).<br />

63


可 以 这 样 来 做 需 要 的 适 配 :<br />

Fewer = { (C,P) :- wKnowParent(C) },<br />

listChildren(Fewer)<br />

输 出 会 是 这 样 :<br />

We know a parent of Son1<br />

We know a parent of Son2<br />

我 们 还 可 以 使 用 哑 参 数 :<br />

More = { (_,P) :- addChildren(P, 1) }<br />

listChildren(More)<br />

这 里 的 addChildren 会 “ 把 孩 子 数 加 到 P 中 ”, 因 为 对 应 每 个 孩 子 的 调 用 addChild 都 提 供 了 一 个 “ 哑<br />

参 数 ”1。More 是 提 供 一 个 哑 参 数 并 丢 弃 一 个 参 数 的 适 配 器 。<br />

筛 选<br />

设 有 谓 词 :<br />

class predicates<br />

writeFiltered :(string L, filterPredicate{integer} Filter).<br />

clauses<br />

writeFiltered(Label, Filter) :-<br />

List = [1,2,3,4,5,6,7,8,9],<br />

FilteredList = filter(List, Filter),<br />

writef("%\t%\n", Label, FilteredList).<br />

过 滤 器 Filter 用 来 筛 选 表 [1,2,3,4,5,6,7,8,9], 筛 选 出 来 的 表 和 标 签 Label 写 到 标 准 输 出 设 备 上 。<br />

我 们 先 用 个 全 选 过 滤 器 :<br />

All = { :- succeed },<br />

writeFiltered("All", All)<br />

这 个 过 滤 器 对 任 何 元 素 都 成 功 , 所 以 输 出 是 全 表 :<br />

All [1,2,3,4,5,6,7,8,9]<br />

创 建 一 个 过 滤 器 对 任 何 元 素 都 失 败 也 很 简 单 :<br />

None = { :- fail },<br />

writeFiltered("None", None)<br />

输 出 就 是 个 空 表 :<br />

None []<br />

也 可 以 创 建 一 个 过 滤 器 选 出 大 于 3 的 元 素 和 能 被 3 整 除 的 元 素 :<br />

GreaterThan3 = { (X) :- X > 3 },<br />

64


输 出 是 :<br />

writeFiltered("> 3", GreaterThan3),<br />

Rem3 = { (X) :- 0 = X rem 3 },<br />

writeFiltered("Rem3", Rem3)<br />

> 3 [4,5,6,7,8,9]<br />

Rem3 [3,6,9]<br />

排 序<br />

表 包 里 有 排 序 谓 词 , 不 过 有 时 缺 省 的 顺 序 不 能 满 足 要 求 , 所 以 表 包 里 还 有 一 个 sortBy 谓 词 , 可 以 按 程<br />

序 员 定 义 的 比 较 操 作 对 元 素 排 序 。 我 们 用 这 个 谓 词 先 来 考 虑 一 下 串 的 排 序 :<br />

class predicates<br />

writeStringsSorted :<br />

(string Label, comparator{string} Comp).<br />

clauses<br />

writeStringsSorted(Label, C) :-<br />

List = ["John Wayne", "Uma Thurman",<br />

"Harrison Ford", "Nicolas Cage",<br />

"Elizabeth Taylor", "Cary Grant",<br />

"Jerry Lewis", "Robert De Niro"],<br />

Sorted = sortBy(C, List),<br />

write(Label, "\n"),<br />

foreach S = list::getMember_nd(Sorted) do<br />

writef(" %\n", S)<br />

end foreach.<br />

我 们 可 以 用 “normal” 比 较 符 调 用 谓 词 , 还 可 以 用 一 个 匿 名 谓 词 很 方 便 地 实 现 降 序 排 序 :<br />

Normal = compare,<br />

writeStringsSorted("Normal", Normal),<br />

Descending = { (A,B) = compare(B,A) },<br />

writeStringsSorted("Descending", Descending)<br />

输 出 是 这 样 的 :<br />

Normal<br />

Cary Grant<br />

Elizabeth Taylor<br />

Harrison Ford<br />

Jerry Lewis<br />

John Wayne<br />

Nicolas Cage<br />

Robert De Niro<br />

Uma Thurman<br />

Descending<br />

Uma Thurman<br />

Robert De Niro<br />

Nicolas Cage<br />

John Wayne<br />

Jerry Lewis<br />

Harrison Ford<br />

Elizabeth Taylor<br />

Cary Grant<br />

65


我 们 再 来 排 序 一 些 更 复 杂 的 元 素 。 下 面 的 一 些 人 名 使 用 这 样 的 域 :<br />

domains<br />

person = p(string First, string Last).<br />

作 为 演 示 , 我 们 用 这 个 谓 词 :<br />

class predicates<br />

writePersonsSorted :<br />

(string Label, comparator{person} Comparator).<br />

clauses<br />

writePersonsSorted(Label, C) :-<br />

List = [p("John","Wayne"),<br />

p("Uma","Thurman"),<br />

p("Harrison","Ford"),<br />

p("Nicolas","Cage"),<br />

p("Elizabeth","Taylor"),<br />

p("Cary","Grant"),<br />

p("Jerry","Lewis"),<br />

p("Robert","De Niro")],<br />

Sorted = sortBy(C, List),<br />

write(Label, "\n"),<br />

foreach p(F,L) = list::getMember_nd(Sorted) do<br />

writef(" % %\n", F, L)<br />

end foreach.<br />

同 样 , 我 们 也 可 以 用 正 常 和 降 序 比 较 符 :<br />

Normal = compare,<br />

writePersonsSorted("Normal", Normal),<br />

Descending = { (A,B) = compare(B,A) },<br />

writePersonsSorted("Descending", Descending)<br />

因 为 比 较 谓 词 对 函 子 使 用 从 左 到 右 的 词 典 顺 序 , 结 果 与 前 面 的 一 样 :<br />

Normal<br />

Cary Grant<br />

Elizabeth Taylor<br />

Harrison Ford<br />

Jerry Lewis<br />

John Wayne<br />

Nicolas Cage<br />

Robert De Niro<br />

Uma Thurman<br />

Descending<br />

Uma Thurman<br />

Robert De Niro<br />

Nicolas Cage<br />

John Wayne<br />

Jerry Lewis<br />

Harrison Ford<br />

Elizabeth Taylor<br />

Cary Grant<br />

但 用 更 复 杂 些 的 域 我 们 还 可 以 创 建 一 个 比 较 符 , 不 是 按 名 而 是 按 姓 来 排 序 :<br />

66


获 取 关 联<br />

LN = { (p(_,L1), p(_, L2)) = compare(L1,L2) },<br />

writePersonsSorted("LastName", LN)<br />

这 正 是 我 们 期 望 的 结 果 :<br />

LastName<br />

Nicolas Cage<br />

Robert De Niro<br />

Harrison Ford<br />

Cary Grant<br />

Jerry Lewis<br />

Elizabeth Taylor<br />

Uma Thurman<br />

John Wayne<br />

前 面 说 过 , 匿 名 谓 词 一 个 很 有 用 的 特 性 是 获 取 关 联 的 能 力 。 下 面 的 例 子 说 明 如 何 使 用 这 个 特 性 。<br />

Background threads( 后 台 线 程 )<br />

启 动 线 程 的 例 程 有 一 个 空 元 谓 词 , 会 在 一 个 新 线 程 中 运 行 它 。 不 过 我 们 常 常 需 要 传 递 一 些 输 入 数 据 给<br />

新 线 程 中 的 作 业 。 可 以 有 多 种 办 法 , 但 最 简 单 的 办 法 是 用 匿 名 谓 词 。<strong>Visual</strong> <strong>Prolog</strong> 例 集 ( 可 以 在 IDE 中 安<br />

装 ) 中 的 bgDemo 工 程 用 的 就 是 这 个 办 法 。 这 个 工 程 中 有 一 个 表 格 , 可 以 启 动 后 台 作 业 , 并 用 表 格 中 一<br />

个 jobControl 控 件 显 示 作 业 的 状 态 信 息 。 后 台 作 业 是 一 个 谓 词 , 它 接 收 jobLog, 这 个 控 件 可 以 用 来 报 告<br />

状 态 和 进 度 :<br />

domains<br />

job = (jobLog Log).<br />

jobLog 是 这 样 的 :<br />

interface jobLog<br />

properties<br />

completion : real (i).<br />

properties<br />

status : string (i).<br />

end interface jobLog<br />

作 业 通 过 设 置 完 成 属 性 (0 到 1) 来 报 告 完 成 进 度 。 同 样 , 状 态 属 性 可 以 用 来 反 映 作 业 的 当 前 状 态 。 状<br />

态 和 完 成 进 度 用 一 个 作 业 名 显 示 在 表 格 里 。 作 业 的 启 动 是 通 过 调 用 表 格 的 addJob 谓 词 :<br />

clauses<br />

addJob(JobName, Job) :-<br />

JobCtrl = jobControl::new(This),<br />

JobCtrl:name := JobName,<br />

JobCtrl:show(),<br />

assert(jobCtrl_fact(JobCtrl)),<br />

arrange(),<br />

JobLog = jobLog::new(JobCtrl),<br />

Action = { :- Job(JobLog) },<br />

_ = thread::start(Action).<br />

67


上 面 最 后 三 行 是 我 们 关 心 的 关 联 。thread::start 用 一 个 空 元 谓 词 作 参 数 , 但 作 业 是 一 个 以 jobLog<br />

为 参 数 的 谓 词 。 因 此 , 我 们 用 了 一 个 匿 名 谓 词 Action, 它 没 有 参 数 但 在 JobLog 上 调 用 Job。 匿 名 谓 词 从<br />

关 联 中 既 获 取 了 Job 又 获 取 了 JobLog, 因 而 它 们 的 值 可 以 传 递 给 新 线 程 , 尽 管 线 程 得 到 的 是 一 个 空 元 谓<br />

词 。bgDemo 工 程 中 的 作 业 差 不 多 是 个 空 作 业 , 只 是 操 控 jobLog。 作 业 是 这 个 样 子 的 :<br />

clauses<br />

job(Log, From, To) :-<br />

Log:status := "Step 1",<br />

foreach N1 = std::fromTo(From, To) do<br />

Log:completion :=<br />

(N1-From) / (To-From) / 2,<br />

programControl::sleep(3)<br />

end foreach,<br />

Log:status := "Step 2",<br />

foreach N2 = std::fromTo(From, To) do<br />

Log:completion :=<br />

(N2-From) / (To-From) / 2 + 0.5,<br />

programControl::sleep(3)<br />

end foreach,<br />

Log:status := "finished".<br />

从 From 到 To 这 里 有 两 个 循 环 , 计 算 完 成 进 度 并 把 它 设 置 给 Log。 它 还 要 在 循 环 前 , 循 环 中 和 循 环 后<br />

设 置 状 态 文 字 。 可 以 看 到 , 这 个 作 业 没 有 固 有 的 作 业 类 型 , 因 为 一 个 特 定 的 作 业 只 有 一 个 参 数 (jobLog),<br />

而 这 个 作 业 有 三 个 参 数 。 还 是 匿 名 谓 词 帮 了 忙 。<br />

在 表 格 中 添 加 作 业 的 代 码 是 这 样 的 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(_Source, _MenuTag) :-<br />

JF = jobForm::display(This),<br />

Job11 = {(L) :- job1::job(L, 1, 1000)},<br />

Job12 = {(L) :- job1::job(L, 200, 600)},<br />

Job13 = {(L) :- job1::job(L, 1200, 3000)},<br />

Job14 = {(L) :- job1::job(L, 1, 1000)},<br />

JF:addJob("job1.1", Job11),<br />

JF:addJob("job1.2", Job12),<br />

JF:addJob("job1.3", Job13),<br />

JF:addJob("job1.4", Job14),<br />

...<br />

在 实 际 的 程 序 中 ,From 和 To 不 会 是 常 数 , 它 会 是 从 别 的 地 方 传 递 来 的 参 数 。 在 这 种 情 况 下 , 匿 名 谓<br />

词 也 会 从 关 联 中 获 取 变 量 。 在 bgDemo 中 的 jobLog 说 明 了 匿 名 谓 词 又 一 种 使 用 方 法 , 它 传 递 完 成 进 度 和<br />

状 态 信 息 给 一 个 控 件 jobControl。jobControl 是 jobForm 中 的 一 个 GUI 控 件 , 它 以 适 当 的 形 式 表 现 得<br />

到 的 信 息 。 不 过 , 这 里 有 个 同 步 的 问 题 , 因 为 GUI 控 件 在 线 程 级 并 不 稳 定 , 而 在 这 里 我 们 是 从 一 个 后 台 线<br />

程 中 更 新 控 件 的 。 这 有 可 能 导 致 冲 突 , 因 为 是 主 线 程 在 画 该 控 件 。 解 决 的 办 法 是 把 控 件 的 更 新 交 给 GUI 线<br />

程 。 可 以 通 过 把 动 作 传 递 给 控 件 来 这 样 做 。 状 态 更 新 的 实 现 是 这 样 的 :<br />

clauses<br />

status(Status) :-<br />

Action = { :- jobCtrl:status := Status },<br />

jobCtrl:postAction(Action).<br />

68


Action 是 一 个 空 元 谓 词 , 它 会 设 置 jobCtrl 中 的 状 态 。 我 们 把 这 个 动 作 传 递 给 jobCtrl, 当 它 接 收 到<br />

action 时 就 会 调 用 Action 完 成 更 新 。 这 样 , 控 件 的 实 际 更 新 是 由 GUI 线 程 完 成 的 , 这 个 匿 名 谓 词 不 仅 获 取<br />

Status 变 量 而 且 获 取 了 jobCtrl 事 实 。<br />

匿 名 回 叫<br />

考 虑 一 下 给 远 端 服 务 发 送 命 令 的 情 况 。 命 令 的 执 行 是 异 步 的 , 所 以 执 行 一 个 命 令 也 还 需 要 一 个 回 叫 动<br />

作 , 执 行 完 了 命 令 就 调 用 这 个 动 作 。 也 就 是 说 , 为 执 行 命 令 需 要 调 用 这 样 的 谓 词 :<br />

predicates<br />

executeCommand :<br />

(command Cmd, predicate{} OnDone).<br />

基 于 这 个 谓 词 , 还 可 以 创 建 一 个 可 以 执 行 一 个 命 令 表 的 谓 词 , 前 一 个 命 令 执 行 完 了 接 着 执 行 下 一 个 命<br />

令 。 执 行 命 令 表 也 是 异 步 的 , 所 以 命 令 脚 本 都 执 行 完 了 也 需 要 调 用 一 个 动 作 。 脚 本 执 行 的 形 式 是 这 样 的 :<br />

predicates<br />

executeScript :<br />

(command* Script, predicate{} OnDone).<br />

如 果 脚 本 空 了 , 就 调 用 OnDone。 若 脚 本 有 一 个 命 令 H 及 剩 下 的 脚 本 T, 必 须 先 执 行 H, 完 成 之 后 执<br />

行 剩 下 的 脚 本 T。So the OnDone action we supply when executing H must execute T. 总 之 , 实 现<br />

会 是 这 样 的 :<br />

clauses<br />

executeScript([], OnDone) :-<br />

OnDone().<br />

executeScript([H|T], OnDone) :-<br />

DoneH = { :- executeScript(T, OnDone) },<br />

executeCommand(H, DoneH).<br />

我 们 用 了 一 个 匿 名 谓 词 来 完 成 剩 余 脚 本 的 执 行 , 这 个 谓 词 获 取 T 和 OnDone。<br />

事 实 变 量 的 赋 值<br />

赋 值 算 子 := 用 于 给 事 实 变 量 FactVariable 赋 予 新 值 。Term 必 须 能 求 值 得 到 适 当 的 类 型 ( 与 事 实 变<br />

量 相 同 的 类 型 或 是 其 子 类 型 )。<br />

事 实<br />

FactVariableAssignment:<br />

FactVariable := Term<br />

事 实 数 据 库 包 含 有 若 干 完 全 实 例 化 的 ( 根 基 ) 谓 词 头 部 的 相 应 事 实 , 这 些 事 实 是 在 事 实 段 声 明 的 。 事<br />

实 可 以 由 谓 词 调 用 来 访 问 , 只 要 把 事 实 名 当 谓 词 名 就 行 了 。 各 个 事 实 依 次 与 谓 词 调 用 进 行 匹 配 , 每 次 谓 词<br />

调 用 与 事 实 匹 配 了 则 成 功 , 可 能 还 会 带 有 对 下 一 个 事 实 的 回 溯 点 ; 当 事 实 数 据 库 中 再 也 没 有 其 它 的 事 实 时<br />

这 个 谓 词 调 用 就 失 败 了 。<br />

可 以 用 谓 词 assert/1、asserta/1 和 assertz/1 插 入 新 的 事 实 。assert/1 和 assertz/1 是 一 样 的 , 把 新 事<br />

实 插 入 到 事 实 列 表 的 尾 部 , 而 asserta/1 则 把 新 事 实 插 入 到 列 表 的 开 头 。<br />

69


已 有 的 事 实 可 以 用 谓 词 retract/1 和 retractAll 撤 消 。retract/1 撤 消 第 一 个 与 参 数 匹 配 的 事 实 , 并 且 可<br />

以 通 过 回 溯 撤 消 更 多 的 事 实 。retractAll 能 撤 消 所 有 与 参 数 匹 配 的 事 实 , 它 总 能 成 功 。<br />

运 算 符<br />

运 算 符 ( 算 子 ) 按 优 先 级 分 组 , 下 面 各 组 的 算 子 在 组 内 具 有 相 同 的 优 先 级 , 而 在 前 面 的 组 优 先 级 高 于<br />

后 面 组 的 优 先 级 。 如 , 一 元 加 减 号 ( 也 就 是 正 负 号 ) 优 先 级 高 于 幂 算 子 , 而 幂 算 子 的 优 先 级 又 高 于 乘 法 算<br />

子 , 等 等 。 圆 括 号 可 以 用 来 标 示 优 先 级 使 之 更 清 晰 。<br />

UnaryOperator: one of<br />

- +<br />

BinaryOperator: one of<br />

PowerOperator<br />

MultiplicationOperator<br />

AdditionOperator<br />

RelationOperator<br />

AndOperator<br />

OrOperator<br />

只 有 幂 算 子 是 右 结 合 的 , 其 它 所 有 算 子 都 是 左 结 合 的 。<br />

例 下 面 的 项 :<br />

7 + 3 * 5 * 13 + 4 + 3 = X / 6 ; A < 7, p(X)<br />

与 下 面 的 项 是 一 样 的 :<br />

((((7 + ((3 * 5) * 13)) + 4) + 3) = (X / 6)) ; ((A < 7) , p(X))<br />

也 就 是 说 , 最 外 层 是 两 个 “ 或 ” 关 系 的 项 , 其 中 第 一 个 项 是 一 个 “ 等 号 ” 关 系 项 而 第 二 个 是 一 个 “ 与 ”<br />

项 。<br />

算 术 运 算 符<br />

算 术 运 算 符 用 于 对 数 进 行 算 术 运 算 。 它 们 是 表 达 式 , 并 以 表 达 式 为 参 数 。 参 数 是 根 类 型 的 , 而 返 回 的<br />

结 果 具 有 通 用 类 型 ( 参 看 通 用 类 型 和 根 类 型 )。<br />

关 系 运 算 符<br />

PowerOperator:<br />

^<br />

MultiplicationOperator: one of<br />

* / div mod quot rem<br />

AdditionOperator: one of<br />

+ -<br />

关 系 运 算 符 是 公 式 , 以 表 达 式 为 参 数 。 由 于 这 个 特 性 , 所 以 它 们 是 非 结 合 的 。<br />

70


RelationOperator: one of<br />

= > < >= <<br />

先 对 左 边 的 项 做 计 算 , 而 后 是 右 边 的 , 最 后 将 结 果 做 比 较 。<br />

注 意 , 并 非 双 算 子 的 =, 是 比 较 两 个 值 , 而 = 是 要 合 一 两 个 项 ( 至 少 通 常 情 况 下 是 这 样 )。<br />

对 偶 的 表 达 式 A = B 与 (A = B) 也 是 有 差 别 的 。<br />

逻 辑 运 算 符<br />

与 算 子 (AndOperator) 和 或 算 子 (OrOperator) 是 公 式 , 以 公 式 为 参 数 , 它 们 都 是 左 结 合 的 。<br />

“,” 和 “and” 是 一 回 事 ,“;” 和 “or” 也 是 一 回 事 。<br />

AndOperator: one of<br />

, and<br />

OrOperator: one of<br />

; or<br />

And (,)<br />

一 个 “and” 项 “A, B” 的 处 理 过 程 如 下 : 先 对 左 子 项 A 求 值 , 如 果 求 值 失 败 , 整 个 “and” 项 就 失<br />

败 了 。 如 果 A 成 功 了 , 再 对 右 子 项 B 求 值 , 如 果 失 败 ,“and” 项 失 败 。 否 则 “and” 项 就 成 功 了 。 因 此 ,<br />

如 果 子 项 A 成 功 了 , 只 需 要 对 第 二 个 子 项 B 求 值 就 行 了 。<br />

例<br />

clauses<br />

ppp() :-<br />

qqq(), rrr().<br />

当 调 用 ppp 时 , 首 先 会 调 用 qqq, 如 果 qqq 成 功 了 , 则 会 调 用 rrr。 如 果 rrr 成 功 , 则 “and” 项 成 功 ,<br />

进 而 整 个 子 句 成 功 。<br />

Or (;)<br />

一 个 “or” 项 “A; B” 的 处 理 过 程 如 下 : 首 先 在 第 二 项 B 上 创 建 一 个 回 溯 点 , 然 后 对 第 一 项 A 求 值 。<br />

如 果 求 值 成 功 , 则 整 个 “or” 项 成 功 , 并 留 有 一 个 第 二 项 B 的 回 溯 点 。 如 果 第 一 项 求 值 失 败 了 , 第 二 项 上<br />

的 回 溯 点 就 被 激 活 。 激 活 后 ( 无 论 是 第 一 项 失 败 而 激 活 的 还 是 以 后 调 用 过 程 中 激 活 的 ), 就 对 第 二 项 B 求<br />

值 , 如 果 B 成 功 则 整 个 “or” 项 成 功 。 因 此 , 一 个 “or” 项 可 以 在 一 个 回 溯 点 上 成 功 , 而 回 溯 中 仅 需 要 对<br />

第 二 个 子 项 B 求 值 。<br />

注 意 , 目 前 “or” 项 只 能 用 在 子 句 体 的 最 外 层 , 或 是 用 在 目 标 中 。 以 后 的 版 本 会 对 此 作 出 修 改 。<br />

例<br />

clauses<br />

ppp() :-<br />

qqq(V), write(V), fail.<br />

qqq(X) :-<br />

X = 3 ; X = 7.<br />

调 用 ppp 时 首 先 调 用 qqq。qqq 会 先 在 X = 7 上 创 建 一 个 回 溯 点 , 再 计 算 X = 3。 这 时 ,X 会 绑 定 为 3,<br />

qqq 返 回 。 而 这 时 ppp 中 的 V 会 绑 定 为 3, 写 V( 即 3) 然 后 碰 到 fail,fail 总 是 失 败 , 这 样 就 又 会 回 到 qqq<br />

71


中 的 回 溯 点 。 回 溯 会 解 除 创 建 回 溯 点 以 来 所 有 的 绑 定 , 所 以 ppp 中 的 V 和 qqq 中 的 X 绑 定 都 解 除 了 。 然 后<br />

计 算 qqq 中 的 X = 7,X 被 绑 定 为 7, 回 到 ppp 中 ,V 被 绑 定 为 7 并 写 出 来 。 又 碰 到 fail, 此 时 在 ppp 中 已 经<br />

没 有 回 溯 点 了 , 所 以 ppp 失 败 。<br />

深 层 嵌 套 的 “or”<br />

可 以 在 子 句 中 嵌 套 “or”。<br />

clauses<br />

p(X) = Y :-<br />

(X = 1, !, Z = 3 or Z = 7), Y = 2*Z.<br />

这 里 使 用 了 关 键 字 “or”, 但 也 可 以 用 分 号 “;”, 推 荐 使 用 “or”。 它 主 要 是 用 在 条 件 测 试 中 :<br />

clauses<br />

isOutside(X) :-<br />

(X < 10 or X > 90),!.<br />

“or” 是 一 个 非 确 定 性 的 结 构 , 因 此 很 多 情 况 下 需 要 用 截 断 使 代 码 是 确 定 性 的 。 尤 其 是 在<br />

“if-then-else” 结 构 中 , 需 要 使 用 截 断 。<br />

clauses<br />

p(X) :-<br />

if (X = 1 or X > 17),! then<br />

...<br />

end if.<br />

今 后 的 版 本 中 非 确 定 性 条 件 是 允 许 的 , 因 而 截 断 会 隐 含 在 这 样 的 条 件 中 。 这 种 变 化 是 向 后 兼 容 的 , 已<br />

有 程 序 不 需 要 改 变 。<br />

Not<br />

not/1 用 一 个 项 作 参 数 。not(A) 的 计 算 , 首 先 计 算 A, 如 果 A 成 功 , 则 not(A) 失 败 , 如 果 A 失 败 , 则<br />

not(A) 成 功 。<br />

注 意 ,not(A) 不 会 绑 定 任 何 变 量 。 因 为 如 果 not(A) 成 功 则 A 会 失 败 , 而 失 败 的 项 不 会 绑 定 任 何 东 西 ;<br />

而 如 果 not(A) 失 败 , 也 不 会 绑 定 任 何 变 量 , 因 为 该 项 本 身 是 失 败 的 。 还 要 注 意 ,not(A) 不 会 在 带 有 回 溯<br />

点 的 情 况 下 成 功 , 因 为 如 果 not(A) 成 功 则 A 是 失 败 的 , 而 失 败 的 项 不 可 能 还 有 回 溯 点 。 这 也 就 意 味 着 , 所<br />

有 A 成 功 的 可 能 都 已 经 穷 尽 了 。<br />

fail/0 和 succeed/0<br />

fail/0 和 succeed/0 是 两 个 内 建 的 空 元 谓 词 , 前 者 总 是 失 败 而 后 者 总 是 成 功 , 此 外 它 们 没 有 别 的 效 果 。<br />

Cut( 截 断 )<br />

截 断 “!” 将 进 入 当 前 谓 词 以 来 创 建 的 所 有 回 溯 点 都 消 除 掉 , 这 包 括 对 后 继 子 句 的 所 有 回 溯 点 , 以 及 谓<br />

词 调 用 在 当 前 子 句 “!” 之 前 建 立 的 回 溯 点 。<br />

Cut:<br />

72


!<br />

例<br />

clauses<br />

ppp(X) :-<br />

X > 7,<br />

!,<br />

write("Greater than seven").<br />

ppp(_X) :-<br />

write("Not greater than seven").<br />

执 行 ppp 时 , 首 先 在 第 二 个 子 句 上 创 建 一 个 回 溯 点 , 然 后 执 行 第 一 个 子 句 。 如 果 “X > 7” 成 功 了 ,<br />

就 碰 到 了 截 断 “!”, 它 会 消 除 第 二 个 子 句 上 的 回 溯 点 。<br />

例<br />

clauses<br />

ppp() :-<br />

qqq(X),<br />

X > 7,<br />

!,<br />

write("Found one").<br />

ppp() :-<br />

write("Did not find one").<br />

clauses<br />

qqq(3).<br />

qqq(12).<br />

qqq(13).<br />

当 执 行 ppp 时 首 先 在 ppp 第 二 个 子 句 创 建 一 个 回 溯 点 , 接 着 调 用 qqq。 而 qqq 会 在 它 的 第 二 个 子 句 上<br />

创 建 一 个 回 溯 点 并 执 行 第 一 个 子 句 , 返 回 值 3。 这 样 , 在 ppp 中 的 X 就 绑 定 为 这 个 值 并 与 7 相 比 较 , 它 失 败<br />

了 , 控 制 就 会 回 退 到 qqq 的 第 二 个 子 句 上 。 在 执 行 qqq 的 第 二 个 子 句 之 前 , 会 在 它 的 第 三 个 子 句 上 创 建 一<br />

个 回 溯 点 , 执 行 第 二 个 子 句 返 回 的 值 是 12。 这 次 与 7 的 比 较 成 功 了 , 执 行 截 断 , 它 会 把 在 qqq 上 剩 下 的 回<br />

溯 点 以 及 在 ppp 第 二 个 子 句 上 的 回 溯 点 都 取 消 。<br />

Cut 范 围<br />

cut 范 围 是 指 截 断 的 作 用 域 , 其 意 义 是 : 一 个 cut, 只 能 撤 消 在 其 作 用 范 围 内 的 回 溯 点 , 而 在 这 个 范 围<br />

之 外 的 回 溯 点 则 不 受 影 响 , 仍 然 保 留 。<br />

谓 词 的 子 句 是 一 个 cut 范 围 , 也 就 是 说 , 一 个 cut( 至 多 ) 会 撤 消 进 入 这 个 谓 词 之 后 创 建 的 回 溯 点 , 在<br />

进 入 这 个 谓 词 之 前 创 建 的 那 些 回 溯 点 将 保 留 下 来 。<br />

例<br />

clauses<br />

aaa() :- p1_nd(), qqq().<br />

qqq() :- p2_nd(), !.<br />

aaa 调 用 p1_nd, 会 留 下 一 个 回 溯 点 , 然 后 又 调 用 qqq;qqq 又 调 用 p2_nd, 也 会 留 下 一 个 回 溯 点 ,<br />

接 着 就 碰 到 一 个 截 断 (cut)。 这 个 截 断 是 在 qqq 谓 词 的 截 断 范 围 内 的 , 所 以 它 只 能 废 掉 p2_nd 上 的 那 个<br />

回 溯 点 , 而 p1_nd 上 的 仍 然 保 留 。<br />

73


有 几 个 项 涉 及 到 截 断 范 围 ( 参 看 list comprehension、if-thenelse、foreach)。 这 里 以 if-then-else<br />

为 例 来 说 明 一 下 截 断 范 围 的 影 响 。 来 看 下 面 这 个 示 例 性 的 if-then-else 项 :<br />

if Cond then T1 else T2 end if<br />

条 件 Cond 是 一 个 截 断 范 围 , 在 Cond 中 的 cut 只 在 Cond 中 起 作 用 。 另 一 方 面 , 在 T1 和 T2 中 的 Cut,<br />

其 作 用 则 会 超 出 if-then-else 的 语 句 。 看 下 面 的 代 码 片 断 :<br />

X = getMember_nd([3,1,2]),<br />

if X = getMember_nd([3,3]), ! then<br />

write(X)<br />

else<br />

!<br />

end if,<br />

fail<br />

getMember_nd 是 一 个 非 确 定 谓 词 。 这 段 代 码 的 计 算 过 程 如 下 : 首 先 X 绑 定 为 3,getMember_nd<br />

留 下 一 个 回 溯 点 ( 这 样 以 后 X 还 可 以 为 1 再 为 2)。 接 着 , 要 计 算 if-then-else 项 的 条 件 , 这 个 条 件 的 第 一<br />

部 分 会 成 功 , 因 为 3 是 [3,3] 的 一 个 成 员 。 第 一 部 分 也 一 样 会 留 下 一 个 回 溯 点 , 这 样 它 就 能 多 次 检 查 X 是 否<br />

为 一 个 成 员 。 接 下 来 , 碰 到 一 个 截 断 , 这 个 截 断 是 在 if-then-else 语 句 条 件 部 分 中 的 , 故 只 在 这 个 范 围 中<br />

起 作 用 , 也 就 是 说 它 只 废 掉 了 第 二 个 getMember_nd 上 的 回 溯 点 , 而 第 一 个 getMember_nd 谓 词 上 的<br />

回 溯 点 仍 然 保 留 。 整 个 条 件 部 分 成 功 , 现 在 进 入 到 then- 部 分 , 写 出 “3”。if-then-else 之 后 我 们 遇 到<br />

了 fail, 这 样 就 又 回 溯 到 第 一 个 getMember_nd, 它 又 绑 定 X 为 1, 留 下 一 个 回 溯 点 ( 以 后 再 回 溯 时 可 以<br />

使 X 为 2)。 再 次 进 行 if-then-else 的 条 件 部 分 的 计 算 , 条 件 的 第 一 部 分 失 败 了 , 因 为 1 不 是 [3,3] 的 成 员 。<br />

这 样 就 进 入 到 else- 部 分 , 在 这 儿 遇 到 一 个 截 断 , 这 个 cut 是 在 选 择 项 的 else- 部 分 的 , 它 的 作 用 会 超 越<br />

if-then-else 项 , 因 此 第 一 个 getMember_nd 上 的 回 溯 点 被 撤 消 了 。 在 if-then-else 项 之 后 遇 到 了 fail,<br />

而 此 时 代 码 中 已 经 没 有 回 溯 点 , 这 段 代 码 就 失 败 了 , 而 X 总 也 不 会 为 2。<br />

List Comprehension( 述 求 表 )<br />

ListComprehensionTerm :<br />

[ Term || Term ]<br />

述 求 表 项 是 一 个 表 的 表 达 式 。 看 下 面 的 示 例 项 :<br />

[ Exp || Gen ]<br />

通 常 Gen 是 一 个 nondeterm 项 , 而 Exp 是 收 集 Gen 的 各 个 解 计 算 得 到 的 。 它 形 成 了 一 个 表 , 相 应 于<br />

Gen 的 第 一 个 解 的 是 Exp 的 表 中 的 第 一 个 元 素 , 等 等 。 这 个 表 , 是 述 求 表 项 的 结 果 。Exp 必 须 是 procedure<br />

( 或 erroneous)。Exp 和 Gen 都 是 cut 范 围 。<br />

述 求 表 一 般 读 为 : 如 Gen 所 述 , 那 样 的 Exp 的 表 。<br />

例<br />

[ X || X = getMember_nd(L), X div 2 = 0 ]<br />

它 可 以 读 为 :X 在 L 中 并 且 X 是 偶 数 的 X 的 表 。 所 以 , 这 个 表 达 式 是 L 中 的 偶 数 成 员 。<br />

例<br />

[ X + 1 || X = getMember_nd(L), X div 2 = 0 ]<br />

74


这 里 的 收 集 表 达 式 更 复 杂 了 , 要 说 这 个 项 也 更 麻 烦 了 : 这 样 的 (X+1) 的 表 …。 上 面 这 个 表 达 式 还 是<br />

找 L 中 的 偶 数 成 员 , 但 结 果 表 则 是 把 它 们 各 加 1。 因 而 , 它 与 下 面 这 个 表 达 式 是 完 全 等 价 的 :<br />

[ Y || X = getMember_nd(L), X div 2 = 0 , Y = X+1 ]<br />

这 个 项 就 比 较 易 于 说 :X 则 在 L 中 并 且 X 是 偶 数 ,Y 是 X+1, 那 样 的 Y 的 表 。<br />

访 问 对 象 成 员<br />

无 论 什 么 时 候 , 只 要 我 们 引 用 了 一 个 对 象 , 我 们 就 可 以 访 问 这 个 对 象 的 对 象 成 员 谓 词 。<br />

MemberAccess:<br />

Term : Identifier<br />

( 目 前 term( 项 ) 只 能 是 一 个 变 量 或 一 个 事 实 变 量 )。<br />

标 识 必 须 是 项 的 类 型 。<br />

在 实 现 内 部 , 调 用 对 象 成 员 谓 词 时 不 必 引 用 对 象 , 因 为 包 含 了 “This”, 参 看 范 围 。<br />

访 问 域 、 函 子 和 常 数<br />

对 域 、 函 子 和 常 数 , 可 以 将 它 们 视 为 类 成 员 来 访 问 , 即 便 是 在 接 口 中 声 明 的 也 一 样 。 这 也 意 味 着 , 如<br />

要 对 它 们 做 限 定 , 总 是 用 类 / 接 口 名 加 双 冒 号 。<br />

foreach<br />

ForeachTerm :<br />

foreach Term do Term end foreach<br />

看 下 面 的 foreach 示 例 项 :<br />

foreach Gen do Body end foreach<br />

通 常 Gen 是 一 个 nondeterm 项 。Body 对 每 一 个 Gen 的 解 求 值 。 当 Gen 失 败 则 foreach 项 成 功 , 不<br />

需 要 进 行 Body 计 算 。Body 必 须 是 procedure( 或 erroneous)。Gen 和 Body 都 是 cut 范 围 。<br />

这 个 示 例 的 项 , 类 似 于 下 面 的 fail 循 环 :<br />

Gen,<br />

Body,<br />

fail<br />

两 者 的 主 要 差 别 是 , 重 复 后 foreach 项 成 功 , 而 fail 循 环 是 失 败 。 因 此 ,foreach 项 可 以 后 跟 其 它 的<br />

项 , 还 可 以 嵌 套 。<br />

例<br />

foreach L = list::getMember_nd(LL) do<br />

write("


write(" >>\n")<br />

end foreach.<br />

这 里 的 LL 假 设 是 一 个 表 的 表 。 外 层 的 foreach 循 环 用 L 遍 访 这 个 表 , 所 以 L 是 一 个 表 。 内 层 的 foreach<br />

循 环 用 X 遍 访 L 中 的 元 素 。<br />

有 几 点 需 要 注 意 :<br />

• 在 关 键 字 do 和 end foreach 之 前 都 没 有 逗 号 ;<br />

• foreach 项 的 Body 是 一 个 cut 范 围 , 所 以 在 体 中 的 cut 不 会 影 响 重 复 ;<br />

• foreach 项 总 是 成 功 ( 或 引 起 异 常 ), 在 它 求 值 完 成 之 后 , 没 有 额 外 的 变 量 被 绑 定 。 因 此 ,foreach<br />

项 只 能 使 用 其 辅 助 功 能 。<br />

例<br />

clauses<br />

p(L) :-<br />

foreach X = list::getMember_nd(L) do<br />

stdio::write(X, "\n")<br />

end foreach,<br />

stdio::write("Finished\n").<br />

Foreach 可 以 用 来 替 代 传 统 的 fail 循 环 :<br />

clauses<br />

p(L) :-<br />

X = list::getMember_nd(L),<br />

stdio::write(X, "\n"),<br />

fail.<br />

p(_L) :-<br />

stdio::write("Finished\n").<br />

在 这 种 情 况 下 , 体 的 过 程 性 是 很 有 好 处 的 , 因 为 在 fail 循 环 中 就 怕 在 到 达 循 环 的 fail 之 前 的 偶 尔 失 败 。<br />

还 有 一 个 便 利 之 处 是 ,foreach 循 环 在 循 环 结 束 时 是 成 功 的 , 不 像 fail 循 环 是 失 败 , 所 以 可 以 在 同 一 个 子 句<br />

内 继 续 进 行 下 去 。<br />

foreach 还 可 以 嵌 套 :<br />

if-then-else<br />

clauses<br />

p(LL) :-<br />

foreach L = list::getMember_nd(LL) do<br />

foreach X = list::getMember_nd(L) do<br />

stdio::write(X, "\n")<br />

end foreach,<br />

stdio::write("Finished a list\n")<br />

end foreach,<br />

stdio::write("Finished all lists\n").<br />

按 不 同 条 件 执 行 一 组 语 句 。<br />

IfThenElseTerm:<br />

if Condition then Term Elseif-list-opt Else-opt end if<br />

76


Elseif:<br />

elseif Condition then Term<br />

Else:<br />

else Term<br />

下 面 两 个 项 是 等 价 的 :<br />

if Cond1 then T1 elseif Cond2 then T2 else T3 end if<br />

if Cond1 then T1 else<br />

if Cond2 then T2 else T3 end if<br />

end if<br />

来 看 一 下 示 例 的 if then else 项 :<br />

if Cond then T1 else T2 end if<br />

首 先 对 Cond 求 值 , 如 果 成 功 则 对 T1 求 值 , 否 则 对 T2 求 值 。Cond 不 允 许 有 回 溯 点 , 也 就 是 说 它 不 能<br />

是 多 态 的 (multi) 或 非 确 定 的 (nondeterm)。Cond 是 一 个 cut 范 围 ( 参 看 cut 范 围 )。<br />

例<br />

clauses<br />

w(X) :-<br />

if X div 2 = 0 and X > 3 then<br />

write("X is good")<br />

else<br />

write("X is bad"),<br />

nl<br />

end if,<br />

nl.<br />

这 个 例 子 中 , 要 注 意 几 点 :<br />

• 在 所 有 三 个 子 项 中 , 可 以 使 用 and 和 or 逻 辑 操 作 符 以 及 其 它 “ 组 合 ” 项 ;<br />

• 在 关 键 字 then、elseif、else 和 end if 之 前 没 有 逗 号 。<br />

为 有 良 好 的 可 读 性 , 推 荐 用 “or” 替 代 “;”。 同 样 , 当 表 示 一 个 逻 辑 条 件 时 ( 如 上 例 中 的 情 况 ),<br />

推 荐 用 “and” 替 代 “,”。<br />

省 去 else 部 分 是 “else succeed” 的 简 化 形 式 , 即 :<br />

if Cond then T1 end if<br />

是 下 面 的 简 写 形 式 :<br />

try-catch-finally<br />

if Cond then T1 else succeed end if<br />

try-catch-finally 语 句 提 供 了 一 种 处 理 给 定 程 序 代 码 块 中 可 能 出 现 异 常 的 方 法 。<br />

TryCatchTerm:<br />

try Term CatchFinally-list end try<br />

77


CatchFinally: one of<br />

catch Variable do Trap-Handler<br />

finally Finally-Handler<br />

Handler:<br />

Term<br />

try 结 构 有 一 个 项 及 一 个 捕 获 变 量 和 最 终 异 常 处 理 过 程 的 表 。 项 和 异 常 处 理 过 程 都 不 能 留 有 回 溯 点 ( 即<br />

不 能 是 multi 或 nondeterm)。<br />

带 有 多 个 异 常 处 理 过 程 的 try 结 构 等 效 于 单 个 异 常 处 理 过 程 的 try 结 构 的 嵌 套 , 下 面 的 项 :<br />

try<br />

Body<br />

catch Var1 do<br />

Handler1<br />

finally<br />

Handler2<br />

catch Var2 do<br />

Handler3<br />

end try<br />

与 下 面 的 嵌 套 项 是 等 价 的 :<br />

try<br />

try<br />

try<br />

Body<br />

catch Var1 do<br />

Handler1<br />

end try<br />

finally<br />

Handler2<br />

end try<br />

catch Var2 do<br />

Handler3<br />

end try<br />

try-catch<br />

看 下 面 这 个 try-catch 结 构 :<br />

try<br />

Body<br />

catch Var do<br />

Handler<br />

end try<br />

首 先 是 对 Body 求 值 。 如 果 Body 失 败 或 成 功 , 则 整 个 try 结 构 失 败 或 成 功 。 即 : 若 Body 并 未 因 一 个<br />

异 常 而 终 止 , 则 try 结 构 就 相 当 于 对 Body 求 值 。<br />

如 果 由 于 一 个 异 常 而 使 Body 终 止 了 , 这 个 异 常 就 被 捕 获 , 意 思 是 Var 首 先 被 绑 定 为 该 异 常 ( 在 PFC<br />

关 联 中 这 个 异 常 是 一 个 traceId), 接 着 对 Handler 求 值 。 这 种 情 况 下 该 结 构 就 是 带 有 绑 定 于 捕 获 异 常 的<br />

Var 对 Handler 的 求 值 。<br />

注 意 , 如 果 Body 因 异 常 而 终 止 了 , 它 本 身 并 不 会 绑 定 什 么 东 西 。<br />

78


例 处 理 一 个 文 件 不 能 读 的 情 况 :<br />

clauses<br />

read(Filename) = Contents :-<br />

try<br />

Contents = file::readFile(Filename)<br />

catch TraceId do<br />

stdio::writef("Could not read the file: %\n", Filename),<br />

exceptionDump::dumpToStdio(TraceId),<br />

Contents = ""<br />

end try.<br />

首 先 , 求 解 Contents = file::readFile(Filename), 如 果 它 并 未 因 为 read 异 常 而 终 止 , 就 会 返 回<br />

文 件 的 内 容 。<br />

如 果 出 现 了 异 常 , 就 会 带 着 绑 定 于 该 异 常 的 TraceId 对 异 常 处 理 过 程 求 解 。 此 时 , 会 将 一 个 消 息 写 到<br />

stdio 并 把 异 常 输 出 到 stdio, 最 后 把 Contents 设 为 空 串 作 为 结 果 返 回 。<br />

try-finally<br />

看 下 面 的 try-finally 结 构 :<br />

try<br />

Body<br />

finally<br />

Handler<br />

end try<br />

这 个 结 构 的 目 标 是 在 Body 之 后 对 Handler 求 值 , 而 不 管 Body 是 如 何 结 束 的 , 即 它 是 成 功 也 好 , 失<br />

败 也 好 , 因 异 常 而 终 止 也 好 ( 这 时 不 能 留 有 回 溯 点 ), 都 要 对 Handler 求 值 。<br />

求 值 过 程 是 这 样 的 :<br />

先 对 Body 求 值 , 若 成 功 再 执 行 Handler,try-finally 结 构 成 功 ; 若 失 败 也 执 行 Handler,try-finally<br />

结 构 失 败 ; 若 出 现 异 常 Exn, 还 是 执 行 Handler,try-finally 结 构 带 着 Exn 异 常 终 止 。<br />

例 确 保 在 使 用 过 后 odbcStatement 是 自 由 的 :<br />

clauses<br />

tryGetName(Connection, PersonId) = Name :-<br />

Stmt = odbcStatement::new(Connection),<br />

try<br />

Stmt:setParameterValue(1, core::integer(PersonId)),<br />

Stmt:execDirect("select name from person where id=?"),<br />

Stmt:fetch(),<br />

Name = Stmt:getParameterValue_string(1)<br />

finally<br />

Stmt:free()<br />

end try.<br />

即 使 fetch 失 败 或 是 其 它 什 么 原 因 出 现 异 常 了 ,Stmt 也 是 自 由 的 。<br />

转 换<br />

79


视 类 型 与 构 造 类 型<br />

接 口 定 义 对 象 的 类 型 , 支 持 某 个 接 口 的 所 有 对 象 都 具 有 这 个 接 口 定 义 的 类 型 。 以 后 , 我 们 把 对 象 类 型<br />

与 接 口 定 义 的 类 型 视 为 同 义 语 。<br />

接 口 定 义 了 类 型 , 这 些 类 型 就 可 以 在 谓 词 及 事 实 声 明 和 域 定 义 中 用 作 正 式 的 类 型 说 明 符 。<br />

对 象 具 有 了 任 意 它 所 支 持 接 口 的 对 象 类 型 , 它 也 就 可 以 当 作 任 意 这 些 类 型 的 对 象 来 使 用 , 也 就 是 说 ,<br />

对 象 类 型 被 转 换 为 任 意 支 持 的 对 象 类 型 。 下 面 要 说 明 , 这 种 转 换 多 数 情 况 下 是 自 动 进 行 的 。<br />

因 此 , 同 一 个 对 象 在 不 同 关 联 中 可 以 视 为 具 有 不 同 的 类 型 。 这 样 观 察 的 对 象 类 型 被 称 为 视 类 型 , 而 构<br />

造 对 象 的 类 的 类 型 被 称 作 构 造 类 型 或 定 义 类 型 。 构 造 类 型 也 是 一 个 视 类 型 。<br />

类 型 转 换<br />

如 上 所 述 , 对 象 可 以 当 成 任 意 它 所 支 持 的 接 口 的 类 型 来 使 用 。 这 一 节 要 描 述 各 种 支 持 类 型 的 转 换 是 怎<br />

么 进 行 的 。<br />

Conversion Upwards( 向 上 转 换 )<br />

如 果 某 个 项 静 态 地 归 类 于 某 个 类 型 T1, 而 T1 又 声 明 支 持 T2, 那 么 很 明 显 该 变 量 引 用 的 任 何 对 象 肯 定<br />

是 支 持 的 T2。 因 此 , 向 上 支 持 的 消 息 是 静 态 获 取 的 。 因 而 , 在 支 持 层 级 上 的 所 有 向 上 转 换 是 自 动 进 行 的 。<br />

例 假 设 有 接 口 bb, 它 支 持 接 口 aa 及 构 造 类 型 为 bb 的 类 bb_class。 来 看 下 面 的 代 码 :<br />

implement ...<br />

predicates<br />

ppp : (aa AA).<br />

clauses<br />

... :-<br />

BB = bb_class::new(), % (1)<br />

ppp(B). % (2)<br />

clauses<br />

ppp(AA) :- % (3)<br />

...<br />

BB = convert(bb,AA), % 可 以 转 换 , 因 为 AA 的 定 义 类 型 是 bb<br />

...<br />

在 (1) 那 行 我 们 创 建 了 一 个 bb_class 对 象 , 该 对 象 具 有 构 造 类 型 bb。 变 量 BB 是 对 这 个 新 对 象 的 引<br />

用 , 它 提 供 了 对 该 对 象 的 视 类 型 bb。 在 (2) 那 一 行 该 对 象 作 为 一 个 aa 对 象 传 递 给 了 ppp, 从 视 类 型 bb<br />

到 视 类 型 aa 的 转 换 是 隐 含 进 行 的 。 到 了 (3) 那 行 对 象 的 视 类 型 就 是 aa 了 , 尽 管 此 时 构 造 类 型 仍 是 bb。<br />

显 式 转 换<br />

显 式 转 换 是 通 过 调 用 转 换 谓 词 来 完 成 的 , 可 用 的 转 换 谓 词 有 若 干 个 。<br />

Checked Conversion<br />

谓 词 convert/2-> 和 tryConvert/2-> 用 于 安 全 地 完 成 从 一 个 类 型 到 另 一 个 类 型 的 转 换 。 这 两 个 谓 词 都<br />

给 不 出 真 实 的 声 明 , 伪 声 明 如 下 :<br />

predicates<br />

80


convert : ("type" Type, _ Value) -> Type ConvertedValue.<br />

tryConvert : ("type" Type, _ Value) -> Type ConvertedValue determ.<br />

convert/2-> 和 tryConvert/2-> 这 两 个 谓 词 都 用 “type” 作 第 一 个 参 数 , 以 任 意 类 型 的 一 个 值 做 第<br />

二 个 参 数 , 并 把 转 换 的 值 作 为 结 果 返 回 。<br />

如 果 不 能 进 行 转 换 ,convert/2-> 会 产 生 一 个 异 常 , 而 tryConvert/2-> 只 会 失 败 。<br />

注 意 , 如 果 源 类 型 是 目 标 类 型 的 子 类 型 , 使 用 convert/2-> 和 tryConvert/2-> 就 总 是 多 余 的 , 因 为 此<br />

时 会 隐 含 地 进 行 转 换 。<br />

报 告 。<br />

这 两 个 谓 词 可 以 用 在 以 下 场 合 :<br />

• 从 一 个 数 域 到 另 一 个 数 域 的 转 换 ;<br />

• 将 一 个 对 象 转 换 到 另 一 个 类 型 。<br />

编 译 器 如 果 能 确 定 一 个 转 换 肯 定 不 会 成 功 ( 比 如 要 转 换 的 数 域 间 根 本 没 有 交 集 ), 会 有 ( 但 并 非 总 有 )<br />

Conversion Downward( 向 下 转 换 )<br />

当 一 个 对 象 转 换 成 超 类 型 ( 如 支 持 的 接 口 ) 时 , 这 个 对 象 的 信 息 就 “ 不 记 得 ” 了 。 当 然 能 力 并 不 是 真<br />

的 丢 失 了 , 而 只 是 从 这 种 弱 化 能 力 接 口 的 关 联 中 来 看 对 象 时 看 不 到 了 。 在 很 多 情 况 下 需 要 恢 复 对 象 的 实 际<br />

能 力 , 因 此 需 要 对 它 们 进 行 向 下 及 向 上 的 转 换 。<br />

向 下 转 换 ( 一 般 ) 无 法 静 态 地 验 证 , 因 此 , 当 恢 复 “ 丢 失 ” 的 接 口 时 需 要 显 式 地 转 换 。<br />

例 尽 管 要 弄 个 在 支 持 层 级 中 向 上 转 换 类 型 的 有 意 义 的 说 明 例 子 很 简 单 , 但 说 明 有 意 义 的 向 下 转 换 的<br />

用 法 还 是 需 要 一 个 “ 真 实 ” 的 例 子 , 这 里 我 们 给 出 一 个 较 “ 真 实 ” 的 例 子 。<br />

假 设 我 们 要 实 现 一 组 相 似 类 型 的 对 象 , 比 如 一 组 支 持 某 个 视 类 型 的 对 象 , 我 们 需 要 这 样 的 集 合 用 于 若<br />

干 对 象 类 型 。 因 此 , 我 们 打 算 这 么 办 : 即 要 使 不 同 类 型 的 对 象 能 接 受 , 又 要 保 持 这 些 对 象 的 同 质 性 。<br />

方 法 是 标 准 化 的 : 我 们 使 集 合 的 实 际 实 现 基 于 对 象 的 类 型 , 这 个 类 型 是 所 有 对 象 都 支 持 的 。 然 后 用 一<br />

个 在 实 际 类 型 和 对 象 间 转 换 的 薄 层 来 构 造 集 合 的 特 殊 版 本 。 没 有 对 象 集 合 的 实 际 实 现 , 我 们 只 需 要 假 设 它<br />

形 式 上 存 在 于 下 面 的 类 和 接 口 中 :<br />

interface objectSet<br />

predicates<br />

insert : (object Elem).<br />

getSomeElem : () -> object determ.<br />

...<br />

end interface<br />

class objectSet_class : objectSet<br />

end class<br />

假 设 有 某 个 对 象 类 型 myObject 而 我 们 想 要 创 建 相 应 的 “set” 类 myObjectSet_class。 这 样 声 明<br />

myObjectSet_class:<br />

interface myObjectSet<br />

predicates<br />

insert : (myObject Elem).<br />

getSomeElem : () -> myObject determ.<br />

...<br />

end interface<br />

class myObjectSet_class : myObjectSet<br />

end class<br />

即 ,myObjectSet 有 objectSet 的 所 有 谓 词 , 但 每 当 object 出 现 时 就 替 换 成 myObject。<br />

81


myObjectSet_class 的 实 现 继 承 自 objectSet_class, 这 个 内 置 / 继 承 的 objectSet 将 取 得 该 集 合 的 成<br />

员 。 该 实 现 会 执 行 如 下 的 不 变 性 : 内 置 的 objectSet 将 只 包 含 myObject 类 型 的 对 象 ( 尽 管 从 “ 技 术 ” 上<br />

说 它 们 具 有 object 类 型 )。<br />

实 现 如 下 :<br />

implement myObjectSet_class<br />

inherit objectSet_class<br />

clauses<br />

insert(Elem) :-<br />

objectSet_class::insert(Elem). % (1)<br />

getSomeElem() = Some :-<br />

SomeObject = objectSet_class::getSomeElem(), % (2)<br />

Some = convert(myObject, SomeObject). % (3)<br />

...<br />

end implement<br />

在 (1) 行 ,Elem 自 动 从 类 型 myObject 转 换 为 object。 在 (2) 那 行 , 从 内 置 的 object 集 中 取 回<br />

一 个 对 象 。 理 论 上 说 这 个 对 象 具 有 object 类 型 , 但 从 不 变 性 上 看 这 个 对 象 也 支 持 myObject。 因 而 我 们<br />

可 以 安 全 地 恢 复 myObject 接 口 , 在 (3) 中 显 式 地 这 样 做 了 。<br />

Private 类 型 和 Public 类 型<br />

由 构 造 器 创 建 的 对 象 , 返 回 时 是 构 造 类 型 。 这 样 的 对 象 可 以 自 动 转 换 成 任 意 所 支 持 的 接 口 类 型 并 又 被<br />

显 式 地 返 回 。<br />

即 使 实 现 对 象 的 类 已 经 声 明 所 支 持 的 接 口 是 私 有 的 , 也 可 以 把 “ 公 用 ” 对 象 转 换 成 这 些 私 有 的 类 型 。<br />

无 论 怎 样 , 在 实 现 内 部 , 对 象 是 可 以 用 任 意 私 有 支 持 的 类 型 访 问 的 。 而 且 ,“This” 可 以 用 任 意 这 些<br />

私 有 支 持 的 类 型 递 交 到 实 现 之 外 。<br />

对 象 这 样 的 “ 私 有 ” 版 本 还 可 以 在 其 层 级 上 隐 含 地 向 上 转 换 并 显 式 地 向 下 转 换 回 来 。 事 实 上 , 这 样 的<br />

“ 私 有 ” 对 象 可 以 显 式 地 转 换 成 任 意 公 用 或 私 有 支 持 的 接 口 。<br />

所 以 , 一 个 对 象 会 有 两 面 , 公 用 的 一 面 和 私 有 的 一 面 , 而 私 有 的 一 面 包 括 了 公 用 类 型 。 对 象 不 能 从 一<br />

面 转 换 到 另 一 面 , 但 因 为 私 有 的 一 面 包 括 了 公 用 类 型 , 所 以 私 有 的 一 面 可 以 转 换 成 任 意 支 持 的 类 型 。<br />

Unchecked Conversion<br />

谓 词 uncheckedConvert/2 用 于 进 行 基 于 内 存 表 示 法 的 非 安 全 性 转 换 。 这 个 谓 词 并 不 对 内 存 做 任 何 修<br />

改 , 它 只 是 强 迫 编 译 器 对 所 指 的 存 储 片 断 解 释 为 另 外 的 类 型 。<br />

注 意 , 这 个 谓 词 是 很 不 安 全 的 , 使 用 时 要 小 心 。<br />

这 个 谓 词 主 要 用 于 与 其 它 语 言 的 接 口 , 来 解 释 其 它 语 言 对 内 存 映 象 的 使 用 方 法 。 它 只 能 使 用 在 大 小 完<br />

全 一 样 的 内 存 片 上 , 而 有 很 多 种 数 据 是 用 指 针 代 表 的 , 这 样 的 数 据 有 相 同 的 大 小 。<br />

例<br />

predicates<br />

uncheckedConvert : ("type" Type, _ Value) -> Type ConvertedValue.<br />

predicates<br />

interpretBufferAsString : (pointer BufferPointer) -> string Value.<br />

clauses<br />

interpretBufferAsString(BufferPointer) = uncheckedConvert(string,BufferPointer).<br />

82


这 个 谓 词 将 把 一 个 指 针 表 示 的 缓 冲 区 解 释 ( 转 换 ) 为 一 个 串 。 只 有 这 个 内 存 块 真 的 可 以 正 确 地 表 示 成<br />

一 个 串 , 它 才 是 有 意 义 的 。<br />

异 常 处 理<br />

本 节 描 述 <strong>Visual</strong> <strong>Prolog</strong> 中 异 常 处 理 的 低 级 结 构 。PFC 在 这 个 低 级 机 制 之 上 还 有 一 个 较 高 级 的 层 次 ( 参<br />

看 异 常 处 理 指 南 )。<br />

异 常 处 理 的 基 础 是 基 于 内 建 谓 词 errorExit/1 及 try-catch 语 言 结 构 的 。<br />

• errorExit/1 产 生 一 个 异 常<br />

• try-catch 为 某 个 计 算 设 置 异 常 处 理 程 序 。<br />

当 errorExit/1 调 用 时 , 当 前 激 活 的 异 常 处 理 程 序 被 调 用 , 这 个 异 常 处 理 程 序 是 按 原 来 的 关 联 执 行 的 ,<br />

这 个 关 联 是 原 来 设 置 的 而 不 是 发 生 异 常 那 里 的 关 联 。 调 用 时 的 参 数 传 递 给 了 异 常 处 理 程 序 , 这 个 参 数 必 须<br />

以 某 种 方 式 提 供 需 要 的 异 常 描 述 。<br />

与 其 它 运 行 时 例 程 一 道 , 在 这 个 系 统 之 上 就 可 以 建 立 起 高 级 的 异 常 处 理 机 制 了 。 不 过 运 行 时 系 统 访 问<br />

程 序 及 其 如 何 处 理 异 常 等 问 题 已 经 超 出 本 文 档 的 范 围 , 就 不 介 绍 了 。<br />

try-catch 的 第 一 个 参 数 是 与 新 异 常 处 理 程 序 一 道 执 行 的 项 ; 第 二 个 参 数 必 须 是 一 个 变 量 。 如 果 异 常 处<br />

理 程 序 被 激 活 , 这 个 变 量 将 绑 定 于 errorExit/1 被 调 用 时 的 值 。 第 三 个 参 数 是 异 常 处 理 程 序 , 如 果<br />

errorExit/1 被 调 用 则 会 激 活 这 个 异 常 处 理 程 序 。<br />

异 常 处 理 程 序 可 以 访 问 第 二 个 参 数 说 明 的 变 量 , 因 而 可 以 检 查 发 生 了 什 么 异 常 。<br />

例<br />

clauses<br />

p(X) :-<br />

try<br />

dangerous(X)<br />

catch Exception do<br />

handleDangerous(Exception)<br />

end try.<br />

如 果 在 执 行 dangerous 时 出 现 了 异 常 ,Exception 就 会 绑 定 为 异 常 值 ,try-catch 控 制 将 转 到 的 第 三<br />

个 参 数 , 在 这 里 就 是 把 Exception 传 递 给 handleDangerous。<br />

内 建 实 体<br />

<strong>Visual</strong> <strong>Prolog</strong> 有 一 个 内 含 的 类 , 提 供 了 所 有 内 建 的 常 数 、 域 及 谓 词 的 声 明 和 实 现 。 这 些 内 建 的 常 数 、<br />

域 及 谓 词 既 可 以 用 在 编 译 时 ( 如 在 #if ... 结 构 中 ) 也 可 以 用 在 实 现 中 ( 由 运 行 时 支 持 )。 每 个 编 译 单 元 隐<br />

含 地 包 括 了 这 个 内 含 类 的 声 明 。 可 以 在 内 建 项 的 名 称 前 使 用 “::” 以 与 其 它 同 名 的 实 体 相 区 分 。<br />

注 意 , 子 句 变 量 This 是 在 对 象 谓 词 子 句 中 自 动 定 义 的 。<br />

运 算 符<br />

运 算 符 描 述 备 注<br />

83


-( 一 元 ) 一 元 减 ( 负 号 )<br />

^ 幂 对 64 位 整 数 没 有 定 义<br />

*, / 乘 、 除 法<br />

div, mod 整 数 除 法 取 商 和 取 余 对 实 数 无 定 义<br />

quot, rem 整 数 除 法 取 商 和 取 余 对 实 数 无 定 义<br />

+, - 加 、 减 法<br />

• 上 面 的 运 算 符 是 按 优 先 级 由 高 到 低 排 列 的 ;<br />

• 所 有 的 乘 法 和 除 法 运 算 符 优 先 级 是 一 样 的 ;<br />

• 幂 运 算 符 是 右 结 合 的 ;<br />

• 所 有 其 它 运 算 符 都 是 左 结 合 的 。<br />

所 有 二 元 运 算 符 都 有 两 个 参 数 , 这 两 个 参 数 基 类 型 相 同 , 返 回 的 值 也 是 同 样 的 基 类 型 。 操 作 数 和 结 果<br />

的 类 型 可 以 应 用 普 通 子 类 型 规 则 进 行 转 换 。<br />

整 数 除 法<br />

当 计 算 结 果 是 正 数 时 ,div 与 quot 没 有 什 么 区 别 ; 但 当 结 果 是 负 值 时 ,div 的 取 整 使 用 最 接 近 的 最 小<br />

整 数 而 quot 是 使 用 最 接 近 的 最 大 整 数 。mod 是 对 应 于 div 的 余 数 , 而 rem 是 对 应 于 quot 的 余 数 。<br />

A B A div B A mod B A quot B A rem B<br />

15 7 2 1 2 1<br />

-15 7 -3 6 -2 -1<br />

15 -7 -3 -6 -2 1<br />

-15 -7 2 -1 2 -1<br />

常 数<br />

compilation date<br />

compilation time<br />

compiler buildDate<br />

compiler version<br />

maxFloatDigits<br />

null<br />

nullHandle<br />

invalidHandle<br />

platform bits<br />

platform name<br />

编 译 日 期<br />

编 译 时 间<br />

编 译 器 的 构 建 日 期<br />

编 译 器 版 本<br />

定 义 编 译 器 支 持 的 数 字 位 数 的 最 大 值<br />

缺 省 的 空 指 针<br />

值 为 零 的 句 柄 类 型 的 一 个 特 殊 常 数<br />

表 示 非 法 的 ( 值 为 -1) 句 柄 类 型 的 一 个 特 殊 常 数<br />

定 义 编 译 平 台 的 数 字 能 力<br />

定 义 目 标 平 台 名 称<br />

编 译 日 期<br />

compilation_date : string = "YYYY-MM-DD".<br />

84


这 里 的 YYYY 是 代 表 年 的 数 字 ,MM 是 代 表 月 份 的 数 字 ,DD 是 代 表 日 的 数 字 。<br />

编 译 时 间<br />

compilation_time : string = "HH-MM-SS".<br />

这 里 的 HH 代 表 小 时 ,MM 代 表 分 钟 ,SS 代 表 秒 。<br />

编 译 器 构 建 日 期<br />

compiler_buildDate : string = "YYYY-MM-DD HH-MM-SS".<br />

编 译 器 的 构 建 日 期 。<br />

编 译 器 版 本<br />

编 译 器 的 版 本 , 这 个 数 值 与 编 译 器 的 版 本 相 关 。<br />

maxFloatDigits<br />

compiler_version = 6003.<br />

定 义 编 译 器 支 持 的 数 字 位 数 的 最 大 值 。<br />

maxFloatDigits = 19.<br />

null<br />

null : pointer = uncheckedConvert(pointer, 0).<br />

值 为 零 的 指 针 类 型 的 一 个 特 殊 常 数 。<br />

nullHandle<br />

值 为 零 的 句 柄 类 型 的 一 个 特 殊 常 数 。<br />

invalidHandle<br />

nullHandle : handle = uncheckedConvert(handle, 0).<br />

其 值 (-1) 表 示 非 法 的 句 柄 类 型 的 一 个 特 殊 常 数 。<br />

platform_bits<br />

invalidHandle : handle = uncheckedConvert(handle, 0xFFFFFFFF).<br />

编 译 平 台 的 数 字 能 力 。<br />

platform_bits = 32.<br />

85


platform_name<br />

域<br />

目 标 编 译 平 台 名 称 。<br />

platform_name : string = "Windows 32bits".<br />

char<br />

string<br />

string8<br />

symbol<br />

binary<br />

binaryNonAtomic<br />

integer<br />

integer64<br />

unsigned<br />

unsigned64<br />

real<br />

real32<br />

pointer<br />

handle<br />

boolean<br />

factDB<br />

compareResult<br />

宽 ( 两 字 节 ) 字 符<br />

以 两 个 零 字 节 结 尾 的 宽 字 符 序 列<br />

以 一 个 零 字 节 结 尾 的 ASCII ( 单 字 节 ) 字 符 序 列<br />

以 两 个 零 字 节 结 尾 的 宽 字 符 序 列<br />

字 节 序 列<br />

字 节 序 列<br />

有 符 号 整 数<br />

有 符 号 整 数<br />

无 符 号 整 数<br />

无 符 号 整 数<br />

浮 点 数<br />

浮 点 数<br />

指 向 内 存 地 点 的 4 字 节 指 针<br />

指 向 内 存 地 点 的 4 字 节 指 针<br />

布 尔 值<br />

命 名 了 的 内 部 数 据 库 描 述 符<br />

比 较 结 果 的 值<br />

char<br />

宽 字 符 。<br />

char<br />

这 个 域 的 值 是 UNICODE 字 符 , 由 两 个 字 节 无 符 号 数 实 现 。<br />

对 这 个 域 的 值 的 操 作 , 只 能 有 赋 值 和 ( 字 典 意 义 上 的 ) 比 较 。 字 符 映 象 语 法 如 下 :<br />

Char_image :<br />

' Char_value '<br />

Char_value :<br />

Letter<br />

Digit<br />

Graphical_symbol<br />

\ Escape_seq<br />

Escape_seq:<br />

t<br />

n<br />

r<br />

86


\<br />

'<br />

"<br />

u HHHH<br />

上 面 的 HHHH 对 应 于 4 个 十 六 进 制 数 。 还 有 , 反 斜 杠 和 单 引 号 只 能 由 换 码 序 列 来 表 示 。<br />

compareResult<br />

这 是 一 个 内 建 的 域 , 用 来 定 义 一 个 比 较 结 果 。 内 建 谓 词 compare 的 结 果 就 是 这 个 域 的 。<br />

domains<br />

compareResult = less; equal; greater.<br />

string<br />

以 宽 零 结 尾 的 宽 字 符 序 列 。<br />

string<br />

串 是 UNICODE 字 符 序 列 。 它 是 由 一 个 指 向 以 宽 零 结 尾 的 宽 字 符 序 列 的 指 针 实 现 的 。 这 个 域 的 值 只 能<br />

进 行 赋 值 与 ( 字 典 意 义 上 的 ) 比 较 操 作 。 在 源 代 码 中 , 可 以 用 以 双 引 号 括 起 的 字 符 序 列 表 示 一 个 字 符 串 。<br />

StringLiteral:<br />

StringLiteralPart-list<br />

StringLiteralPart :<br />

@" AnyCharacter-list-opt "<br />

" CharacterValue-list-opt "<br />

字 符 串 由 一 个 或 多 个 连 接 起 来 的 StringLiteralPart 组 成 。 带 有 @ 的 StringLiteralPart 不 使 用 换 码<br />

序 列 , 而 不 带 @ 的 StringLiteralPart 则 使 用 以 下 的 换 码 序 列 :<br />

• \\ 表 示 \<br />

• \t 表 示 制 表 符<br />

• \n 表 示 换 行 符<br />

• \r 表 示 回 车<br />

• \' 表 示 单 引 号<br />

• \" 表 示 双 引 号<br />

• \u 后 跟 四 个 十 六 进 制 数 表 示 相 应 的 Unicode 字 符 。<br />

串 中 的 双 引 号 只 能 由 换 码 序 列 来 表 示 , 而 单 引 号 可 以 用 换 码 序 列 或 图 形 符 号 两 种 方 法 来 表 示 。<br />

string8<br />

内 建 的 string8 域 这 个 项 是 一 个 ASCII 字 符 序 列 。 它 是 由 一 个 指 向 零 结 尾 的 ASCII 字 符 序 列 的 指 针 实 现<br />

的 。 这 个 域 的 值 只 能 进 行 赋 值 与 ( 字 典 意 义 上 的 ) 相 等 比 较 操 作 。 目 前 , 对 这 个 域 来 说 , 不 能 使 用 文 字 。<br />

(Currently no literals are allowed for this domain.)<br />

symbol<br />

87


以 宽 零 结 尾 的 宽 字 符 序 列 。<br />

symbol<br />

与 串 一 样 ,symbol 也 是 一 个 UNICODE 字 符 序 列 。 它 是 由 一 个 指 向 包 含 串 的 symbol 表 的 入 口 指 针 实<br />

现 的 。 对 它 可 以 做 的 操 作 与 串 一 样 。<br />

symbol 的 映 象 是 由 串 文 字 ( 用 双 引 号 包 围 的 串 ) 来 表 示 的 。 它 与 串 在 很 大 程 度 上 是 可 以 互 换 的 , 但<br />

它 们 的 存 储 方 式 不 相 同 。symbol 被 放 在 一 个 关 注 表 中 , 它 们 的 地 址 ( 而 不 是 symbol 本 身 ) 存 储 起 来 用 于<br />

代 表 对 象 。 这 意 味 着 symbol 可 以 快 速 地 匹 配 , 而 且 如 果 它 重 复 地 出 现 在 程 序 中 时 占 用 存 储 空 间 很 小 。 串<br />

则 不 是 放 在 关 注 表 中 的 , 进 行 匹 配 时 <strong>Visual</strong> <strong>Prolog</strong> 要 逐 个 字 符 地 进 行 检 查 。<br />

binary<br />

N 个 字 节 的 序 列 。<br />

binary<br />

这 个 域 的 值 用 于 保 存 二 进 制 数 。 它 是 由 指 向 这 个 字 节 序 列 的 一 个 指 针 实 现 的 。 这 个 项 的 长 度 放 在 字 节<br />

序 列 的 前 面 , 占 四 个 字 节 。 这 四 个 字 节 的 值 为 :<br />

TotalNumberOfBytesOccupiedByBinary = ByteLen + 4<br />

ByteLen 是 二 进 制 项 本 身 的 长 度 。<br />

对 这 个 域 的 值 只 能 进 行 赋 值 和 比 较 操 作 。 比 较 操 作 的 方 法 如 下 :<br />

• 如 果 两 个 项 长 度 不 一 , 长 度 大 的 那 个 项 大 ;<br />

• 如 果 两 个 项 长 度 一 样 , 就 按 无 符 号 数 逐 字 节 地 比 较 , 比 较 到 不 一 样 的 字 节 时 就 停 止 , 那 个 字 节 的<br />

比 较 结 果 就 是 这 两 个 项 的 比 较 结 果 。 如 果 字 节 都 一 样 , 那 就 是 相 等 。<br />

二 进 制 映 象 的 内 容 语 法 由 Binary 规 则 确 定 :<br />

Binary :<br />

$ [ Byte_value-comma-sep-list-opt ]<br />

Byte_value :<br />

Expression<br />

各 个 表 达 式 的 值 应 是 在 编 译 时 可 计 算 得 到 的 , 且 值 的 范 围 在 0 到 255 之 间 。<br />

binaryNonAtomic<br />

N 个 字 节 的 序 列 。<br />

binaryNonAtomic<br />

同 binary, 但 包 含 指 针 。<br />

integer<br />

有 符 号 整 数 。<br />

integer<br />

88


这 个 域 的 值 占 四 个 字 节 。 可 以 进 行 算 术 运 算 (+, -, /, *, ^)、 比 较 、 赋 值 、div、mod、quot 及 rem<br />

操 作 。 值 的 范 围 是 -2147483648 到 2147483647。<br />

它 的 文 字 表 示 方 法 由 Integer 规 则 确 定 :<br />

Integer :<br />

Add_operation-opt 0o Oct_number<br />

Add_operation-opt Dec_number<br />

Add_operation-opt 0x Hex_number<br />

Add_operation :<br />

+<br />

-<br />

Oct_number :<br />

Oct_digit-list<br />

Oct_digit : one of<br />

0 1 2 3 4 5 6 7<br />

Dec_number :<br />

Dec_digit-list<br />

Dec_digit : one of<br />

Oct_digit 8 9<br />

Hex_number :<br />

Hex_digit-list<br />

Hex_digit : one of<br />

Dec_digit a b c d e f A B C D E F<br />

integer64<br />

有 符 号 整 数 。<br />

integer64<br />

这 个 域 的 值 占 八 个 字 节 。 值 的 范 围 是 -2^63 = -9,223,372,036,854,775,808 到 2^63-1 =<br />

9,223,372,036,854,775,807。 其 文 字 表 示 方 法 及 可 以 进 行 的 操 作 与 integer 相 同 。<br />

unsigned<br />

无 符 号 整 数 。<br />

unsigned<br />

这 个 域 的 值 占 四 个 字 节 。 可 以 进 行 算 术 运 算 (+, -, /, *, ^)、 比 较 、 赋 值 、div、mod、quot 及 rem<br />

操 作 。 值 的 范 围 是 0 到 4294967295。 它 的 表 示 方 法 同 integer, 只 是 不 能 使 用 负 号 。<br />

unsigned64<br />

无 符 号 整 数 。<br />

unsigned64<br />

这 个 域 的 值 占 八 个 字 节 。 值 的 范 围 是 0 到 2^64-1 = 18,446,744,073,709,551,615。 它 的 表 示<br />

方 法 同 integer64, 只 是 不 能 使 用 负 号 。 可 以 进 行 的 操 作 同 unsigned。<br />

89


eal<br />

浮 点 数 ( 实 数 )。<br />

real<br />

这 个 域 的 值 占 八 个 字 节 。 这 个 域 只 是 为 了 方 便 用 户 引 入 的 , 可 以 进 行 所 有 的 算 术 、 赋 值 及 比 较 操 作 。<br />

它 的 值 范 围 是 -1.7e+308 到 1.7e+308。 需 要 的 时 候 , 整 数 域 的 值 会 自 动 地 转 换 成 实 数 域 的 值 。 它 的 文<br />

字 表 示 方 法 由 Real 规 则 确 定 :<br />

Real :<br />

Add_operation-opt Fraction Exponent-opt<br />

Fraction :<br />

Dec_number Fractional_part-opt<br />

Fractional_part :<br />

. Dec_number<br />

Exponent :<br />

Exp Add_operation-opt Dec_number<br />

Exp :<br />

e<br />

E<br />

Add_operation :<br />

+<br />

-<br />

Dec_number :<br />

Dec_digit-list<br />

Dec_digit : one of<br />

0 1 2 3 4 5 6 7 8 9<br />

real32<br />

浮 点 数 。<br />

real32<br />

这 个 域 的 值 占 四 个 字 节 。 它 只 是 为 了 用 户 的 方 便 而 引 入 的 , 可 以 进 行 所 有 的 算 术 、 赋 值 及 比 较 操 作 。<br />

它 的 值 范 围 是 -3.4e+38 到 3.4e+38。 表 示 方 法 同 real。<br />

pointer<br />

内 存 地 址 的 指 针 。<br />

pointer<br />

指 针 直 接 对 应 于 内 存 地 址 , 只 能 进 行 相 等 操 作 。 这 个 类 型 中 有 一 个 内 建 的 常 数 null。<br />

handle<br />

handle( 句 柄 ) 用 于 Windows API 类 函 数 调 用 , 它 占 四 个 字 节 , 不 能 对 它 进 行 什 么 操 作 , 也 不 能 从<br />

别 的 域 转 换 来 或 转 换 到 别 的 域 ( 除 非 是 uncheckedConvert)。 这 个 类 型 中 有 内 建 的 常 数 nullHandle 及<br />

90


invalidHandle。<br />

boolean<br />

布 尔 值 。<br />

boolean<br />

这 个 域 只 是 为 了 用 户 方 便 而 引 入 的 , 它 可 以 看 成 如 下 定 义 的 复 合 域 :<br />

domains<br />

boolean = false(); true().<br />

factDB<br />

命 名 了 的 内 部 数 据 库 描 述 符 。<br />

factDB<br />

这 个 域 有 如 下 隐 含 的 元 声 明 :<br />

domains<br />

factDB = struct @factdb( named_internal_database_domain, object ).<br />

事 实 段 中 所 有 用 户 定 义 的 名 称 都 是 这 个 域 的 常 数 。 需 要 时 , 编 译 器 自 动 地 从 这 些 常 数 中 构 建 相 应 的 复<br />

合 项 。 在 运 行 时 , 这 个 结 构 的 第 一 项 含 有 相 应 域 描 述 符 的 地 址 而 第 二 项 或 是 零 ( 对 类 事 实 段 ) 或 是 一 个 对<br />

象 的 指 针 ( 即 This, 对 对 象 事 实 段 )。<br />

谓 词<br />

assert/1<br />

asserta/1<br />

assertz/1<br />

bound/1 determ<br />

class_Name/0-><br />

compare/2-><br />

convert/2-><br />

digitsOf/1-><br />

errorExit/1 erroneous<br />

fail/0 failure<br />

free/1 determ<br />

hasDomain/2<br />

isErroneous/1 determ<br />

lowerBound/1-><br />

maxDigits/1-><br />

not/1 determ<br />

在 相 应 内 部 数 据 库 的 末 尾 插 入 指 定 的 事 实<br />

在 相 应 内 部 数 据 库 的 最 前 面 插 入 指 定 的 事 实<br />

在 相 应 内 部 数 据 库 的 末 尾 插 入 指 定 的 事 实<br />

测 试 指 定 变 量 是 否 已 经 绑 定<br />

这 个 编 译 时 的 谓 词 返 回 代 表 当 前 接 口 或 类 的 名 称 的 串<br />

返 回 变 量 比 较 的 结 果<br />

有 核 查 的 项 转 换<br />

返 回 指 定 实 数 域 的 精 度<br />

以 指 定 的 返 回 代 码 ErrorNumber 完 成 一 次 运 行 时 错 误 , 并 设 置 内 部 错 误 消 息<br />

引 发 回 溯<br />

检 查 一 个 变 量 是 否 是 自 由 的<br />

检 查 一 个 变 量 是 否 是 指 定 域 的<br />

检 查 事 实 变 量 是 否 为 erroneous 的<br />

返 回 指 定 数 字 域 的 下 界 值<br />

获 取 相 应 实 数 域 的 精 度 值<br />

子 目 标 结 果 (success/fail) 取 非<br />

91


predicate_fullname/0-><br />

predicate name/0-><br />

retract/1 nondeterm<br />

retractall/1<br />

retractFactDb/1<br />

sizeBitsOf/1-><br />

sizeOf/1-><br />

sizeOfDomain/1-><br />

sourcefile lineno/0-><br />

sourcefile_name/0-><br />

sourcefile_timestamp/0-><br />

succeed/0<br />

toBinary/1-><br />

toBoolean/1-><br />

toString/1-><br />

toTerm/1-><br />

tryToTerm/1-><br />

tryConvert/2-> determ<br />

uncheckedConvert/2-><br />

upperBound/1-><br />

返 回 调 用 这 个 谓 词 的 谓 词 全 称<br />

返 回 调 用 这 个 谓 词 的 谓 词 名<br />

由 相 应 内 部 数 据 库 中 删 除 相 应 的 事 实<br />

由 相 应 内 部 数 据 库 中 删 除 所 有 相 应 的 事 实<br />

由 相 应 内 部 数 据 库 中 删 除 所 有 事 实<br />

获 取 指 定 域 的 实 体 所 占 内 存 的 比 特 数<br />

获 取 指 定 项 所 占 内 存 的 字 节 数<br />

获 取 指 定 域 的 实 体 所 占 内 存 的 字 节 数<br />

返 回 编 译 器 处 理 的 源 文 件 的 当 前 行 号<br />

返 回 编 译 器 处 理 的 源 文 件 名<br />

返 回 表 示 编 译 器 处 理 的 源 文 件 时 间 的 串<br />

该 谓 词 总 是 成 功<br />

将 指 定 的 项 转 换 成 二 进 制 表 示<br />

这 个 元 谓 词 的 目 标 是 要 把 ( 对 一 个 谓 词 或 事 实 的 ) 确 定 性 的 调 用 转 换 成<br />

procedure 的 , 返 回 boolean 域 的 值 。<br />

将 指 定 的 项 转 换 成 串 表 示<br />

将 指 定 的 串 / 二 进 制 表 示 项 转 换 为 相 应 域 的 返 回 值 变 量 的 <strong>Prolog</strong> 的 项<br />

将 指 定 的 串 / 二 进 制 表 示 项 转 换 为 相 应 域 的 返 回 值 变 量 的 <strong>Prolog</strong> 的 项<br />

核 查 输 入 项 能 否 被 严 格 地 转 换 为 指 定 域 的 项 , 返 回 转 换 后 的 项<br />

没 有 核 查 的 域 转 换<br />

返 回 指 定 数 字 域 的 上 界 值<br />

下 面 的 谓 词 已 经 不 再 使 用 :<br />

finally/2<br />

findall/3<br />

trap/3 determ<br />

用 try ... finally ... end try 替 代<br />

用 述 求 表 [ ... || ... ] 替 代<br />

用 try ... catch V do ... end try 替 代<br />

assert/1<br />

assert : ( FactTerm).<br />

assert(Fact) 把 指 定 的 事 实 Fact 插 入 到 相 应 的 内 部 事 实 数 据 库 的 最 后 面 。Fact 必 须 是 属 于 内 部 事 实<br />

数 据 库 域 的 一 个 项 。(assert/1 applied to a single fact changes the existing instance of a fact to<br />

the specified one.), 它 与 assertz/1 效 果 是 一 样 的 。 还 可 以 参 看 asserta/1。<br />

注 意 ,retract/1 和 assert/1 两 者 的 如 下 组 合 会 导 致 死 循 环 :<br />

loop() :-<br />

retract(fct(X)),<br />

... % 由 X 建 立 Y<br />

assert(fct(Y)),<br />

fail.<br />

问 题 在 于 , 在 第 一 行 的 retract 将 最 后 删 除 插 入 到 最 后 一 行 的 事 实 , 因 为 在 事 实 链 中 它 是 最 后 插 入 的 。<br />

异 常<br />

插 入 一 个 声 明 为 determ 的 事 实 , 而 那 个 事 实 实 例 已 经 存 在 了 。<br />

92


asserta/1<br />

asserta : ( FactTerm).<br />

插 入 一 个 事 实 到 相 应 内 部 数 据 库 的 最 前 面 。<br />

描 述<br />

asserta(Fact) 把 指 定 的 事 实 Fact 插 入 到 相 应 的 内 部 事 实 数 据 库 的 最 前 面 。Fact 必 须 是 属 于 内 部 事<br />

实 数 据 库 域 的 一 个 项 。(asserta/1 applied to a single fact changes the existing instance of a fact<br />

to the specified one.)。 可 以 参 看 assert/1 和 assertz/1。<br />

异 常<br />

插 入 一 个 声 明 为 determ 的 事 实 , 而 那 个 事 实 实 例 已 经 存 在 了 。<br />

assertz/1<br />

assertz : ( FactTerm).<br />

assertz 与 assert/1 谓 词 完 全 一 样 。<br />

bound/1<br />

bound : ( Variable) determ.<br />

测 试 指 定 的 变 量 是 否 已 经 绑 定 了 一 个 值 。<br />

描 述<br />

如 果 Variable 已 经 被 绑 定 ,bound(Variable) 成 功 , 反 之 则 失 败 。 谓 词 用 于 控 制 流 样 式 以 及 检 查 引<br />

用 的 变 量 绑 定 情 况 , 如 果 Variable 的 任 意 一 部 分 已 经 实 例 化 了 , 都 将 视 为 已 经 绑 定<br />

参 看 free/1。<br />

class_Name/0-><br />

class_Name : () -> string ClassName.<br />

这 个 编 译 时 谓 词 返 回 一 个 串 , 这 个 串 是 当 前 接 口 或 类 的 名 称 。<br />

compare/2-><br />

compare : (A Left, A Right) -> compareResult CompareResult.<br />

对 相 同 域 的 两 个 项 进 行 比 较 。 这 里 的 CompareResult 是 compareResult 域 的 结 果 变 量 。<br />

例<br />

CompareResult = compare("123","abc")<br />

convert/2-><br />

convert : ( Type, Term) -> Converted.<br />

有 核 查 的 项 转 换 。<br />

93


描 述<br />

这 个 函 数 的 调 用 模 板 是 :<br />

ReturnTerm = convert(returnDomain, InputTerm)<br />

参 数 :<br />

returnDomain: 指 定 要 将 InputTerm 转 换 到 该 域 , 它 必 须 是 内 建 的 <strong>Visual</strong> <strong>Prolog</strong> 域 或 接 口 域 的 名 称 ,<br />

或 是 用 户 定 义 的 与 内 建 的 <strong>Visual</strong> <strong>Prolog</strong> 域 、 数 字 域 、 二 进 制 域 、 指 针 域 同 义 的 名 称 。 这 个 名 称 在 编 译 时 必 须 是<br />

确 定 的 , 也 就 是 说 , 不 能 用 变 量 来 表 示 。<br />

InputTerm: 指 定 要 转 换 的 值 , 它 可 以 是 任 意 <strong>Prolog</strong> 项 或 表 达 式 。 如 果 是 表 达 式 , 在 转 换 时 应 有 明<br />

确 的 计 算 结 果 。<br />

ReturnTerm: 返 回 参 数 ReturnTerm 是 returnDomain 类 型 的 。<br />

备 注<br />

谓 词 convert 把 InputTerm 彻 底 转 换 到 指 定 的 returnDomain 域 , 返 回 新 的 项 ReturnTerm。 如 果<br />

它 不 能 完 成 要 求 的 转 换 , 就 会 产 生 一 个 错 误 。 还 有 一 个 功 能 上 相 似 的 谓 词 tryConvert/2->, 但 这 个 谓 词 如<br />

果 完 成 不 了 指 定 的 转 换 就 只 是 失 败 , 不 会 产 生 任 何 运 行 时 错 误 。<br />

允 许 的 转 换 :<br />

• 数 字 域 间 ;<br />

• 接 口 类 型 间 ;<br />

• string 和 symbol 间 ;<br />

• 从 binary 转 换 到 pointer;<br />

• 上 述 各 项 的 同 义 域 间 ;<br />

• reference 域 间 及 相 应 的 non-reference 域 间 。<br />

与 谓 词 uncheckedConvert/2-> 对 比 一 下 ,uncheckedConvert/2-> 可 以 对 两 个 项 进 行 非 核 查 的 转<br />

换 , 对 域 的 类 型 没 有 规 定 , 只 要 这 两 个 域 的 比 特 位 长 短 一 样 就 行 。<br />

当 编 译 时 对 源 及 目 标 域 静 态 已 知 的 情 况 下 ,convert/2->( 或 tryConvert/2->) 谓 词 进 行 一 个 有 核<br />

查 的 显 式 的 转 换 。 这 种 显 式 转 换 的 结 果 是 下 列 情 况 之 一 :<br />

• ok- 成 功 转 换 到 目 标 域 ;<br />

• run-time-check- 转 换 到 目 标 域 , 但 带 有 一 个 运 行 时 兼 容 性 的 核 查 ;<br />

• error- 不 可 能 转 换 , 产 生 错 误 。<br />

带 核 查 的 显 式 转 换 规 则 :<br />

• 同 义 域 使 用 相 应 的 域 本 身 转 换 的 规 则 ;<br />

• 数 字 域 只 能 转 换 为 数 字 域 ;<br />

• 匿 名 整 数 域 [const .. const] 由 整 数 常 数 代 表 ;<br />

• 匿 名 实 数 域 digits dig [const.. const] 由 实 数 常 数 代 表 , 这 里 的 是 尾 数 中 去 掉 无 意 义 的 零 后<br />

的 数 字 位 数 ;<br />

• symbol 域 和 string 域 的 值 可 以 相 互 转 换 ;<br />

• binary 域 的 值 可 以 转 换 为 pointer 域 ;<br />

• 对 接 口 隐 含 引 入 的 域 只 能 按 下 面 要 介 绍 的 规 则 转 换 到 该 接 口 域 ;<br />

• 其 它 的 域 间 不 能 转 换 。<br />

94


数 字 域 间 的 转 换 :<br />

这 种 转 换 首 先 要 考 虑 数 值 的 范 围 , 如 果 源 的 范 围 与 目 标 的 不 重 合 , 就 会 产 生 错 误 ; 而 如 果 只 是 部 分 重<br />

合 , 就 会 有 运 行 时 的 核 查 。 还 有 , 如 果 一 个 是 整 数 域 一 个 是 实 数 域 , 就 要 在 比 较 前 把 整 数 范 围 转 换 成 实 数<br />

范 围 。<br />

如 果 输 入 项 是 实 数 而 输 出 项 是 整 数 ,convert/2-> 及 tryConvert/2-> 谓 词 就 会 把 输 入 值 截 断 成 最 接<br />

近 零 的 近 似 整 数 值 。<br />

接 口 类 型 的 转 换<br />

谓 词 convert/2-> 允 许 把 任 意 对 象 转 换 成 任 意 接 口 类 型 的 , 而 这 种 转 换 的 实 际 正 确 性 在 运 行 时 作 核<br />

查 。 当 创 建 一 个 对 象 时 , 保 存 了 它 的 类 型 , 因 而 这 个 对 象 作 为 参 数 传 递 时 它 是 知 道 自 己 原 来 的 类 型 的 , 这<br />

个 原 来 的 类 型 可 以 用 来 核 查 允 许 的 转 换 。 例 如 :<br />

interface x<br />

supports a, b<br />

end interface x<br />

如 果 对 象 是 由 实 现 接 口 x 的 类 创 建 的 , 而 后 对 象 又 以 类 型 a 的 参 数 传 递 给 某 个 谓 词 , 那 么 就 允 许 转 换 这<br />

个 对 象 到 类 型 b。<br />

异 常<br />

• 核 查 范 围 出 错 ;<br />

• 不 支 持 的 接 口 类 型 。<br />

digitsOf/1-><br />

digitsOf : ( Domain) -> unsigned.<br />

返 回 实 数 域 表 示 的 精 度 。<br />

描 述<br />

这 个 函 数 的 调 用 模 板 是 :<br />

Precision = digitsof(domainName)<br />

这 个 编 译 时 谓 词 的 输 入 参 数 domainName 是 一 个 实 数 域 , 编 译 时 domainName 应 该 是 明 确 的 ( 也 就<br />

是 说 它 不 能 来 自 变 量 )。 谓 词 返 回 的 Precision 数 值 由 该 域 声 明 中 的 digits 属 性 确 定 。<br />

编 译 器 保 证 domainName 域 的 值 至 少 具 有 Precision 个 有 效 十 进 制 位 数 。<br />

errorExit/1<br />

errorExit : (unsigned ErrorNumber) erroneous.<br />

以 指 定 的 返 回 代 码 ErrorNumber 完 成 一 次 运 行 时 错 误 , 错 误 代 码 可 以 用 在 try-catch-finally 中 。<br />

fail/0<br />

fail : () failure.<br />

95


引 起 回 溯 。<br />

描 述<br />

fail 谓 词 强 迫 一 个 谓 词 失 败 , 因 而 总 是 引 起 回 溯 。 一 个 以 fail 结 尾 的 子 句 , 无 法 为 子 句 绑 定 输 出 参 数 。<br />

free/1<br />

free : ( Variable) determ.<br />

检 查 一 个 变 量 是 否 为 自 由 的 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

free(Variable)<br />

如 果 指 定 的 变 量 Variable 是 自 由 的 ,free 这 个 谓 词 就 成 功 , 而 如 果 Variable 是 绑 定 的 , 则 失 败 。 如<br />

果 Variable 的 任 意 部 分 实 例 化 了 ,free 谓 词 将 认 为 它 是 绑 定 的 。<br />

参 看 bound/1。<br />

hasDomain/2<br />

hasDomain : ( Type, Variable) procedure.<br />

检 查 变 量 VariableName 的 域 名 是 否 为 domainName。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

hasDomain(domainName, VariableName)<br />

谓 词 声 明 VariableName 属 于 参 数 domainName 说 明 的 域 。domainName 参 数 应 当 是 一 个 标 准 的 或<br />

用 户 定 义 的 域 的 名 称 , 这 个 域 名 在 编 译 时 应 该 是 确 定 的 ( 也 就 是 说 不 能 来 自 一 个 变 量 )。<br />

lowerBound/1-><br />

lowerBound : ( NumericDomain) -> LowerBound.<br />

返 回 指 定 的 NumericDomain 域 的 下 限 值 LowerBound。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

LowerBoundValue = lowerBound(domainName)<br />

这 是 一 个 编 译 时 谓 词 , 它 返 回 指 定 的 数 字 域 domainName 的 下 限 值 LowerBoundValue, 这 个 值 也<br />

是 domainName 域 的 。 域 名 domainName 在 编 译 时 应 该 是 确 定 的 ( 也 就 是 说 不 能 来 自 一 个 变 量 )。<br />

参 看 upperBound/1->。<br />

96


异 常<br />

如 果 指 定 的 domainName 域 不 是 数 字 域 , 编 译 时 会 出 错 。<br />

isErroneous/1<br />

isErroneous : ( FactVariable) determ.<br />

如 果 指 定 的 事 实 变 量 是 erroneous 的 , 这 个 谓 词 就 成 功 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

isErroneous(factVariableName)<br />

如 果 指 定 的 事 实 变 量 factVariableName 具 有 erroneous 的 值 , 这 个 谓 词 就 会 成 功 , 否 则 就 失 败 。<br />

maxDigits/1-><br />

maxDigits : ( RealDomain) -> unsigned MaxDigits<br />

获 取 相 应 于 domainName 指 定 的 实 数 域 的 精 度 值 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

MaxDigitsNumber = maxdigits(domainName)<br />

返 回 对 应 domainName 参 数 的 最 大 位 数 MaxDigitsNumber。domainName 应 该 是 real 域 名 。<br />

not/1<br />

not : ( SubGoal) determ.<br />

如 果 SubGoal 失 败 则 该 谓 词 成 功 , 反 之 则 失 败<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

not(SubGoal)<br />

求 值 时 , 如 果 SubGoal 失 败 , 则 not 成 功 。 注 意 ,SubGoal 无 法 绑 定 任 何 变 量 , 因 为 只 有 当 SubGoal<br />

失 败 not(SubGoal) 才 能 成 功 ( 而 失 败 的 目 标 无 法 绑 定 任 何 东 西 )。<br />

predicate_fullname/0-><br />

predicate_fullname : () -> string PredicateFullName.<br />

这 个 谓 词 返 回 调 用 它 的 谓 词 全 称 PredicateFullName, 全 称 中 带 有 范 围 名 称 的 限 定 。<br />

97


描 述<br />

谓 词 predicate_fullname 只 能 用 在 一 个 谓 词 定 义 的 子 句 体 中 , 如 果 在 其 它 的 地 方 使 用 这 个 谓 词 , 会<br />

在 编 译 时 出 错 。 参 看 predicate_name。<br />

predicate_name/0-><br />

predicate_name : () -> string PredicateName.<br />

这 个 谓 词 返 回 调 用 它 的 谓 词 名 称 PredicateName。<br />

描 述<br />

谓 词 predicate_fullname 只 能 用 在 一 个 谓 词 定 义 的 子 句 体 中 , 如 果 在 其 它 的 地 方 使 用 这 个 谓 词 , 会<br />

在 编 译 时 出 错 。 参 看 predicate_fullname。<br />

retract/1<br />

retract : ( FactTerm) nondeterm anyfow.<br />

从 事 实 数 据 库 中 删 除 了 第 一 个 匹 配 的 事 实 时 成 功 , 如 果 没 有 匹 配 的 事 实 则 失 败 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

retract(FactTemplate)<br />

这 里 的 FactTemplate 应 该 是 一 个 事 实 项 。retract/1 谓 词 从 相 应 的 事 实 数 据 库 中 删 除 第 一 个 匹 配 的<br />

FactTemplate; 通 过 回 溯 还 可 以 删 除 剩 余 的 匹 配 事 实 。<br />

注 意 ,FactTemplate 可 以 是 实 例 的 任 意 层 级 , 它 是 与 事 实 数 据 库 中 的 事 实 匹 配 , 这 意 味 着 所 有 自 由<br />

变 量 在 retract/1 调 用 时 都 会 被 绑 定 。<br />

FactTemplate 可 以 有 任 意 个 匿 名 变 量 , 也 就 是 变 量 名 可 以 仅 是 一 个 下 划 线 或 以 一 个 下 划 线 开 头 的 变<br />

量 名 , 只 要 是 这 个 变 量 在 子 句 中 只 出 现 一 次 。 例 如 :<br />

retract(person("Hans", _Age)),<br />

它 会 删 除 第 一 个 匹 配 的 事 实 , 这 个 事 实 的 第 一 个 参 数 是 “Hans” 而 第 二 个 参 数 可 以 是 任 意 的 。<br />

当 删 除 的 事 实 声 明 是 determ 的 时 , 对 retract/1 的 调 用 将 是 确 定 性 的 。<br />

参 看 retractall/1 和 retractFactDb。<br />

提 示<br />

retract/1 谓 词 不 能 应 用 于 single 事 实 或 事 实 变 量 。<br />

如 果 在 工 程 当 前 范 围 中 声 明 了 single 事 实 , 以 自 由 的 FactTemplate 变 量 调 用 retract/1 时 要 特 别 小<br />

心 。 要 是 对 single 事 实 做 retract, 会 产 生 运 行 时 错 误 。<br />

在 没 有 匹 配 的 事 实 时 ,retract/1 谓 词 失 败 。<br />

retractall/1<br />

retractall : ( FactTerm) .<br />

98


从 事 实 数 据 库 中 删 除 所 有 匹 配 的 事 实 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

retractall(FactTemplate)<br />

FactTemplate 应 是 一 个 事 实 项 。<br />

retractall/1 删 除 所 有 与 给 定 的 FactTemplate 匹 配 的 事 实 ( 在 未 命 名 的 事 实 数 据 库 中 是 事 实 数 据 库<br />

谓 词 for facts database predicates in unnamed facts databases)。 它 总 是 成 功 , 即 使 没 有 删 除 什 么<br />

事 实 时 也 会 成 功 。<br />

注 意 , 因 为 不 可 能 删 除 single 事 实 , 所 以 这 个 谓 词 不 会 删 除 在 未 命 名 事 实 数 据 库 中 匹 配 的 single 事 实<br />

或 事 实 变 量 。 不 过 , 如 果 编 译 时 编 译 器 发 现 匹 配 的 事 实 只 有 一 些 single 事 实 , 就 会 产 生 编 译 时 的 错 误 。<br />

由 retractall/1 不 可 能 得 到 任 何 输 出 值 , 因 此 , 调 用 时 所 有 变 量 必 须 都 是 绑 定 的 或 是 单 个 下 划 线 ( 匿<br />

名 的 )。 注 意 ,FactTemplate 可 以 是 实 例 的 任 意 层 级 , 但 自 由 变 量 必 须 是 单 个 下 划 线 ( 无 条 件 匿 名 ),<br />

与 retract/1 不 同 , 以 下 划 线 开 头 的 条 件 匿 名 变 量 ( 如 _AnyValue) 不 能 在 retractall/1 中 使 用 。<br />

参 看 retract/1 和 retractFactDb/1。<br />

retractFactDb/1<br />

retractFactDb : (factDB FactDB).<br />

从 命 名 的 内 部 事 实 数 据 库 FactDB 中 删 除 所 有 的 事 实 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

retractFactDb(FactDB)<br />

retractFactDb/1 从 命 名 的 内 部 事 实 数 据 库 FactDB 中 删 除 所 有 的 事 实 。 注 意 , 对 single 的 事 实 或<br />

事 实 变 量 仍 会 原 样 保 留 下 来 。 参 看 retractall/1 和 retract/1。<br />

retractall/2<br />

废 弃 的 谓 词 ! 用 retractFactDb/1 替 代 。<br />

sizeBitsOf/1-><br />

sizeBitsOf : ( DomainName) -> unsigned BitSize.<br />

获 取 指 定 域 DomainName 的 实 体 在 内 存 中 占 用 的 比 特 位 数 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

BitSize = sizeBitsOf(DomainName)<br />

这 个 编 译 时 谓 词 以 域 DomainName 为 输 入 参 数 , 返 回 该 域 的 实 体 占 用 内 存 的 大 小 , 结 果 以 比 特 位 计 。<br />

99


对 整 数 域 sizeBitsOf/1-> 返 回 的 值 是 域 声 明 中 的 size 字 段 的 定 义 值 。 对 整 数 域 来 说 , 下 面 的 关 系 总<br />

是 成 立 的 :<br />

sizeOfDomain(domain)*8 - 7 <br />

sizeOf : ( Term) -> unsigned ByteSize.<br />

获 取 指 定 项 Term 占 用 的 内 存 字 节 数 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

ByteSize = sizeOf(Term)<br />

sizeOf/1-> 函 数 以 一 个 项 为 输 入 参 数 , 返 回 Term 项 占 用 内 存 的 字 节 数 ByteSize, 这 个 值 是 无 符 号<br />

整 数 值 。<br />

sizeOfDomain/1-><br />

sizeOfDomain : ( DomainName) -> unsigned ByteSize.<br />

获 取 指 定 域 DomainName 的 实 体 在 内 存 中 占 用 的 字 节 数 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

ByteSize = sizeOfDomain(DomainName)<br />

这 个 编 译 时 谓 词 以 域 DomainName 为 输 入 参 数 , 返 回 该 域 的 实 体 占 用 内 存 的 大 小 , 结 果 以 字 节 计 ,<br />

ByteSize 的 值 是 无 符 号 整 数 域 的 。 可 以 对 比 一 下 sizeBitsOf/1->, 在 它 那 里 以 比 特 计 算 大 小 。<br />

sourcefile_lineno/0-><br />

sourcefile_lineno : () -> unsigned LineNumber.<br />

返 回 编 译 器 处 理 的 源 文 件 的 当 前 行 号 。<br />

描 述<br />

这 个 编 译 时 谓 词 返 回 编 译 器 正 在 处 理 的 源 文 件 的 当 前 行 号 。<br />

sourcefile_name/0-><br />

sourcefile_name : () -> string FileName.<br />

返 回 编 译 器 处 理 的 源 文 件 名 。<br />

100


描 述<br />

这 个 编 译 时 谓 词 返 回 表 示 编 译 器 正 在 处 理 的 源 文 件 名 的 串 。<br />

sourcefile_timestamp/0-><br />

sourcefile_timestamp : () -> string TimeStamp..<br />

返 回 表 示 编 译 器 处 理 的 源 文 件 日 期 时 间 的 串 。<br />

描 述<br />

这 个 编 译 时 谓 词 按 YYYY-MM-DD HH:MM:SS 格 式 返 回 表 示 编 译 器 正 在 处 理 的 源 文 件 日 期 时 间 的<br />

串 , 格 式 中 :<br />

YYYY – 年<br />

MM – 月<br />

DD – 日<br />

HH – 小 时<br />

MM – 分 钟<br />

SS – 秒<br />

succeed/0<br />

succeed : ().<br />

谓 词 succeed/0 总 是 成 功 。<br />

描 述<br />

标 准 谓 词 succeed/0 明 确 地 成 功 一 次 。<br />

toBinary/1-><br />

toBinary : (Term) -> binary Serialized.<br />

将 项 Term 转 换 成 binary 表 示 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

Serialized = toBinary(Term)<br />

当 ( 某 个 域 domainName) 的 项 Term 转 换 成 二 进 制 项 时 , 可 以 安 全 地 存 储 在 文 件 中 或 是 在 网 络 上 传<br />

给 另 一 个 程 序 。 以 后 这 个 二 进 制 项 值 Serialized 还 可 以 用 函 数 toTerm/1-> 转 换 回 <strong>Visual</strong> <strong>Prolog</strong> 项 ( 反<br />

转 项 的 域 要 适 合 于 domainName)。<br />

toBoolean/1-><br />

toBoolean : ( SubGoal) -> boolean Succeed.<br />

101


这 个 元 谓 词 的 目 标 是 要 把 ( 对 一 个 谓 词 或 事 实 的 ) 确 定 性 的 调 用 转 换 成 procedure 的 , 返 回 boolean<br />

域 的 值 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

True_or_False = toBoolean(deterministic_call)<br />

元 谓 词 toBoolean/1-> 返 回 boolean 值 。 如 果 确 定 性 调 用 成 功 则 返 回 结 果 是 true, 如 果 确 定 性 调 用<br />

失 败 返 回 结 果 是 false。<br />

toString/1-><br />

toString : (Term) -> string Serialized.<br />

将 指 定 的 项 Term 转 换 为 串 表 示 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

Serialized = toString(Term)<br />

当 ( 某 个 域 的 )Term 项 转 换 成 一 个 串 时 , 它 可 以 安 全 地 保 存 在 文 件 中 或 是 经 网 络 传 送 给 另 外 的 程 序 。<br />

以 后 串 值 还 可 以 用 函 数 toTerm/1-> 转 换 回 <strong>Visual</strong> <strong>Prolog</strong> 项 ( 反 转 项 的 域 要 适 合 于 domainName)。<br />

toTerm/1-><br />

toTerm : (string Serialized) -> Term.<br />

toTerm : (binary Serialized) -> Term.<br />

将 指 定 的 项 Serialized 的 串 / 二 进 制 表 示 转 换 为 相 应 域 的 返 回 值 变 量 的 <strong>Prolog</strong> 的 Term。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

Term = toTerm(Serialized)<br />

在 编 译 时 , 编 译 器 可 以 确 定 返 回 值 Term 的 域 。 要 注 意 , 二 进 制 版 的 toTerm 谓 词 几 乎 是 逐 字 节 地 做<br />

转 换 , 并 且 只 检 查 返 回 值 Term 的 域 所 要 求 的 与 Serialized 数 据 的 兼 容 性 。 保 证 提 供 的 Serialized 二 进 制<br />

数 可 以 正 确 地 转 换 到 期 望 的 域 是 程 序 员 的 事 情 。<br />

toTerm 谓 词 与 toBinary/1-> 和 toString/1-> 谓 词 是 配 套 的 , 当 ( 某 个 域 的 )Term 项 由 toBinary/1-><br />

或 toString/1-> 转 换 成 一 个 二 进 制 或 串 的 Serialized 时 , 它 可 以 安 全 地 保 存 在 文 件 中 或 是 经 网 络 传 送 给 另<br />

外 的 程 序 。 以 后 用 对 应 的 函 数 toTerm/1-> 还 可 以 把 这 个 串 / 二 进 制 值 Serialized 转 换 回 <strong>Visual</strong> <strong>Prolog</strong><br />

的 项 Term。 为 正 确 地 反 转 项 , 子 句 变 量 Term 的 域 要 适 合 于 原 来 的 域 domainName。<br />

参 看 tryToTerm。<br />

异 常<br />

如 果 编 译 时 编 译 器 不 能 确 定 返 回 值 的 域 , 则 出 现 编 译 时 错 误 。<br />

102


当 toTerm/1-> 谓 词 不 能 转 换 串 或 二 进 制 项 为 指 定 域 的 项 时 , 会 产 生 运 行 时 错 误 。<br />

tryToTerm/1-><br />

tryToTerm : (string Serialized)-> Term determ.<br />

tryToTerm : (binary Serialized)-> Term determ.<br />

如 同 toTerm 一 样 , 转 换 相 应 的 串 或 二 进 制 表 示 的 Serialized 为 项 Term, 它 们 之 间 的 差 别 是 : 谓 词<br />

tryToTerm 如 果 不 能 把 相 应 的 串 或 二 进 制 项 转 换 为 指 定 域 的 项 时 就 失 败 , 而 toTerm 谓 词 则 会 产 生 异 常 。<br />

参 看 toTerm。<br />

tryConvert/2-><br />

tryConvert : ( Type, Value) determ -> Converted determ.<br />

核 查 输 入 项 Value 能 否 被 严 格 地 转 换 为 指 定 域 Type 的 项 , 返 回 转 换 后 的 项 Converted。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

ReturnTerm = tryConvert(returnDomain, InputTerm)<br />

参 数 :<br />

returnDomain: 规 定 tryConvert/2-> 谓 词 尝 试 将 指 定 项 InputTerm 转 换 到 该 域 , 它 可 以 是 当 前<br />

范 围 可 访 问 的 任 何 域 。 域 名 returnDomain 在 编 译 时 必 须 是 确 定 的 , 即 不 能 来 自 于 变 量 。<br />

InputTerm: 要 转 换 的 项 , 它 可 以 是 任 意 <strong>Prolog</strong> 项 或 表 达 式 , 如 果 是 表 达 式 , 转 换 前 要 求 值 。<br />

ReturnTerm: 返 回 的 项 , 它 是 returnDomai 域 的 。<br />

备 注<br />

转 换 规 则 tryConvert/2-> 与 内 含 谓 词 convert/2-> 是 一 样 的 , 但 convert/2-> 产 生 转 换 错 误 时 , 同<br />

样 条 件 下 的 tryConvert/2-> 会 是 失 败 。<br />

如 果 相 应 的 转 换 成 功 则 这 个 谓 词 成 功 , 反 之 它 就 失 败 。 该 谓 词 尝 试 把 InputTerm 彻 底 转 换 到 指 定 的<br />

returnDomain 域 , 若 它 不 能 完 成 要 求 的 转 换 , 就 会 失 败 。 当 tryConvert/2-> 谓 词 成 功 时 , 它 会 返 回 转<br />

换 到 指 定 域 returnDomain 的 项 ReturnTerm。<br />

允 许 的 转 换 及 规 则 请 参 看 带 核 查 的 显 式 转 换 谓 词 convert/2->。 还 可 参 看 uncheckedConvert/2->。<br />

uncheckedConvert/2-><br />

uncheckedConvert : ( Type, Value) -> Converted.<br />

将 值 转 换 为 另 一 个 类 型 , 不 带 核 查 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

ReturnTerm = uncheckedConvert(returnDomain, InputTerm)<br />

参 数 :<br />

103


eturnDomain: 指 定 uncheckedConvert 要 将 InputTerm 非 安 全 性 地 转 换 到 该 域 , 它 可 以 是 当 前 范<br />

围 能 够 访 问 的 任 何 域 。ReturnTerm 和 InputTerm 的 比 特 位 的 长 短 应 该 是 一 样 的 。 域 名 称 returnDomain<br />

在 编 译 时 必 须 是 确 定 的 , 也 就 是 说 , 不 能 用 变 量 来 表 示 。<br />

InputTerm: 指 定 要 转 换 的 值 , 它 可 以 是 任 意 <strong>Prolog</strong> 项 或 表 达 式 。 如 果 是 表 达 式 , 在 转 换 前 先 要 求<br />

值 。<br />

ReturnTerm: 返 回 参 数 ReturnTerm 是 returnDomain 类 型 的 。<br />

备 注<br />

uncheckedConvert 谓 词 进 行 一 个 很 武 断 的 转 换 。 它 对 InputTerm 进 行 预 求 值 ( 如 果 它 是 一 个 表<br />

达 式 的 话 ), 将 其 当 前 的 类 型 转 换 到 returnDomain 的 类 型 并 与 ReturnTerm 合 一 。 它 在 运 行 时 不 做 核 查 ,<br />

所 以 差 不 多 任 何 项 都 可 以 很 莽 撞 地 转 换 为 别 的 类 型 。 因 而 , 不 正 确 地 使 用 它 可 能 会 导 致 很 糟 糕 的 结 果 , 一<br />

定 要 小 心 ! 强 烈 建 议 , 只 要 可 能 就 避 免 使 用 它 而 代 之 以 convert/2-> 和 tryConvert/2->。 不 过 , 当 对 象 是<br />

由 COM 系 统 返 回 的 时 候 , 需 要 用 uncheckedConvert 来 进 行 转 换 , 因 为 <strong>Prolog</strong> 程 序 没 有 关 于 它 的 实 际 类<br />

型 的 信 息 。<br />

upperBound/1-><br />

upperBound : ( NumericDomain) -> UpperBound.<br />

返 回 指 定 数 字 域 的 上 限 值 。<br />

描 述<br />

这 个 谓 词 的 调 用 模 板 是 :<br />

UpperBound = upperBound(domainName)<br />

upperBound 是 一 个 编 译 时 谓 词 , 它 返 回 指 定 数 字 域 domainName 的 上 限 值 。upperBound 谓 词 返<br />

回 的 值 UpperBound 也 是 domainName 域 的 。domainName 参 数 必 须 是 任 意 数 字 域 的 名 称 , 编 译 时 必 须<br />

是 确 定 的 ( 即 不 能 来 自 于 变 量 )。<br />

参 看 lowerBound/1->。<br />

异 常<br />

如 果 指 定 的 域 不 是 数 字 域 , 则 会 出 现 编 译 时 错 误 。<br />

编 译 指 令<br />

编 译 器 的 每 个 指 令 都 是 以 字 符 # 开 头 的 , 所 支 持 的 指 令 如 下 :<br />

• #include, #bininclude – 文 件 包 含 ;<br />

• #if, #then, #else, #elseif, #endif – 条 件 语 句 ;<br />

• #export, #externally – 输 出 及 引 入 类 ;<br />

• #message, #error, #requires, #orrequires – 编 译 时 间 信 息 ;<br />

• #options – 编 译 器 选 项 。<br />

源 文 件 的 包 含<br />

104


编 译 指 令 #include 用 于 将 其 它 文 件 的 内 容 在 编 译 时 包 含 进 用 户 开 发 的 程 序 代 码 中 。 其 语 法 如 下 :<br />

Pp_dir_include :<br />

#include String_literal<br />

文 字 串 (string literal) 应 该 指 向 一 个 已 有 文 件 名 。 文 件 名 可 以 包 含 路 径 名 , 但 要 记 住 : 用 于 子 目 录<br />

的 反 斜 杠 字 符 \ 是 一 个 换 码 序 列 符 , 因 此 在 要 使 用 它 的 地 方 要 写 两 次 :<br />

#include "pfc\\exception\\exception.ph"<br />

% 包 含 pfc\exception\exception.ph 文 件<br />

或 者 用 @ 字 符 在 文 件 名 前 作 前 缀 , 像 这 样 :<br />

#include @"pfc\vpi\vpimessage\vpimessage.ph"<br />

pfc\vpi\vpimessage\vpimessage.ph 文 件<br />

% 包 含<br />

从 语 义 上 来 讲 , 这 个 指 令 使 用 “ 仅 包 含 第 一 次 出 现 的 文 件 ” 策 略 , 也 就 是 说 , 如 果 一 个 编 译 单 元 中 对<br />

同 一 个 文 件 有 多 个 包 括 指 令 , 则 只 采 用 第 一 个 出 现 的 包 括 指 令 。<br />

各 包 含 文 件 必 须 含 有 分 开 的 已 经 完 成 的 范 围 。 包 含 文 件 不 能 含 有 未 完 成 的 范 围 , 也 就 是 说 , 它 含 有 的<br />

是 分 开 的 已 经 完 成 的 接 口 声 明 、 类 声 明 、 类 实 现 或 / 和 分 开 的 编 译 指 令 。<br />

编 译 器 按 下 述 方 法 来 查 找 指 定 包 括 的 源 文 件 :<br />

1. 如 果 文 件 名 中 包 括 了 绝 对 路 径 , 就 直 接 包 括 该 文 件 ;<br />

2. 否 则 , 编 译 器 就 按 命 令 行 选 项 /Include 定 义 的 路 径 搜 索 指 定 的 包 含 文 件 名 , 有 多 个 路 径 时 按 顺<br />

序 使 用 。 可 以 在 VDE 中 Project Settings 的 Directories 标 签 里 的 Include Directories 中 设<br />

置 这 些 路 径 。<br />

如 果 编 译 器 找 不 到 指 定 文 件 , 就 会 出 现 编 译 时 错 误 。<br />

二 进 制 文 件 的 包 含<br />

编 译 指 定 #bininclude 用 于 ( 将 由 string literal 串 指 定 的 ) 文 件 内 容 作 为 ::binary 类 型 的 常 数 包 含 进<br />

用 户 开 发 的 程 序 源 代 码 中 。 其 语 法 如 下 :<br />

Pp_dir_bininclude :<br />

#bininclude ( String_literal )<br />

#bininclude 编 译 指 令 告 诉 编 译 器 把 指 定 文 件 的 内 容 当 作 一 个 ::binary 常 数 的 值 插 入 。 这 个 指 令 可 以<br />

用 在 任 何 ::binary 常 数 可 以 使 用 的 地 方 。<br />

典 型 的 使 用 方 法 如 下 :<br />

constants<br />

myBin : ::binary = #bininclude ("Bin.bin"). % 由 “Bin.bin” 文 件 创 建 一 个 二 进 制 常 数 值<br />

myBin2 = #bininclude ("Bin2.bin"). % 初 始 化 一 个 二 进 制 常 数 的 简 式<br />

在 上 面 这 个 例 子 中 , 分 析 第 一 个 常 数 myBin 的 声 明 时 , 编 译 器 碰 到 了 #bininclude ("Bin.bin") 指<br />

令 。 于 是 它 在 包 含 #bininclude ("Bin.bin") 这 个 指 令 的 源 文 件 所 在 的 目 录 中 、 在 Project Settings 对<br />

话 框 的 Include Directories 表 框 中 指 定 的 目 录 中 ( 在 /Include 命 令 行 选 项 指 定 的 目 录 中 ) 查 找 是 否 有<br />

这 个 Bin.bin 文 件 。 如 果 找 到 了 , 编 译 器 就 读 这 个 文 件 , 按 该 文 件 的 内 容 形 成 正 确 的 ::binary 域 的 数 据 并<br />

将 其 作 为 myBin 常 数 的 值 。 比 如 说 ,Bin.bin 文 件 中 只 有 一 个 字 符 A,myBin 常 数 就 会 有 下 面 这 样 的 值 :<br />

105


myBin = $[0x41]<br />

% 这 里 的 0x41 是 'A' 的 十 六 进 制 ASCII 码 值<br />

string literal 应 该 指 向 一 个 已 有 文 件 名 。 这 个 指 令 的 语 法 与 前 面 “ 源 文 件 的 包 含 ” 中 的 #include 指<br />

令 是 一 样 的 , 搜 索 指 定 文 件 的 方 法 也 一 样 。<br />

输 出 和 引 入 类<br />

编 译 指 令 #export 和 #externally 分 别 用 于 确 定 要 输 出 的 类 及 要 引 入 的 类 。 其 语 法 如 下 :<br />

Pp_dir_export :<br />

#export ClassNames-comma-sep-list<br />

Pp_dir_export :<br />

#externally ClassNames-comma-sep-list<br />

这 两 个 编 译 指 令 只 能 用 于 不 构 造 对 象 的 类 classNames。 而 且 , 它 们 也 只 能 用 于 外 部 范 围 , 这 就 是 说 ,<br />

它 们 不 能 用 在 接 口 和 类 的 声 明 里 , 也 不 能 用 在 类 的 实 现 里 。<br />

缺 省 时 , 在 一 个 执 行 单 元 内 部 的 谓 词 对 其 它 执 行 单 元 来 说 是 完 全 隐 藏 的 。 而 编 译 指 令 #export 使 它 所<br />

指 定 的 那 些 类 名 对 外 部 开 放 了 , 因 而 运 行 时 这 些 类 中 声 明 的 模 块 里 的 所 有 谓 词 对 其 它 执 行 单 元 就 是 可 访 问<br />

的 。<br />

一 般 情 况 下 ,#export 编 译 指 令 用 在 目 标 模 块 是 DLL 的 工 程 中 , 它 列 出 一 个 DLL 中 所 声 明 的 类 , 这 些<br />

列 出 的 类 就 可 以 被 使 用 这 个 DLL 的 其 它 模 块 所 访 问 。<br />

如 果 某 个 编 译 单 元 输 出 一 个 类 , 该 编 译 单 元 就 应 该 含 有 这 个 类 的 实 现 。( 译 注 : 原 文 为 “If a compilation<br />

unit export some class, then this compilation unit should not contain this class implementation.”<br />

not 应 该 是 错 误 的 )<br />

#export 编 译 指 令 还 可 以 用 在 #if 编 译 指 令 中 规 定 condition 表 达 式 。 例 如 , 假 设 在 一 个 编 译 单 元 开 始<br />

的 地 方 编 译 器 碰 到 了 这 样 的 #export 编 译 指 令 :<br />

#export className<br />

而 在 后 面 编 译 器 又 遇 到 一 个 #if 编 译 指 令 , 它 以 #export 编 译 指 令 为 条 件 表 达 式 , 比 如 说 是 这 样 的 :<br />

#if #export className #then ... #endif<br />

那 么 , 编 译 器 就 会 对 #export 条 件 表 达 式 求 值 , 在 我 们 这 里 其 结 果 是 true, 接 着 就 会 执 行 条 件 编 译<br />

指 令 的 #then 分 支 。<br />

比 如 , 下 面 这 个 例 子 中 , 就 能 保 证 把 some.pack 包 包 含 到 所 编 译 的 单 元 中 :<br />

#export className<br />

...<br />

#if #export className #then #requires "some.pack" #endif<br />

而 另 一 方 面 , 如 果 没 有 前 一 个 #export 编 译 指 令 , 编 译 器 对 #export 条 件 表 达 式 的 求 值 就 会 是 false,<br />

也 就 不 会 执 行 #if 的 分 支 #then, 比 如 例 子 是 这 样 :<br />

#if #export className #then #requires "some.pack" #endif<br />

这 时 , 就 不 会 请 求 包 含 some.pack 包 。<br />

#externally 编 译 指 令 是 与 #export 编 译 指 令 配 对 的 , 它 可 以 替 代 ( 也 可 以 同 时 使 用 )IMPORTS<br />

106


指 令 用 于 定 义 文 件 中 。 这 个 指 令 列 出 一 些 类 , 它 们 是 在 一 个 模 块 中 声 明 的 但 却 是 在 其 它 模 块 中 实 现 的 。 这<br />

样 , 当 编 译 器 遇 到 这 样 的 类 时 就 不 会 发 生 错 误 。 引 述 的 类 可 以 是 在 DLL 中 实 现 ( 并 输 出 ) 的 , 运 行 时 可 以<br />

将 这 些 DLL 链 接 到 这 个 模 块 上 来 。<br />

#export 和 #externally 编 译 指 令 可 以 在 Conditional Directives 中 用 作 条 件 布 尔 表 达 式 。 例 如 :<br />

#if #export className #then #include "Some package.pack" #endif<br />

编 译 时 消 息<br />

在 编 译 工 程 模 块 时 , 可 以 用 编 译 指 令 #message、#requires、#orrequires 和 #error 来 发 布 用<br />

户 定 义 的 消 息 到 一 个 列 表 文 件 中 或 中 断 编 译 过 程 。<br />

这 些 指 令 既 可 以 用 在 范 围 ( 接 口 声 明 、 类 声 明 或 类 实 现 ) 之 外 , 也 可 以 用 在 范 围 内 但 在 段 之 外 。 其 语<br />

法 如 下 :<br />

Pp_dir_message :<br />

#message String_literal<br />

Pp_dir_error :<br />

#error String_literal<br />

Pp_dir_requires :<br />

#requires String_literal Pp_dir_orrequires-list-opt<br />

Pp_dir_orrequires :<br />

#orrequires String_literal<br />

当 编 译 器 遇 到 上 列 任 何 一 个 指 令 时 , 都 会 产 生 相 应 的 警 告 信 息 并 把 指 令 的 文 本 内 容 写 到 一 个 列 表 文 件<br />

中 去 。<br />

编 译 指 令 中 可 以 指 定 一 个 列 表 文 件 名 :<br />

/listingfile:"FileName"<br />

注 意 , 在 冒 号 前 后 都 不 要 有 空 格 。<br />

缺 省 时 , 编 译 器 不 会 为 #message、#requires 和 #orrequires 指 令 产 生 消 息 , 程 序 员 可 以 用 编 译<br />

器 选 项 打 开 这 些 消 息 :<br />

/listing:message<br />

/listing:requires<br />

/listing:ALL<br />

在 这 种 情 况 下 , 当 编 译 器 遇 到 这 些 指 令 , 比 如 :<br />

#message "Some message"<br />

就 会 把 下 面 的 文 本 写 到 列 表 文 件 中 去 :<br />

C:\Tests\test\test.pro(14,10) : information c062: #message "Some message"<br />

#requires (#orrequires) 指 令 会 把 用 户 定 义 的 关 于 需 要 源 ( 目 标 ) 文 件 的 消 息 放 到 列 表 文 件 中 去 。<br />

#orrequires 指 令 不 能 单 独 使 用 , 紧 靠 它 之 前 ( 用 空 格 或 注 释 隔 开 ) 应 有 #requires 指 令 。<br />

107


#error 指 令 总 是 终 止 编 译 并 发 布 用 户 定 义 的 错 误 消 息 到 列 表 文 件 中 , 如 :<br />

C:\Tests\test\test.pro(14,10) : error c080: #error "Compilation is interrupted"<br />

可 以 对 这 些 消 息 进 行 分 析 并 接 受 所 要 求 的 事 情 。 例 如 ,VDE 会 对 #requires 和 #orrequires 指 令 给<br />

出 的 消 息 进 行 分 析 , 自 动 地 添 加 所 有 需 要 的 PFC 包 及 标 准 库 到 被 编 译 的 工 程 ( 参 看 Handling Project<br />

Modules 主 题 )。<br />

#requires 和 #orrequires 指 令 的 例 子 :<br />

#requires @"\Common\Sources\CommonTypes.pack"<br />

#orrequires @"\Common\Lib\CommonTypes.lib"<br />

#orrequires @"\Common\Obj\Foreign.obj"<br />

#if someClass::debugLevel > 0 #then<br />

#requires @"\Sources\Debug\Tools.pack"<br />

#orrequires @"\Lib\Debug\Tools.lib"<br />

#else<br />

#requires @"\Sources\Release\Tools.pack"<br />

#orrequires @"\Lib\Release\Tools.lib"<br />

#endif<br />

#orrequires "SomeLibrary.lib"<br />

#requires "SomePackage.pack"<br />

#if someClass::debugLevel > 0 #then<br />

#orrequires @"\Debug\SomePackage.lib"<br />

#else<br />

#orrequires @"\Release\SomePackage.lib"<br />

#endif<br />

#message 指 令 的 例 子 :<br />

#message "Some text"<br />

#if someClass::someConstant > 0 #then<br />

#message "someClass::someConstant > 0"<br />

#else<br />

#message "someClass::someConstant


编 译 指 令 #options 影 响 整 个 编 译 单 元 。 这 个 指 令 只 能 用 在 外 部 范 围 及 源 文 件 的<br />

条 件 编 译 语 句 中 ( 编 译 单 元 通 过 这 个 源 文 件 传 递 给 编 译 器 ), 否 则 就 会 出 现 编 译 时 的 警 告 消 息 并 忽 略 这 个<br />

指 令 。<br />

只 能 包 含 下 面 的 编 译 选 项 :<br />

"/Warning"<br />

"/Check"<br />

"/NOCheck"<br />

"/Optimize"<br />

"/DEBug"<br />

"/GOAL"<br />

"/MAXErrors"<br />

"/MAXWarnings"<br />

对 其 它 的 输 入 编 译 器 会 产 生 非 法 选 项 的 错 误 消 息 。 如 果 有 若 干 个 #options 指 令 , 则 会 按 文 本 顺 序 处<br />

理 它 们 。<br />

条 件 编 译<br />

条 件 编 程 结 构 是 <strong>Visual</strong> <strong>Prolog</strong> 的 部 件 。 编 译 单 元 和 程 序 段 ( 包 括 空 的 ) 可 以 条 件 化 。 其 语 法 如 下 :<br />

ConditionalItem :<br />

#if Condition #then CompilationItem-list-opt ElseIfItem-list-opt<br />

ElseItem-opt #endif<br />

ElseIfItem :<br />

#elseif Condition #then CompilationItem<br />

ElseItem :<br />

#else CompilationItem<br />

这 里 的 Condition 可 以 是 任 意 表 达 式 , 它 在 编 译 时 应 能 得 到 失 败 或 成 功 的 求 值 结 果 。<br />

各 个 条 件 编 译 语 句 必 须 在 一 个 文 件 中 , 也 就 是 说 , 嵌 套 的 相 同 层 次 的 编 译 指 令 #if、#then、#elseif<br />

和 #else( 如 果 有 的 话 ) 及 #endif 必 须 在 一 个 文 件 中 。<br />

编 译 时 , 编 译 器 会 对 条 件 求 值 , 以 便 确 定 哪 些 部 分 要 包 括 在 最 终 文 件 中 。 排 除 在 最 终 程 序 外 的 那 些 部<br />

分 称 为 死 分 支 。<br />

所 有 条 件 编 译 分 支 项 都 要 进 行 语 法 检 查 , 语 法 上 必 须 正 确 。 即 便 是 对 死 分 支 , 语 法 上 也 要 正 确 。 但 编<br />

译 器 只 对 所 需 要 的 条 件 做 求 值 运 算 , 不 会 对 死 分 支 上 的 条 件 求 值 。<br />

在 条 件 句 中 出 现 的 条 件 文 本 , 不 应 该 依 赖 于 任 何 代 码 。<br />

乙 丁 译<br />

its2u@qq.com<br />

109

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!