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

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

<strong>Visual</strong> <strong>Prolog</strong> 7 边 练 边 学<br />

Contents<br />

<strong>Visual</strong> <strong>Prolog</strong> 7 边 练 边 学 ................................................................................................................. 1<br />

第 一 章 简 介 ................................................................................................................................. 5<br />

第 1.1 节 创 建 一 个 新 的 GUI 程 序 ......................................................................................... 5<br />

第 1.2 节 编 译 和 运 行 你 的 程 序 .............................................................................................. 6<br />

第 1.3 节 玩 一 把 : 把 程 序 的 显 示 标 题 改 成 我 的 名 字 .......................................................... 8<br />

第 1.4 节 小 结 和 说 明 .............................................................................................................. 9<br />

第 1.5 节 逻 辑 小 知 识 : 希 腊 人 发 明 了 逻 辑 ........................................................................ 10<br />

第 二 章 Forms ............................................................................................................................ 10<br />

第 2.1 节 创 建 一 个 新 的 Form .............................................................................................. 10<br />

第 2.2 节 让 任 务 菜 单 的 项 目 工 作 起 来 ................................................................................ 12<br />

第 2.3 节 用 Code Expert 添 加 程 序 .................................................................................... 13<br />

第 2.4 节 小 结 和 说 明 .......................................................................................................... 15<br />

第 2.5 节 逻 辑 小 知 识 : 亚 里 士 多 德 的 符 号 逻 辑 ................................................................ 15<br />

第 三 章 鼠 标 事 件 ....................................................................................................................... 16<br />

第 3.1 节 增 加 鼠 标 按 下 事 件 侦 听 器 .................................................................................... 16<br />

第 3.2 节 onPaint 绘 图 .......................................................................................................... 18<br />

第 3.3 节 小 结 和 说 明 ............................................................................................................ 20<br />

第 3.4 节 逻 辑 小 知 识 : 布 尔 代 数 ........................................................................................ 21<br />

第 3.5 节 论 证 的 形 式 ............................................................................................................ 21<br />

第 四 章 基 本 图 例 ....................................................................................................................... 22<br />

第 4.1 节 任 务 菜 单 ................................................................................................................ 22<br />

第 4.2 节 目 录 树 .................................................................................................................... 23<br />

第 4.3 节 创 建 一 个 工 程 项 目 ................................................................................................ 25<br />

第 4.4 节 创 建 一 个 新 的 class ............................................................................................... 27<br />

第 4.5 节 设 置 控 件 属 性 ........................................................................................................ 28<br />

第 4.6 节 小 结 和 说 明 .......................................................................................................... 29<br />

第 4.7 节 逻 辑 小 知 识 : 谓 词 运 算 ........................................................................................ 29<br />

第 五 章 Horn 语 句 ..................................................................................................................... 30<br />

第 5.1 节 函 数 ........................................................................................................................ 30<br />

第 5.2 节 谓 词 ........................................................................................................................ 30<br />

第 5.3 节 求 解 ........................................................................................................................ 30<br />

第 5.4 节 答 案 不 惟 一 ............................................................................................................ 34<br />

第 5.5 节 逻 辑 符 号 ................................................................................................................ 36<br />

第 5.6 节 Horn 句 子 .............................................................................................................. 36<br />

第 5.7 节 声 明 ........................................................................................................................ 37<br />

第 5.8 节 绘 图 谓 词 ................................................................................................................ 38<br />

第 5.8 节 GDI 实 体 ................................................................................................................ 39


第 5.9 节 小 结 和 说 明 .......................................................................................................... 40<br />

第 5.10 节 逻 辑 小 知 识 :Horn 句 子 的 意 义 ......................................................................... 40<br />

第 六 章 控 制 台 应 用 ................................................................................................................... 41<br />

第 6.1 节 截 断 ........................................................................................................................ 41<br />

第 6.2 节 表 ............................................................................................................................ 43<br />

第 6.3 节 表 相 关 的 几 个 话 题 ................................................................................................ 48<br />

第 6.4 节 字 符 串 操 作 ............................................................................................................ 52<br />

第 6.5 节 逻 辑 小 知 识 : 谓 词 的 语 法 .................................................................................... 61<br />

第 七 章 文 本 编 辑 器 ................................................................................................................... 61<br />

第 7.1 节 建 立 文 本 编 辑 器 程 序 ............................................................................................ 61<br />

第 7.2 节 文 件 保 存 ................................................................................................................ 63<br />

第 7.3 节 文 件 调 入 ................................................................................................................ 64<br />

第 7.4 节 编 辑 器 的 改 进 ........................................................................................................ 66<br />

第 八 章 绘 图 ............................................................................................................................... 67<br />

第 8.1 节 onPainting 相 关 的 话 题 ......................................................................................... 67<br />

第 8.2 节 Custom Control 的 应 用 ......................................................................................... 72<br />

第 九 章 数 据 类 型 ....................................................................................................................... 75<br />

第 9.1 节 原 始 数 据 类 型 ........................................................................................................ 75<br />

第 9.2 节 集 合 ........................................................................................................................ 76<br />

第 9.3 节 数 字 的 集 合 ............................................................................................................ 76<br />

第 9.4 节 实 数 ........................................................................................................................ 78<br />

第 9.5 节 格 式 ........................................................................................................................ 78<br />

第 9.6 节 域 ............................................................................................................................ 79<br />

第 十 章 如 何 用 <strong>Prolog</strong> 解 决 问 题 .............................................................................................. 84<br />

第 10.1 节 搜 索 一 个 表 的 所 有 元 素 ...................................................................................... 84<br />

第 10.2 节 寻 求 两 个 表 的 交 集 表 .......................................................................................... 86<br />

第 10.3 节 两 个 表 的 合 并 ...................................................................................................... 87<br />

第 10.4 节 表 的 倒 序 排 列 ..................................................................................................... 88<br />

第 10.5 节 表 的 分 割 ............................................................................................................. 90<br />

第 10.6 节 表 的 快 速 排 序 ..................................................................................................... 91<br />

第 10.7 节 一 年 有 多 少 天 ?.................................................................................................. 92<br />

第 10.8 节 地 图 填 色 问 题 ...................................................................................................... 93<br />

第 10.9 节 翻 子 游 戏 .............................................................................................................. 97<br />

第 10.10 节 NIM 游 戏 ........................................................................................................... 98<br />

第 十 一 章 事 实 ......................................................................................................................... 106<br />

第 11.1 节 数 据 库 记 录 的 生 成 和 保 存 ................................................................................ 106<br />

第 11.2 节 数 据 库 记 录 文 件 的 读 取 .................................................................................... 108<br />

第 11.3 节 file 模 块 : 读 写 字 符 串 readString/writeString .................................................... 109<br />

第 11.4 节 常 数 .................................................................................................................... 111<br />

第 十 二 章 class 和 object ......................................................................................................... 112<br />

第 12.1 节 存 取 社 会 保 障 号 ................................................................................................ 112<br />

第 12.2 节 Object 的 事 实 .................................................................................................... 114<br />

第 十 三 章 乌 龟 爬 和 语 言 处 理 ................................................................................................. 115<br />

第 13.1 节 乌 龟 爬 行 ............................................................................................................ 115


第 13.2 节 乌 龟 的 状 态 描 述 ................................................................................................ 118<br />

第 13.3 节 画 出 其 他 的 规 则 图 形 ........................................................................................ 119<br />

第 13.4 节 保 证 每 次 画 出 的 是 一 样 的 图 形 ........................................................................ 121<br />

第 13.5 节 递 归 .................................................................................................................... 123<br />

第 13.6 节 利 用 递 归 方 法 画 复 杂 曲 线 ................................................................................ 124<br />

第 13.7 节 语 言 处 理 ............................................................................................................ 127<br />

第 十 四 章 L- 系 统 ..................................................................................................................... 131<br />

第 14.1 节 画 一 棵 树 ............................................................................................................ 131<br />

第 14.2 节 如 何 才 能 画 一 棵 树 ............................................................................................ 133<br />

第 14.3 节 画 出 不 同 的 树 .................................................................................................... 136<br />

第 十 五 章 游 戏 ......................................................................................................................... 137<br />

第 15.1 节 游 戏 描 述 ............................................................................................................ 137<br />

第 15.2 节 游 戏 设 计 前 的 分 析 ............................................................................................ 137<br />

第 15.3 节 游 戏 编 程 ............................................................................................................ 137<br />

第 15.4 节 游 戏 的 说 明 ........................................................................................................ 144<br />

第 15.5 节 游 戏 的 改 进 ........................................................................................................ 146<br />

第 15.6 节 实 体 事 实 Object Facts ....................................................................................... 149<br />

第 十 六 章 几 个 有 趣 的 部 件 ..................................................................................................... 150<br />

第 16.1 节 TabControl 的 应 用 ............................................................................................. 150<br />

第 16.2 节 ListControl 的 应 用 ............................................................................................ 155<br />

第 16.3 节 treeView Control、listView Control .................................................................. 169<br />

第 十 七 章 动 画 ......................................................................................................................... 177<br />

第 17.1 节 绘 图 .................................................................................................................. 178<br />

第 17.2 节 定 时 器 的 使 用 .................................................................................................... 179<br />

第 17.3 节 动 画 是 怎 么 实 现 的 ?........................................................................................ 181<br />

第 17.4 节 改 进 动 画 程 序 .................................................................................................... 182<br />

第 十 八 章 相 关 资 料 介 绍 、 索 引 ............................................................................................. 183<br />

第 18.1 节 <strong>Prolog</strong> 书 籍 ........................................................................................................ 183<br />

第 18.2 节 本 书 的 例 子 程 序 的 获 得 .................................................................................... 185<br />

第 18.3 节 中 国 的 知 识 基 系 统 研 究 .................................................................................... 185<br />

附 录 - 要 点 索 引 ..................................................................................................................... 185<br />

自 序 ....................................................................................................................................... 185<br />

第 一 章 简 介 ......................................................................................................................... 185<br />

第 二 章 Forms ...................................................................................................................... 186<br />

第 三 章 鼠 标 事 件 ................................................................................................................. 186<br />

第 四 章 基 本 图 例 ................................................................................................................. 186<br />

第 五 章 Horn 语 句 ............................................................................................................... 187<br />

第 六 章 控 制 台 应 用 ............................................................................................................. 188<br />

第 七 章 文 本 编 辑 器 ............................................................................................................. 189<br />

第 八 章 绘 图 ......................................................................................................................... 189<br />

第 九 章 数 据 类 型 ................................................................................................................. 189<br />

第 十 章 如 何 用 <strong>Prolog</strong> 解 决 问 题 ........................................................................................ 190<br />

第 十 一 章 事 实 ..................................................................................................................... 190<br />

第 十 二 章 class 和 object ..................................................................................................... 191


第 十 三 章 乌 龟 爬 和 语 言 处 理 ............................................................................................. 191<br />

第 十 四 章 L- 系 统 ................................................................................................................. 192<br />

第 十 五 章 游 戏 ..................................................................................................................... 192<br />

第 十 六 章 几 个 有 趣 的 部 件 ................................................................................................. 192<br />

第 十 七 章 动 画 ..................................................................................................................... 193<br />

第 十 八 章 相 关 资 料 介 绍 、 索 引 ......................................................................................... 193


正 文 --------------------------------------<br />

自 序<br />

本 书 是 为 <strong>Visual</strong> <strong>Prolog</strong> 7 以 上 版 本 的 编 程 爱 好 者 而 写 。 我 们 不 谈 大 而 空 的 理 论 , 所 以 您 不 用<br />

担 心 晦 涩 难 懂 ; 我 们 也 不 设 立 好 高 骛 远 的 目 标 , 所 以 您 也 不 用 担 心 看 不 懂 。 所 以 它 完 全 是 是<br />

适 用 于 自 学 的 : 一 个 人 静 静 地 摸 索 。 让 纷 扰 的 世 界 去 它 的 吧 , 把 心 沉 浸 在 一 个 循 序 渐 进 的 创<br />

建 、 享 受 的 过 程 中 吧 。<br />

所 以 本 书 还 适 合 那 些 不 喜 欢 纷 扰 、 喜 欢 独 立 思 考 的 人 们 。<br />

您 可 以 在 www.visual-prolog.com 网 站 上 下 载 一 个 免 费 <strong>Visual</strong> <strong>Prolog</strong> 7 的 版 本 , 然 后 一 切 都<br />

可 以 进 行 下 去 了 。<br />

我 们 拿 一 个 个 例 子 , 来 引 导 您 走 入 <strong>Visual</strong> <strong>Prolog</strong> 殿 堂 , 来 消 除 您 对 一 个 陌 生 语 言 <strong>Visual</strong> <strong>Prolog</strong><br />

的 隔 膜 和 恐 惧 。<br />

我 坚 信 , 逻 辑 语 言 适 合 中 国 人 掌 握 , 因 为 我 们 有 庞 大 的 人 群 , 因 为 我 们 有 五 千 年 的 文 化 , 因<br />

为 我 们 有 庞 大 的 应 用 市 场 。 未 来 是 知 识 的 时 代 , 知 识 处 理 会 是 很 重 要 的 事 , 我 们 不 能 袖 手 旁<br />

观 !<br />

我 知 道 大 学 的 计 算 机 教 育 还 把 <strong>Visual</strong> <strong>Prolog</strong> 5 作 为 实 习 的 平 台 , 然 而 它 虽 然 好 , 但 是 已 经 不<br />

符 合 现 代 化 的 编 程 理 念 了 , 但 是 <strong>Visual</strong> <strong>Prolog</strong> 7 不 同 , 它 的 理 念 是 新 的 , 是 符 合 潮 流 的 ,class<br />

和 objects, 在 这 里 一 样 通 行 ; 而 开 放 的 结 构 让 它 有 很 强 的 包 容 性 。 你 不 会 浪 费 时 间 。<br />

每 个 中 国 的 青 年 和 知 识 壮 年 , 都 该 学 习 学 习 <strong>Visual</strong> <strong>Prolog</strong> 7。 需 要 更 多 信 息 , 也 可 以 访 问<br />

www.visual-prolog.cn , 我 们 会 在 这 里 不 是 更 新 信 息 , 给 <strong>Visual</strong> <strong>Prolog</strong> 爱 好 者 一 个 聚 会 的 交<br />

流 的 平 台 。<br />

2009-1-4 于 北 京<br />

第 一 章 简 介<br />

首 先 我 们 要 下 载 安 装 <strong>Visual</strong> <strong>Prolog</strong> 7.1 免 费 个 人 版 软 件 , 可 以 正 常 启 动 , 本 书 内 容 建 立 在 这<br />

个 软 件 基 础 上 。<br />

第 1.1 节 创 建 一 个 新 的 GUI 程 序<br />

我 们 开 始 创 建 一 个 新 的 程 序 , 如 图 1.1.1 所 示 , 我 们 点 击 菜 单 项 Project|New,


图 1.1.1 这 是 起 点<br />

然 后 , 根 据 提 示 , 我 们 创 建 自 己 的 GUI 程 序 myFirst:<br />

图 1.1.2 简 单 填 一 个 名 字 就 可 以 开 始 写 你 自 己 的 程 序 了<br />

然 后 ,IDE 编 程 环 境 进 入 到 :<br />

图 1.1.3 创 建 了 第 一 个 程 序<br />

这 样 就 完 成 了 “myFisrt” 程 序 的 创 建 , 基 本 的 框 架 已 经 有 程 序 设 计 环 境 为 你 自 动 生 成 了 。<br />

第 1.2 节 编 译 和 运 行 你 的 程 序<br />

在 编 程 环 境 下 , 点 击 菜 单 项 Build||build, 就 可 以 编 译 你 的 程 序 , 如 果 要 执 行 程 序 , 则 点 击<br />

Build|Excute。 编 译 时 状 态 行 显 示 编 译 进 程 信 息 :


图 1.2.1 编 译 时 状 态 行 显 示 编 译 进 程 , 要 耐 心 等 一 等<br />

如 果 要 执 行 的 程 序 还 没 有 编 译 过 , 则 , 点 击 Excute 菜 单 项 , 如 图 1.2.1 所 示 , 则 可 以 完 成 从<br />

编 译 到 执 行 的 全 部 过 程 。<br />

图 1.2.2 执 行 程 序 的 菜 单 操 作 , 也 可 以 用 工 具 栏 的 “E” 来 替 代 , 经 常 用 到<br />

我 们 可 以 看 到 , 我 们 刚 刚 建 立 的 程 序 , 运 行 了 起 来 , 如 图 1.2.3 所 示 。<br />

图 1.2.3 我 的 第 一 个 GUI 程 序 myFirst, 也 很 不 错 吧 ? 但 是 花 不 了 5 分 钟 啊 !<br />

可 以 看 到 , 第 一 个 程 序 虽 然 是 空 的 , 但 是 , 却 样 样 齐 全 : 有 主 窗 口 、 信 息 窗 口 、 菜 单 系 统 、<br />

工 具 栏 和 状 态 行 等 等 基 本 的 元 素 。


第 1.3 节 玩 一 把 : 把 程 序 的 显 示 标 题 改 成 我 的 名 字<br />

为 了 增 加 乐 趣 , 我 们 可 以 考 虑 修 改 myFirst 为 我 们 自 己 的 名 字 。 比 如 如 果 我 叫 “ 汀 郁 桂 霞 ”,<br />

我 们 让 它 显 示 这 个 , 如 何 ? 首 先 我 们 关 闭 运 行 的 程 序 , 然 后 返 回 编 程 环 境 , 看 到 项 目 文 件 树<br />

如 图 1.3.1 所 示 , 发 生 了 变 化 :<br />

图 1.3.1 编 译 后 的 文 件 树<br />

多 出 了 一 些 文 件 。 我 们 点 击 TaskWindow 目 录 , 找 到 TaskWindow.win, 并 点 击 鼠 标 右 键 , 看<br />

到 弹 出 菜 单 , 如 图 1.3.2 所 示 。<br />

图 1.3.2 找 到 主 窗 口 进 行 编 辑<br />

我 们 选 择 Edit 项 目 , 进 行 编 辑 , 首 先 看 到 :


图 1.3.3 修 改 标 题 名 字 成 为 我 的 名 字<br />

我 们 把 Title 项 目 目 前 的 myFirst 修 改 为 “ 汀 郁 桂 霞 ”, 然 后 再 执 行 一 下 程 序 , 可 以 看 到 , 程<br />

序 显 示 的 标 题 已 经 发 生 了 改 变 , 如 图 1.3.4 所 示 。 你 可 以 尝 试 改 成 你 自 己 的 名 字 , 增 加 乐 趣 。<br />

学 习 <strong>Visual</strong> <strong>Prolog</strong> 是 一 个 充 满 乐 趣 的 事 情 , 不 要 忘 记 了 啊 。<br />

图 1.3.4 真 的 修 改 成 我 的 名 字 了 , 很 神 奇 吧 ?!<br />

第 1.4 节 小 结 和 说 明<br />

这 里 我 们 建 立 了 第 一 个 程 序 , 在 目 录 myFirst 里 。 目 前 这 里 包 含 的 文 件 和 目 录 如 图 1.4.1 所<br />

示 。<br />

图 1.4.1 第 一 个 程 序 的 文 件 和 目 录 列 表


我 们 以 后 将 在 这 个 基 础 上 , 进 行 功 能 添 加 。 这 是 创 建 一 个 程 序 的 开 端 。<br />

第 1.5 节 逻 辑 小 知 识 : 希 腊 人 发 明 了 逻 辑<br />

古 希 腊 人 相 信 这 样 的 信 条 :<br />

Our constitution does not copy the laws of neighboring states; we are rather a model to<br />

others than imitators ourselves. Its administration favors the many instead of the few; this<br />

is why it is called a democracy. If we look to the laws, they afford equal justice to all in their<br />

private differences; if no social standing, advancement in public life falls to reputation for<br />

capacity, class considerations not being allowed to interfere with merit; nor again does<br />

poverty bar the way: If a man is able to serve the state, he is not hindered by the obscurity<br />

of his condition from proposing laws, and political actions.<br />

在 这 样 一 个 社 会 里 , 一 个 人 要 想 有 所 作 为 , 想 有 所 创 新 , 必 须 首 先 说 服 大 家 。 一 个 人 的 思 想<br />

一 旦 为 大 多 数 人 接 受 , 就 可 以 成 为 法 律 或 者 大 家 普 遍 遵 从 的 规 则 。 大 家 行 事 只 能 按 照 惯 例 、<br />

英 雄 们 的 做 法 来 做 。<br />

聪 明 的 古 希 腊 哲 人 们 为 了 说 服 别 人 , 发 明 了 逻 辑 方 法 、 诡 辩 术 、 聚 众 心 理 学 、 说 谎 的 艺 术 等<br />

等 技 巧 , 来 试 图 说 服 大 众 , 证 明 自 己 的 正 确 。<br />

而 <strong>Visual</strong> <strong>Prolog</strong> 遵 从 的 逻 辑 学 方 法 , 正 式 这 样 一 个 古 老 的 方 法 的 一 种 应 用 : 用 现 存 的 正 确 命<br />

题 , 经 过 不 断 的 匹 配 和 验 证 , 证 明 一 个 新 命 题 的 正 确 。<br />

第 二 章 Forms<br />

上 一 章 , 我 们 学 习 了 建 立 一 个 空 的 程 序 项 目 。 这 一 章 , 我 们 学 习 怎 么 在 这 个 基 础 上 , 为 项 目<br />

增 加 一 个 Form。Form 是 窗 口 界 面 的 基 本 元 素 , 它 也 是 一 个 窗 口 , 相 当 于 一 个 控 制 面 板 。 在<br />

这 个 面 板 上 , 我 们 可 以 增 加 一 些 控 件 : 比 如 静 态 文 本 、 文 本 输 入 框 、 编 辑 框 、 按 钮 、 滑 块 、<br />

列 表 框 等 等 , 从 而 实 现 对 用 户 事 件 的 反 应 。 我 们 甚 至 可 以 在 这 个 面 板 上 绘 图 。<br />

第 2.1 节 创 建 一 个 新 的 Form<br />

启 动 编 程 环 境 , 打 开 已 有 的 Project 名 字 “myFirst”。 在 myFirst 项 目 基 础 上 , 点 击 菜 单 项<br />

File|New in new Package, 如 图 2.1.1 所 示 。


图 2.1.1 击 菜 单 项 File|New in new Package, 创 建 一 个 新 的 Form<br />

我 们 可 以 看 到 , 出 现 “Create Project Item” 对 话 框 , 我 们 在 Name 处 填 写 “query”, 在 左 边<br />

侧 面 一 列 , 选 择 Form 项 目 , 如 图 2.1.2 所 示 。<br />

图 2.1.2 Create Project Item 对 话 框 Name 处 填 写 query, 选 择 Form<br />

然 后 点 击 下 面 的 Create 按 钮 , 于 是 , 一 个 新 的 Form 就 创 建 了 , 在 文 件 树 列 表 里 , 可 以 看 到<br />

多 了 一 些 东 西 , 如 图 2.1.3 所 示 。<br />

图 2.1.3 文 件 树 列 表 多 了 一 些 东 西<br />

并 且 自 动 进 入 Form 编 辑 状 态 如 图 2.1.4 所 示 。<br />

图 2.1.4 自 动 进 入 Form 编 辑 状 态<br />

图 2.1.5 所 示 是 控 件 工 具 箱 。 这 些 控 件 都 是 可 以 放 到 Form 这 个 控 制 面 板 上 的 零 部 件 。 我 们<br />

选 择 需 要 的 , 首 先 点 击 相 应 的 图 标 , 然 后 当 鼠 标 移 动 到 Form 上 方 , 一 个 阴 影 一 样 的 图 标 就<br />

在 光 标 处 , 你 可 以 用 鼠 标 确 定 放 在 什 么 位 置 , 放 多 大 , 这 样 就 完 成 了 一 个 控 件 的 放 置 或 者 安<br />

放 。 复 杂 的 应 用 界 面 , 就 是 这 些 一 个 一 个 标 准 的 按 钮 组 合 起 来 完 成 的 。


图 2.1.5 控 件 工 具 箱<br />

图 2.1.6 所 示 是 控 件 版 面 编 辑 安 置 工 具 箱 。 这 个 工 具 箱 是 帮 助 我 们 把 控 件 在 Form 上 安 放 、<br />

编 排 地 更 合 理 , 更 有 条 不 紊 。 它 包 含 了 左 对 齐 、 竖 中 心 对 齐 、 右 对 齐 、 显 示 网 格 线 、 上 对 齐 、<br />

下 对 齐 、 横 中 心 对 齐 、 等 大 小 、 等 间 距 、 等 高 、 等 宽 等 等 工 具 。 我 们 可 以 选 中 相 应 的 控 件 ,<br />

然 后 使 用 这 些 功 能 , 安 排 控 件 布 局 。<br />

图 2.1.6 控 件 版 面 编 辑 安 置 工 具 箱 。<br />

需 要 说 明 的 是 ,Form 的 控 件 编 辑 是 随 时 可 以 进 行 的 。 现 在 我 们 只 要 创 建 这 个 缺 省 的 query<br />

Form 就 行 了 , 更 多 的 工 作 留 在 以 后 进 行 。 我 们 可 以 点 击 query 的 右 上 角 关 闭 按 钮 , 根 据 提<br />

示 保 存 Form。<br />

第 2.2 节 让 任 务 菜 单 的 项 目 工 作 起 来<br />

我 们 已 经 创 建 了 一 个 From, 它 的 名 字 就 是 query, 如 何 在 程 序 启 动 后 , 我 们 点 击 菜 单 File|new,<br />

就 可 以 把 这 个 界 面 调 出 来 呢 ?<br />

首 先 , 如 图 2.2.1 所 示 , 我 们 在 这 里 找 到 程 序 菜 单 项 目 所 在 文 件 。


图 2.2.1 程 序 菜 单 项 目 所 在 文 件 是 TaskWindow.mnu<br />

然 后 我 们 按 鼠 标 右 键 激 活 菜 单 , 选 择 Edit 来 编 辑 菜 单 , 如 图 2.2.2 所 示 , 我 们 选 择 File|New<br />

项 目 , 把 上 部 的 Disable 选 择 框 的 勾 通 过 鼠 标 点 击 去 掉 , 然 后 关 闭 这 个 对 话 框 , 根 据 提 示 保<br />

存 修 改 。<br />

图 2.2.2 选 择 File|New 项 目 , 鼠 标 点 击 去 掉 Disable 选 择 框 的 勾<br />

第 2.3 节<br />

用 Code Expert 添 加 程 序<br />

然 后 我 们 选 择 TaskWindow.win 文 件 , 点 击 鼠 标 右 键 , 选 择 Code Expert 菜 单 项 , 如 图 2.3.1<br />

所 示 。 这 样 我 们 就 可 进 入 代 码 专 家 界 面 如 图 2.3.2 所 示 。


图 2.3.1 TaskWindow.win 文 件 上 鼠 标 右 键 选 择 Code Expert 菜 单 项<br />

我 们 在 代 码 专 家 对 话 框 里 选 择 File|New 菜 单 项 目 , 可 以 采 取 鼠 标 双 击 或 者 选 中 项 目 后 点 击<br />

右 下 角 的 add 按 钮 , 进 入 代 码 编 写 界 面 , 程 序 自 动 生 成 的 代 码 如 图 2.3.3 所 示 。<br />

图 2.3.2 双 击 或 者 点 击 add 按 钮<br />

我 们 在 自 动 生 成 的 代 码 后 写 出 相 应 的 代 码 。<br />

图 2.3.3 程 序 自 动 生 成 了 一 些 代 码<br />

我 们 把 自 动 生 成 的 代 码 如 下 :


clauses<br />

onFileNew(_Source, _MenuTag).<br />

修 改 为 代 码 如 下 所 示 :<br />

clauses<br />

onFileNew(W, _MenuTag) :- X= query::new(W), X:show().<br />

然 后 , 重 新 编 译 运 行 一 下 程 序 , 注 意 , 当 程 序 提 示 是 否 加 入 query 包 , 要 选 择 add all。 最 后<br />

发 现 , 当 我 们 点 击 File|New 菜 单 项 后 , 程 序 显 示 了 query 对 话 框 , 如 图 2.3.4 所 示 。<br />

图 2.3.4 点 击 File|New 菜 单 项 程 序 显 示 query 对 话 框<br />

第 2.4 节<br />

小 结 和 说 明<br />

本 章 我 们 主 要 学 习 了 怎 么 创 建 一 个 Form, 怎 么 把 任 务 菜 单 项 目 变 得 可 用 , 怎 么 利 用 代 码 专<br />

家 Code Expert 功 能 来 顺 利 的 编 写 代 码 , 最 后 实 现 了 用 菜 单 激 活 一 个 对 话 框 的 基 本 操 作 。 这<br />

是 很 重 要 的 一 步 , 也 是 编 写 程 序 的 最 基 本 操 作 。 希 望 大 家 认 真 融 会 贯 通 。<br />

第 2.5 节 逻 辑 小 知 识 : 亚 里 士 多 德 的 符 号 逻 辑<br />

亚 里 士 多 德 认 为 , 一 个 命 题 由 主 体 和 谓 词 两 部 分 , 外 加 修 饰 词 构 成 。 表 示 程 度 的 修 饰 词 有 :<br />

“ 每 一 个 / 都 ”(every)、“ 没 有 / 绝 不 ”(no)、“ 一 些 / 某 种 程 度 上 ”(some)、“ 并 非 都 ”(not every),<br />

几 种 。 后 来 葡 萄 牙 和 西 班 牙 的 逻 辑 学 家 在 这 个 基 础 上 发 明 了 符 号 逻 辑 表 示 方 法 , 来 简 化 表 述<br />

命 题 :<br />

限 定 符 号 含 义 书 写 方 法 举 例<br />

a Universal affirmative 完 全 肯 定 PaQ Every P is Q<br />

e Universal negative 完 全 否 定 PeQ No P is Q<br />

i Particular affirmative 部 分 肯 定 PiQ Some P is Q<br />

o Particular negative 部 分 否 定 PoQ Some P is not Q<br />

命 题 由 此 被 分 为 不 同 真 的 、 矛 盾 的 、 不 同 假 的 。 下 面 是 他 们 的 解 释 :<br />

a. 两 个 命 题 P 和 Q 是 矛 盾 的 , 当 且 仅 当<br />

i. P 和 Q 不 能 同 时 是 正 确 的 , 并


ii P 或 Q 有 一 个 是 正 确 的 。<br />

类 型 a 和 类 型 o 是 矛 盾 的 , 同 样 类 型 e 和 类 型 i 也 是 矛 盾 的 。<br />

b. 两 个 命 题 P 和 Q 是 相 反 的 , 当 且 仅 当<br />

i. P 和 Q 不 能 同 时 是 真 的 , 并<br />

ii. P 和 Q 可 以 都 是 假 的 。<br />

类 型 a 和 类 型 e 是 不 同 真 的 。<br />

c. P 和 Q 是 不 完 全 相 反 的 , 当 且 仅 当<br />

i. P 和 Q 不 能 同 时 是 假 的 , 并<br />

ii. P 和 Q 都 可 以 是 真 的 。<br />

类 型 i 和 类 型 o 是 不 同 假 的 。<br />

三 段 论 是 三 个 命 题 构 成 的 证 明 过 程 : 两 个 前 提 和 一 个 结 论 , 并 且 出 现 恰 好 三 个 论 述 , 而 且 每<br />

个 论 述 被 精 确 地 运 用 两 次 。<br />

设 P 代 表 结 论 的 预 言 ( 主 论 述 ),S 是 结 论 的 主 体 ( 次 论 述 ),M 代 表 前 提 中 的 共 同 部 分 , 但<br />

M 并 不 出 现 在 结 论 里 , 它 是 中 间 论 述 。 包 含 P 的 前 提 称 为 大 前 提 , 包 含 S 的 前 提 称 为 小 前<br />

提 。<br />

第 三 章 鼠 标 事 件<br />

上 一 章 , 我 们 实 现 了 点 击 File|New 菜 单 项 , 弹 出 一 个 对 话 框 query, 下 面 我 们 增 加 在 这 个 对<br />

话 框 内 , 对 鼠 标 按 下 事 件 的 反 应 。<br />

第 3.1 节 增 加 鼠 标 按 下 事 件 侦 听 器<br />

如 图 3.1.1 所 示 , 我 们 在 目 录 树 里 找 到 query.frm 文 件 , 鼠 标 双 击 它 , 我 们 就 可 以 进 入 这 个 对<br />

话 框 Form 的 编 辑 状 态 。


图 3.1.1 找 到 query.frm 文 件 , 鼠 标 双 击 进 入 Form 编 辑 状 态<br />

在 Form 编 辑 状 态 下 , 我 们 可 以 看 到 一 个 关 于 query 的 特 性 设 置 和 事 件 设 置 对 话 框 如 图 3.1.2<br />

所 示 , 我 们 点 击 下 部 按 钮 Events, 进 入 下 一 页 , 并 选 择 MouseDownLisener 侦 听 器 的 反 应 谓<br />

词 为 onMouseDown。<br />

图 3.1.2 设 置 鼠 标 按 下 事 件 侦 听 器<br />

然 后 鼠 标 双 击 这 个 选 项 , 则 进 入 代 码 编 辑 界 面 。 我 们 把 原 代 码 :<br />

predicates<br />

onMouseDown : drawWindow::mouseDownListener.


clauses<br />

onMouseDown(_Source, _Point, _ShiftControlAlt, _Button).<br />

改 写 成 为 :<br />

predicates<br />

onMouseDown : drawWindow::mouseDownListener.<br />

clauses<br />

onMouseDown(S, Point, _ShiftControlAlt, _Button) :-<br />

W= S:getVPIWindow(), Point= pnt(X, Y),<br />

vpi::drawText(W, X, Y, "Hello, World!").<br />

然 后 重 新 编 译 、 执 行 一 下 程 序 , 看 到 , 每 当 我 们 在 query 对 话 框 上 点 下 鼠 标 , 就 会 写 出 一 段<br />

文 字 “Hello, World!”, 如 图 3.1.3 所 示 , 这 就 表 明 我 们 实 现 了 对 鼠 标 事 件 的 反 应 程 序 。<br />

图 3.1.3 每 当 我 们 在 框 上 点 下 鼠 标 , 就 会 写 出 一 段 文 字 “Hello, World!”<br />

第 3.2 节 onPaint 绘 图<br />

当 然 , 像 上 节 所 述 的 鼠 标 点 击 绘 图 ( 文 字 ) 事 件 , 也 可 以 用 另 外 一 种 方 式 来 实 现 : 即 利 用 重<br />

画 功 能 onPaint 来 实 现 。 具 体 操 作 步 骤 是 , 如 图 3.2.1 所 示 , 点 击 文 件 query.pro, 则 右 边 框<br />

内 会 出 现 该 文 件 所 包 含 的 谓 词 名 称 列 表 , 找 到 onMouseDown/4 并 双 击 , 则 转 到 代 码 的 相 应<br />

处 。


图 3.2.1 找 到 鼠 标 按 下 事 件 处 理 谓 词 的 代 码 所 在 处<br />

进 入 代 码 编 辑 器 后 , 我 们 把 上 节 编 写 的 代 码 前 后 夹 在 “/*”、“*/” 之 间 , 使 之 变 成 说 明 文 字 ,<br />

然 后 添 加 我 们 编 写 的 新 的 鼠 标 按 下 事 件 处 理 代 码 , 最 后 代 码 部 分 变 成 如 下 所 示 :<br />

/*<br />

predicates<br />

onMouseDown : drawWindow::mouseDownListener.<br />

clauses<br />

onMouseDown(S, Point, _ShiftControlAlt, _Button) :-<br />

W= S:getVPIWindow(), Point= pnt(X, Y),<br />

vpi::drawText(W, X, Y, "Hello, World!").<br />

*/<br />

class facts<br />

mousePoint:pnt := pnt(-1, -1).<br />

predicates<br />

onMouseDown : drawWindow::mouseDownListener.<br />

clauses<br />

onMouseDown(_S, Point, _ShiftControlAlt, _Button) :-<br />

mousePoint := Point,<br />

Point= pnt(X, Y),<br />

R= rct(X-8, Y-8, X+60, Y+8),<br />

invalidate(R).<br />

注 意 到 这 里 我 们 用 到 数 据 库 记 录 mousePoint 来 保 存 鼠 标 点 击 时 的 坐 标 数 值 , 它 的 类 型 是 pnt,<br />

即 坐 标 结 构 ; 并 把 初 始 值 设 为 pnt(-1,-1)。 这 里 还 用 到 invalidate(R), 这 是 一 个 强 迫 启 动 重 画


R 所 指 的 矩 形 区 域 的 谓 词 。R= rct(X-8, Y-8, X+60, Y+8) 表 示 重 画 的 矩 形 的 左 上 角 和 右 下 角 。<br />

注 意 X 代 表 水 平 方 向 , 左 到 右 , 数 值 增 加 ;Y 代 表 垂 直 方 向 , 上 到 下 , 数 值 增 加 。 它 触 发<br />

重 画 事 件 , 所 以 我 们 需 要 增 加 重 画 事 件 处 理 程 序 。 如 图 3.2.2 所 示 , 增 加 paintResponder 为<br />

onPaint。<br />

图 3.2.2 增 加 paintResponder 为 onPaint<br />

编 写 代 码 如 下 :<br />

predicates<br />

onPaint : drawWindow::paintResponder.<br />

clauses<br />

onPaint(_Source, _Rectangle, GDIObject) :-<br />

if pnt(X, Y)= mousePoint, X>0<br />

then<br />

mousePoint := pnt(-1, -1),<br />

GDIObject:drawText(pnt(X, Y), "Hello, World!", -1)<br />

else succeed() end if.<br />

同 样 实 现 了 这 样 的 功 能 。<br />

第 3.3 节 小 结 和 说 明<br />

这 一 章 我 们 学 习 了 如 何 处 理 一 个 事 件 , 鼠 标 事 件 侦 听 器 。 实 际 上 , 鼠 标 事 件 可 以 区 分 出 鼠 标<br />

是 否 在 移 动 、 按 下 左 键 还 是 右 键 、 单 击 还 是 双 击 、 按 下 还 是 弹 起 、 是 否 按 了 中 间 键 或 者 滑 轮 ?<br />

这 样 来 做 出 不 同 的 反 应 。 在 事 件 列 表 里 , 我 们 可 以 看 到 分 成 四 类 如 图 3.3.1 所 示 。<br />

图 3.3.1 鼠 标 事 件 分 四 类<br />

就 按 下 事 件 来 讲 , 鼠 标 按 下 侦 听 器 的 参 数 定 义 如 下 :<br />

drawWindow::mouseDownListener


mouseDownListener = (<br />

drawWindow Source,<br />

vpiDomains::pnt Point,<br />

vpiDomains::keyModifier ShiftControlAlt,<br />

integer Button) .<br />

它 可 以 返 回 所 在 绘 图 窗 口 、 所 在 位 置 、 是 否 按 了 Shift、Control、Alt 等 及 其 联 合 的 修 饰 键 、<br />

以 及 是 那 个 键 发 生 的 事 件 。 这 样 便 于 控 制 。 这 些 细 节 要 查 阅 具 体 的 定 义 。<br />

第 3.4 节 逻 辑 小 知 识 : 布 尔 代 数<br />

1, 符 号 “「” 表 示 逻 辑 上 “ 非 ”。「p 表 示 : 当 p 为 真 , 则 「p 为 假 ; 当 p 为 假 , 则 「p 为<br />

真 。 当 为 p 公 式 , 则 「p 也 是 公 式 。<br />

2, 符 号 “∨” 表 示 逻 辑 上 “ 或 ”。p∨q 表 示 ,p 或 者 q。 它 的 含 义 是 :p 或 者 q 任 何 一 个 为<br />

真 ,p∨q 就 为 真 ;p、q 都 为 假 ,p∨q 才 为 假 。<br />

3, 符 号 “∧” 表 示 逻 辑 上 “ 与 ”。 p∧q 表 示 ,p 与 q。 它 的 含 义 是 :p、q 任 何 一 个 为 假 ,p<br />

∧q 为 假 ;p、q 都 为 真 ,p∧q 才 为 真 。<br />

4,p→q 表 示 逻 辑 上 的 “ 推 定 ”。 由 p 可 以 推 定 q。 它 的 含 义 是 :p 为 真 , 则 q 为 真 。<br />

5,p≡q 表 示 逻 辑 上 的 “ 相 当 ”。 p 相 当 于 q。 它 的 含 义 是 :p 为 真 , 则 q 为 真 ; 同 样 ,q 为<br />

真 , 则 p 为 真 。<br />

通 常 我 们 用 数 字 “1” 表 示 “ 真 ”, 而 数 字 “0” 表 示 “ 假 ”。 这 样 我 们 可 以 用 真 值 表 来 说 明 这<br />

几 个 逻 辑 公 式 的 含 义 如 下 :<br />

p q 「p p∧q p∨q p→q 「p∨q<br />

0 0 1 0 0 1 1<br />

0 1 1 0 1 1 1<br />

1 0 0 0 1 0 0<br />

1 1 0 1 1 1 1<br />

从 这 里 我 们 可 以 看 出 , 很 容 易 证 明 ,p→q 与 「p∨q 是 完 全 相 当 的 , 即 :<br />

(p→q)≡(「p∨q)<br />

第 3.5 节 论 证 的 形 式<br />

我 们 把 一 个 论 证 写 作 :<br />

p∨q<br />

「p<br />

├q<br />

这 一 段 论 证 说 : 因 为 p∨q 是 真 , 而 且 p 为 假 , 则 可 以 插 入 事 实 q 为 真 。 符 号 “├” 是 插 入


的 意 思 。<br />

在 <strong>Prolog</strong> 里 , 我 们 的 证 明 方 法 是 这 样 的 :<br />

q←p<br />

p<br />

├q<br />

这 就 是 说 ,p 推 定 q,p 为 真 , 则 插 入 q 为 真 。 这 就 是 <strong>Prolog</strong> 的 证 明 过 程 。<br />

第 四 章 基 本 图 例<br />

这 一 章 , 我 们 通 过 建 立 一 个 工 程 factorial 来 解 释 说 明 一 下 , 整 个 VIP 编 程 环 境 IDE 的 关 键<br />

部 件 和 基 本 图 例 , 在 以 后 的 章 节 里 , 就 不 再 重 复 了 。<br />

第 4.1 节 任 务 菜 单<br />

如 图 1.1.1 所 示 , 我 们 点 击 主 菜 单 Project|New 菜 单 项 , 就 可 以 新 建 一 个 软 件 工 程 (Project)<br />

了 。 主 菜 单 , 就 是 IDE 的 一 个 主 要 组 成 部 分 , 如 图 4.1.1 所 示 , 有 很 多 项 目 。<br />

图 4.1.1 IDE 的 主 菜 单 有 14 个 项 目<br />

“A|B” 的 描 述 , 是 我 们 对 菜 单 项 目 点 击 顺 序 的 一 种 描 述 , 表 示 先 点 击 菜 单 项 A 然 后 点 击 下<br />

一 层 的 B。 我 们 新 建 项 目 factorial 要 填 写 的 项 目 如 下 :<br />

General<br />

Project Name: factorial<br />

UI Strategy: Object-oriented GUI(pfc/gui)<br />

Target Type: Exe<br />

Base Directory: C:\vispro<br />

注 意 , 我 们 需 要 在 <strong>Prolog</strong> Setting 对 话 框 下 , 对 这 几 个 项 目 做 相 应 的 填 写 或 者 修 改 , 如 图 4.1.2<br />

所 示 。 在 这 里 , 我 们 故 意 把 存 取 我 们 这 个 程 序 文 件 的 目 录 设 置 在 C 盘 的 vispro 之 下 , 其 实<br />

我 们 可 以 根 据 自 己 需 要 进 行 设 置 。 然 后 点 击 OK, 就 可 以 创 建 我 们 自 己 的 工 程 factorial 图 形<br />

界 面 程 序 了 , 如 图 4.1.3 所 示 。


图 4.1.2 填 写 并 更 改 所 存 目 录<br />

图 4.1.3 创 建 factorial 工 程<br />

第 4.2 节 目 录 树<br />

我 们 看 到 , 新 建 的 工 程 的 目 录 树 如 图 4.2.1 所 示 。


图 4.2.1 文 件 目 录 树<br />

这 些 目 录 双 击 可 以 打 开 , 看 到 其 中 有 些 什 么 文 件 ; 而 其 中 的 文 件 , 双 击 , 就 可 以 进 入 相 应 的<br />

编 辑 状 态 。 在 文 件 项 目 上 点 击 鼠 标 右 键 , 则 一 般 会 弹 出 相 应 的 弹 出 菜 单 。 如 果 和 上 图 一 样 的<br />

操 作 , 我 们 的 描 述 就 是 :<br />

用 Code Expert 为 TaskWindow/TaskWindow.win 添 加 代 码<br />

这 个 指 令 描 述 , 包 含 以 下 步 骤 :<br />

(1) 双 击 鼠 标 打 开 目 录 TaskWindow<br />

(2) 找 到 文 件 TaskWindow.win<br />

(3) 在 这 个 文 件 上 按 鼠 标 右 键 , 弹 出 菜 单<br />

(4) 在 弹 出 菜 单 上 , 选 择 Code Expert 菜 单 项<br />

第 4.2.1 代 码 专 家<br />

代 码 专 家 也 有 一 个 树 形 的 对 话 框 , 让 你 来 选 择 , 具 体 增 加 的 编 码 内 容 。 就 之 前 对<br />

TaskWindow.win 代 码 编 写 来 讲 , 当 我 们 启 用 了 代 码 专 家 Code Expert 项 目 后 , 进 入 一 个 对 话<br />

框 , 如 图 4.2.2 所 示 , 为 了 对 菜 单 项 目 File|New 进 行 编 码 , 我 们 展 开 树 形 结 构 , 找 到 New 项<br />

目 , 双 击 它 就 可 以 进 行 代 码 编 写 。


图 4.2.2 Code Expert 通 过 树 形 结 构 来 选 择 具 体 编 码 的 项 目<br />

在 前 面 我 们 谈 到 的 Form 中 ,Code Expert 的 实 现 只 需 要 选 择 Events 的 项 目 , 进 行 双 击 , 就<br />

可 以 实 现 其 功 能 。 所 以 Code Expert 在 不 同 的 地 方 , 有 不 同 的 启 动 方 法 , 方 便 我 们 进 行 代 码<br />

设 计 。 在 factorial 里 , 我 们 依 然 要 设 计 一 个 Form, 名 字 也 叫 query, 这 样 , 我 们 用 File|New<br />

来 启 动 这 个 对 话 框 。 这 样 增 加 代 码 如 下 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(W, _MenuTag) :- S= query::new(W), S:show().<br />

而 整 个 操 作 可 以 描 述 为 一 个 简 写 的 动 作 :<br />

TaskWindow.win/CodeExpert/Menu/TaskMenu/id_file/id_file_new,<br />

而 这 代 表 着 以 下 几 个 动 作 :<br />

(1) 树 形 目 录 操 作 : 找 到 TaskWindow.win 然 后 右 键 启 动 Code Expert<br />

(2) 代 码 专 家 操 作 : 进 入 树 形 结 构 点 击 Menu, 然 后 TaskMenu, 然 后 id_file, 然 后 id_file_new<br />

项 目 , 双 击 进 行 代 码 编 写 。<br />

第 4.3 节 创 建 一 个 工 程 项 目<br />

我 们 可 以 创 建 新 的 工 程 项 目 : 比 如 新 的 代 码 包 (Package)、 新 的 模 块 (class)、 新 的 对 话 框 表<br />

(Form) 等 等 。 我 们 这 里 要 在 新 的 包 里 创 建 一 个 名 字 叫 query 的 Form, 首 先 进 行 如 下 在 操<br />

作 :<br />

File/New in New Package<br />

我 们 创 建 一 个 程 序 包 , 显 示 的 是 一 个 独 立 的 目 录 , 名 字 叫 做 “FormContainer”, 结 果 得 到 如


图 4.3.1 所 示 。<br />

图 4.3.1 新 的 程 序 包 FormContainer<br />

然 后 进 行 如 下 操 作 :<br />

File/New in Existing Package<br />

选 择 FormContainer 程 序 包 , 创 建 Form 名 字 为 query, 如 图 4.3.2 所 示 。<br />

图 4.3.2 选 择 Form 所 在 的 程 序 包 , 可 以 看 到 可 以 用 下 拉 列 表 选 择<br />

这 样 就 进 入 Form 的 编 辑 过 程 。 我 们 希 望 设 计 query 如 图 4.3.3 所 示 。<br />

图 4.3.3 对 话 框 设 计


我 们 设 计 一 个 编 辑 控 件 , 用 来 接 收 用 户 输 入 , 而 factorial 按 钮 , 用 来 发 出 计 算 输 入 数 字 的 阶<br />

乘 的 指 令 。<br />

第 4.4 节 创 建 一 个 新 的 class<br />

我 们 要 新 增 加 一 个 模 块 (class), 名 字 fn, 来 实 现 按 钮 factorial 按 下 的 计 算 功 能 。 如 图 :<br />

图 4.4.1 在 formContainer 里 增 加 class fn<br />

在 文 件 fn.cl 里 编 写 如 下 代 码 实 际 上 只 有 “calculate:(string) procedure.” 是 新 增 加 的 :<br />

class fn : fn<br />

open core<br />

predicates<br />

classInfo : core::classInfo.<br />

calculate:(string) procedure.<br />

end class fn<br />

在 fn.pro 里 编 写 如 下 代 码 :<br />

implement fn<br />

open core<br />

clauses<br />

classInfo("forms/fn", "1.0").<br />

class predicates<br />

fact:(integer, integer) procedure (i,o).<br />

clauses<br />

fact(0, 1) :- !.


fact(N, N*F) :-<br />

fact(N-1, F).<br />

calculate(X) :-<br />

N= toterm(X),<br />

fact(N, F),<br />

stdio::write(F, "\n").<br />

end implement fn<br />

第 4.5 节 设 置 控 件 属 性<br />

我 们 设 置 控 件 属 性 ,edit_ctrl 开 始 显 示 空 的 ,factorial 按 下 事 件 反 应 调 用 fn 的 calculate 来 计<br />

算 结 果 。<br />

图 4.5.1 编 辑 控 件 开 始 显 示 空 的 , 等 待 输 入<br />

在 编 写 factorial 按 钮 事 件 代 码 之 前 必 须 build 一 下 程 序 , 让 我 们 增 加 的 东 西 进 入 程 序 的 工 程<br />

管 理 库 里 。 不 然 , 我 们 在 factorial 按 钮 里 看 不 到 事 件 列 表 。 我 们 如 图 4.5.2 选 择 事 件 的 Code<br />

Expert 功 能 , 进 入 代 码 编 写 状 态 。<br />

图 4.5.2 选 择 按 钮 事 件 的 反 应 器<br />

编 写 如 下 代 码 , 就 把 按 钮 和 模 块 fn 的 功 能 连 接 在 一 起 。 编 写 代 码 之 前 :<br />

predicates<br />

onFactorialClick : button::clickResponder.<br />

clauses


onFactorialClick(_Source) = button::defaultAction.<br />

编 写 代 码 之 后 :<br />

predicates<br />

onFactorialClick : button::clickResponder.<br />

clauses<br />

onFactorialClick(_S) = button::defaultAction() :-<br />

fn::calculate(edit_ctl:getText()).<br />

运 行 整 个 程 序 。 看 到 , 当 我 们 输 入 4, 按 factorial 按 钮 , 在 Message 里 显 示 24。 这 就 是 4!<br />

=4×3×2×1 的 结 果 , 如 图 4.5.3 所 示 。<br />

图 4.5.3 输 入 4, 输 出 结 果 24, 算 出 了 阶 乘 得 数<br />

第 4.6 节<br />

小 结 和 说 明<br />

本 章 我 们 介 绍 了 一 些 基 本 的 编 程 、 程 序 设 计 的 叙 述 约 定 , 及 IDE 的 一 些 基 本 元 素 , 这<br />

样 可 以 使 我 们 以 后 的 叙 述 更 加 简 练 , 而 不 需 要 太 多 的 图 片 来 描 述 。 我 们 介 绍 了 程 序 包 、 模 块<br />

的 增 加 方 法 , 实 现 了 一 个 完 整 的 应 用 factorial 的 计 算 。 介 绍 了 Code Expert 怎 样 帮 助 我 们 设<br />

计 代 码 。<br />

第 4.7 节 逻 辑 小 知 识 : 谓 词 运 算<br />

为 了 逻 辑 运 算 , 逐 步 发 展 了 谓 词 运 算 , 如 下 表 示 :<br />

这 样 , 逻 辑 叙 述 可 以 用 谓 词 运 算 来 描 述 了 , 这 是 <strong>Prolog</strong> 的 逻 辑 运 算 基 础 。


第 五 章 Horn 语 句<br />

第 5.1 节 函 数<br />

函 数 , 是 一 个 函 数 关 系 和 一 些 参 数 构 成 的 运 算 关 系 。 比 如 sin(X), 函 数 关 系 是 sin, 求 正 弦 ,<br />

而 X 是 参 数 。 再 如 , mod(A,B), 是 描 述 A 与 B 的 模 数 关 系 。 当 我 们 描 述 函 数 时 , 总 是 可<br />

以 用 常 数 值 替 代 参 数 或 变 量 , 从 而 获 得 相 应 的 函 数 计 算 结 果 。<br />

函 数 的 参 数 或 变 量 的 可 能 值 定 义 范 围 , 称 为 域 (domain)。<br />

有 些 函 数 的 函 子 ( 或 函 数 关 系 ) 写 在 函 数 参 数 之 间 。 比 如 5+3, 和 +(5,3) 是 一 样 的 。<br />

第 5.2 节 谓 词<br />

谓 词 是 函 数 的 一 种 特 殊 形 式 , 它 是 特 指 那 些 值 只 可 能 为 “ 真 ” 或 “ 假 ” 的 特 殊 函 数 。<br />

X > Y, 在 X 比 Y 大 时 为 真 , 其 他 情 况 为 假<br />

X < Y, 在 X 比 Y 小 时 为 真 , 其 他 情 况 为 假<br />

X = Y, 在 X 与 Y 相 等 时 为 真 , 其 他 情 况 为 假<br />

这 里 >、


city("Yellowstone", pnt(200, 100)).<br />

那 么 这 个 谓 词 , 就 可 以 用 来 检 验 , 给 定 的 城 市 的 坐 标 位 置 与 已 知 的 ( 数 据 库 里 预 存 的 ) 信 息<br />

是 否 吻 合 , 也 可 以 用 来 找 到 指 定 城 市 的 坐 标 , 也 可 以 用 来 根 据 某 个 坐 标 , 寻 找 这 个 城 市 的 名<br />

称 是 什 么 。<br />

city("Salt Lake", pnt(30, 40)) → true<br />

city("Logan", pnt(100, 200)) → false<br />

city("Provo", pnt(100, 200)) → true<br />

city("Salt Lake", P) → P= pnt(30, 40)<br />

我 们 通 常 约 定 大 写 字 母 开 头 的 符 号 变 量 。 比 如 :<br />

X、Y、Wh、Who、B、A、Xs、Temperature、Humidity、Rate<br />

Project Settings. 在 VDE 主 菜 单 下 点 击 Project/New 进 入 Project Settings 对 话 框<br />

General<br />

Project Name: mapDataBase<br />

UI Strategy: Object-oriented (pfc/GUI)<br />

Target Type: Exe<br />

Base Directory: C:\vispro<br />

Sub-Directory: mapDataBase\<br />

创 建 Project Item:Form。 我 们 在 目 录 树 的 根 目 录 下 进 行 如 下 操 作 File/New in New Package,<br />

选 择 Form, 名 字 为 “map”。 在 这 个 面 板 上 , 设 置 三 个 按 钮 Logan、Salt Lake、Provo, 回 头<br />

用 于 显 示 这 些 城 市 。 编 辑 窗 口 大 小 , 留 出 画 图 的 足 够 空 间 。<br />

Build/Build 在 主 菜 单 下 点 击 Build|Build 菜 单 项 , 以 便 下 一 步 操 作 , 否 则 会 出 错 。<br />

Project Tree/TaskMenu.mnu 使 菜 单 项 File/New 可 以 操 作 。<br />

Project Tree/TaskWindow.win/Code Expert 参 照 Menu/TaskMenu/id_file/id_file_new/<br />

onFileNew 指 令 增 加 代 码 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

X= map::new(S), X:show().<br />

这 是 为 File|New 菜 单 项 编 写 代 码 的 。 然 后 在 进 行 Buile|Build 操 作 。<br />

增 加 draw 模 块 class File/New in New Package 指 令 , 选 择 class 增 加 draw。 在 draw.cl 文 件


里 增 加 代 码 后 如 下 :<br />

class draw : draw<br />

open core, vpiDomains<br />

predicates<br />

classInfo : core::classInfo.<br />

drawThem:(windowHandle, string) procedure.<br />

end class draw<br />

在 文 件 draw.pro 里 , 增 加 代 码 后 如 下 所 示 :<br />

implement draw<br />

open core, vpiDomains, vpi<br />

constants<br />

className = "draw".<br />

classVersion = "".<br />

class facts<br />

city:(string, pnt).<br />

clauses<br />

classInfo(className, classVersion).<br />

clauses<br />

city("Salt Lake", pnt(30, 40)).<br />

city("Logan", pnt(100, 120)).<br />

city("Provo", pnt(100, 200)).<br />

city("Yellowstone", pnt(200, 100)).<br />

drawThem(Win, Name) :-<br />

B= brush(pat_solid, color_red),<br />

winSetBrush(Win, B),<br />

city(Name, P),<br />

!,<br />

P= pnt(X1, Y1),<br />

X2= X1+20, Y2= Y1+20,<br />

drawEllipse(Win, rct(X1, Y1, X2, Y2)).<br />

drawThem(_Win, _Name).<br />

end implement draw


然 后 , 编 辑 map 对 话 框 ,map.frm/ Edit /Button Logan/Click Responder : onLoganClick 增 加 按 钮<br />

Logan 的 按 下 事 件 , 增 加 如 下 代 码 :<br />

predicates<br />

onLoganClick : button::clickResponder.<br />

clauses<br />

onLoganClick(S) = button::defaultAction() :-<br />

Parent= S:getParent(),<br />

P= Parent:getVPIWindow(),<br />

draw::drawThem(P, "Logan").<br />

运 行 一 下 , 点 击 Logan 按 钮 , 如 图 5.3.1 所 示 , 得 到 :<br />

图 5.3.1 按 钮 Logan 按 下 , 画 出 城 市 坐 标 位 置 示 意 图<br />

其 他 几 个 城 市 的 按 钮 以 可 以 如 法 炮 制 。 如 图 5.3.2 所 示 , 我 们 画 出 了 三 个 城 市 的 坐 标 位 置 。<br />

图 5.3.2 按 下 三 个 摁 钮 , 画 出 三 个 城 市 的 坐 标 位 置 示 意 图<br />

他 们 的 代 码 是 :<br />

predicates<br />

onSaltLakeClick : button::clickResponder.


clauses<br />

onSaltLakeClick(S) = button::defaultAction() :-<br />

Parent= S:getParent(),<br />

P= Parent:getVPIWindow(),<br />

draw::drawThem(P, "Salt Lake").<br />

predicates<br />

onProvoClick : button::clickResponder.<br />

clauses<br />

onProvoClick(S) = button::defaultAction() :-<br />

Parent= S:getParent(),<br />

P= Parent:getVPIWindow(),<br />

draw::drawThem(P, "Provo").<br />

第 5.4 节 答 案 不 惟 一<br />

上 面 我 们 遇 到 的 谓 词 city/2 是 只 有 一 个 解 的 情 况 , 实 际 上 , 我 们 要 设 计 一 个 conn/2, 来 表 示<br />

两 个 城 市 连 接 的 描 述 :<br />

conn(pnt(30, 40), pnt(100, 120)).<br />

conn(pnt(100, 120), pnt(100, 200)).<br />

conn(pnt(30, 40), pnt(200, 100)).<br />

这 样 , 我 们 往 往 遇 到 2 个 以 上 的 答 案 :<br />

conn(pnt(30, 40), W). → W= pnt(100, 120)<br />

→W=pnt(200, 100)<br />

conn(X, Y). →X=pnt(30, 40)/Y=pnt(100, 120)<br />

→ X= pnt(100, 120)/Y=pnt(100, 200)<br />

→X=pnt(30, 40)/Y=pnt(200, 100))<br />

当 我 们 提 出 问 题 :<br />

conn(pnt(30, 40), W)?<br />

那 么 显 然 我 们 可 以 有 两 个 答 案 :W= pnt(100, 120) 或 者 W=pnt(200, 100)。<br />

第 5.4.1 节 一 个 采 用 多 解 谓 词 的 程 序<br />

我 们 利 用 多 解 特 性 , 利 用 conn/2 来 画 出 城 市 连 接 线 , 结 果 如 图 5.4.1 所 示 。


图 5.4.1 画 出 连 接 线 , 利 用 conn/2 的 多 解 特 性<br />

我 们 增 加 按 钮 drawConnections, 并 对 他 进 行 编 码 :<br />

predicates<br />

onDrawConnections : button::clickResponder.<br />

clauses<br />

onDrawConnections(S) = button::defaultAction :-<br />

Parent= S:getParent(),<br />

P= Parent:getVPIWindow(),<br />

draw::drawConnections(P).<br />

在 模 块 draw 的 draw.pro 文 件 里 增 加 :<br />

class facts<br />

conn : (pnt, pnt).<br />

clauses<br />

conn(pnt(30, 40) , pnt(100, 120)).<br />

conn(pnt(100, 120), pnt(100, 200)).<br />

conn(pnt(30, 40), pnt(200, 100)).<br />

drawConnections(Win) :- conn(P1, P2), drawLine(Win, P1, P2), fail.<br />

drawConnections(_Win).<br />

并 修 改 原 来 画 城 市 的 语 句 为 :<br />

drawThem(Win, Name) :-<br />

B= brush(pat_solid, color_red),<br />

winSetBrush(Win, B),<br />

city(Name, P),<br />

!,<br />

P= pnt(X1, Y1),<br />

X2= X1+10, Y2= Y1+10,<br />

drawEllipse(Win, rct(X1-10, Y1-10, X2, Y2)).


在 draw.cl 里 增 加 :<br />

drawConnections:(windowHandle) procedure.<br />

这 样 , 我 们 利 用 drawConnections(Win) :- conn(P1, P2), drawLine(Win, P1, P2), fail. 的 强 制 回 溯<br />

功 能 , 把 所 存 的 连 接 数 据 全 部 画 出 了 。 注 意 , 有 些 城 市 因 为 没 有 设 置 按 钮 , 并 没 有 画 出 来 。<br />

如 图 5.4.1 所 示 。<br />

第 5.5 节 逻 辑 符 号<br />

“P1 与 P2”, 这 里 “ 与 ” 就 是 逻 辑 符 号 。 这 表 明 , 只 有 P1 和 P1 同 时 为 真 , “P1 与 P2”<br />

才 为 真 。 在 <strong>Prolog</strong> 语 言 里 , 用 逗 号 代 表 “ 与 ”(and); 用 分 号 代 表 “ 或 ”(or)。 这 里 “ 与 ”、<br />

“ 或 ”, 就 是 逻 辑 符 号 。<br />

这 样 如 果 我 们 描 述 (X>4) 与 (Y4),(Y


drawThem(Win) :- connections(Win), drawCities(Win).<br />

“:-” 符 号 之 前 的 部 分 , 我 们 称 其 为 规 则 的 “ 头 ”, 之 后 的 部 分 , 称 其 为 规 则 的 “ 尾 ”, 或 者<br />

规 则 的 “ 体 ”。 在 这 里 是 drawThem(Win) 头 , 而 是 “connections(Win), drawCities(Win)”<br />

尾 。 头 和 尾 之 间 用 “:-” 符 号 连 接 , 末 尾 , 用 “.” 表 示 结 束 。<br />

第 5.7 节 声 明<br />

谓 词 定 义 如 果 没 有 输 入 输 出 流 向 说 明 , 则 其 声 明 是 不 完 整 的 。 我 们 来 看 一 个 完 整 的 谓 词 声 明 :<br />

predicates<br />

drawThem:(windowHandle) procedure (i).<br />

这 个 声 明 说 明 :drawThem 的 参 数 , 是 输 入 的 , 是 属 于 windowHandle 类 型 的 。 通 常 情<br />

况 下 , 你 也 可 以 不 声 明 输 入 输 出 模 式 , 而 编 译 器 会 自 动 推 理 出 合 适 的 流 模 式 。 比<br />

如 我 们 也 可 以 定 义 成 这 样 :<br />

class predicates<br />

connections:( windowHandle).<br />

drawCities:(windowHandle).<br />

当 然 ,Horn 句 子 也 可 以 定 义 成 事 实 的 格 式 :<br />

class facts<br />

city:(string Name, pnt Position).<br />

conn:(pnt, pnt).<br />

稍 后 我 们 可 以 会 看 到 , 事 实 (facts) 是 可 以 插 入 、 删 除 的 , 它 的 参 数 类 型 说 明 和 谓 词 的 类 型<br />

说 明 相 同 。 关 于 类 型 说 明 , 我 可 以 用 直 接 指 明 类 型 声 明 所 在 的 模 块 的 形 式 , 如 下 :<br />

class facts<br />

conn:(vpiDomains::pnt, vpi::Domains::pnt).<br />

也 可 以 在 open 语 句 里 , 把 相 应 的 模 块 打 开 , 这 样 , 类 型 就 不 需 要 前 缀 来 指 明 位 置 了 。 注 意<br />

这 里 pnt 用 到 两 个 不 同 的 位 置 的 说 明 , 但 从 形 式 上 , 是 一 样 的 。<br />

open core, vpiDomains, vpi<br />

确 定 式 声 明 你 可 以 用 以 下 几 种 声 明 , 来 明 确 指 出 谓 词 是 有 一 种 解 、 多 种 解 :<br />

determ —— 指 明 这 个 谓 词 或 真 、 或 假 , 而 且 只 有 一 种 可 能 。<br />

procedure —— 说 明 这 个 谓 词 总 是 成 功 的 。 通 常 我 们 希 望 一 些 比 较 确 定 的 过 程 , 用 到 这 样


的 说 明 , 比 如 前 面 我 们 定 义 的 谓 词 :<br />

class predicates<br />

connections:( windowHandle) procedure (i).<br />

drawCities:(windowHandle) procedure (i).<br />

multi —— 用 到 这 样 说 明 谓 词 , 从 事 成 功 的 , 并 且 有 多 种 解 答 。<br />

nondeterm —— 这 样 说 明 的 谓 词 或 事 实 , 可 能 成 功 , 也 可 能 失 败 , 有 多 种 可 能 性 。 比 如 :<br />

class facts<br />

city:(string Name, pnt Position) nondeterm.<br />

conn:(pnt, pnt) nondeterm.<br />

我 们 可 以 利 用 这 类 谓 词 的 特 性 , 设 计 回 溯 过 程 比 如 :<br />

connections(Win) :-<br />

conn(P1, P2),<br />

drawLine(Win, P1, P2),<br />

fail.<br />

connections(_Win).<br />

这 里 ,conn(P1, P2) 提 供 两 个 点 P1、P2, 而 drawLine(Win, P1, P2) 就 利 用 这 两 个 点 , 画 线 。<br />

遇 到 fail 谓 词 ,<strong>Prolog</strong> 就 强 制 回 溯 , 寻 找 下 一 个 解 , 直 到 conn(P1, P2) 也 失 败 , 那 时 ,<br />

connections(_Win) 是 总 要 成 功 的 , 保 证 了 connections/1 总 能 成 功 。<br />

第 5.8 节 绘 图 谓 词<br />

计 算 机 界 面 很 重 要 部 分 就 是 绘 图 输 出 。 前 面 我 们 遇 到 几 个 绘 图 谓 词 , 比 如 drawLine/3, 它 的<br />

用 法 是 :<br />

drawLine(Win, P1, P2)<br />

它 用 来 在 点 P1 和 P2 之 间 划 一 条 直 线 。 而 P1、P2 用 pnt(X, Y) 来 表 示 。 而 画 椭 圆 就 是 用 到 这<br />

样 的 谓 词 :<br />

drawEllipse(W, rct(X1, Y1, X2, Y2))<br />

这 里 rct(X1, Y1, X2, Y2) 表 示 一 个 矩 形 , 用 左 上 角 坐 标 X1, Y1 与 右 下 角 坐 标 X2, Y2 来 表 示 。<br />

而 W、Win 在 这 里 代 表 要 绘 画 窗 口 的 句 柄 (WindowHandle), 这 不 过 是 告 诉 程 序 , 在 哪 里 绘<br />

图 , 在 窗 口 系 统 , 用 句 柄 来 代 表 窗 口 。


第 5.8 节 GDI 实 体<br />

其 实 , 绘 图 除 了 用 句 柄 之 外 , 还 可 用 到 GDI objects, 也 就 是 GDI 实 体 , 来 绘 图 。 比 如 , 前<br />

面 的 例 子 里 , 如 果 我 们 用 onPaint 谓 词 来 处 理 事 件 paintResponder, 如 图 5.8.1 所 示 , 它 的 编<br />

码 如 下 所 示 :<br />

clauses<br />

onPaint(_S, _Rectangle, GDIObject) :-<br />

draw::drawThem(GDIObject).<br />

图 5.8.1 增 加 onPaint 谓 词 作 为 PaintResponder<br />

那 么 我 们 可 以 重 新 改 写 drawThem 谓 词 的 声 明 , 在 draw.cl 里 这 样 声 明 :<br />

class draw<br />

open core<br />

predicates<br />

drawThem:(windowGDI).<br />

end class draw<br />

在 draw.pro 里 , 谓 词 重 新 改 写 后 如 下 :<br />

implement draw<br />

open core, vpiDomains, vpi<br />

class facts<br />

city:(string Name, pnt Position).<br />

conn:(pnt, pnt).<br />

class predicates<br />

connections:( windowGDI).<br />

drawCities:(windowGDI).<br />

clauses<br />

city("Salt Lake", pnt(30, 40)).<br />

city("Logan", pnt(100, 120)).<br />

city("Provo", pnt(100, 160)).<br />

conn(pnt(30, 40) , pnt(100, 120)).


conn(pnt(100, 120), pnt(100, 160)).<br />

drawCities(W) :-<br />

city(N, P),<br />

P= pnt(X1, Y1),<br />

X2= X1+10, Y2= Y1+10,<br />

W:drawEllipse(rct(X1, Y1, X2, Y2)),<br />

W:drawText(pnt(X1, Y1), N), fail.<br />

drawCities(_Win).<br />

connections(W) :-<br />

conn(P1, P2), W:drawLine(P1, P2), fail.<br />

connections(_W).<br />

drawThem(Win) :-<br />

connections(Win), drawCities(Win).<br />

end implement draw<br />

这 里 最 大 的 变 化 是 绘 图 语 句 W:drawEllipse(rct(X1, Y1, X2, Y2)) 这 样 的 引 用 与 前 面 的 引 用 有<br />

很 大 不 同 drawEllipse(W, rct(X1, Y1, X2, Y2))。<br />

drawCities(W) :-<br />

city(N, P),<br />

P= pnt(X1, Y1),<br />

X2= X1+10, Y2= Y1+10,<br />

W:drawEllipse(rct(X1, Y1, X2, Y2)),<br />

W:drawText(pnt(X1, Y1), N), fail.<br />

drawCities(_Win).<br />

第 5.9 节<br />

小 结 和 说 明<br />

我 们 这 里 介 绍 了 谓 词 的 声 明 、 绘 图 语 句 及 两 种 不 同 的 方 法 , 我 们 还 介 绍 了 Horn 句 子 ,<br />

介 绍 了 谓 词 的 多 解 性 , 这 里 就 慢 慢 接 近 真 实 的 编 程 了 。<br />

第 5.10 节 逻 辑 小 知 识 :Horn 句 子 的 意 义<br />

Horn 句 子 :<br />

H :- T1, T2, T3…<br />

它 的 意 义 是 :


如 果 T1、T2、T3…… 分 别 都 为 真 , 则 H 为 真 。<br />

我 们 需 要 记 住 “:-” 代 表 “ 如 果 ”; 而 逗 号 “,” 代 表 “ 与 ”, 分 号 “;” 代 表 “ 或 ”。 它 们 和 谓<br />

词 的 混 合 语 句 , 构 成 了 Horn 句 子 , 表 达 一 定 的 逻 辑 描 述 , 也 称 为 “ 规 则 ”。<br />

第 六 章 控 制 台 应 用<br />

许 多 人 喜 欢 图 形 界 面 , 但 是 控 制 台 界 面 虽 然 只 有 文 字 描 述 , 但 是 更 能 说 明 程 序 的 核 心 逻 辑 和<br />

计 算 方 法 , 这 一 章 , 我 们 就 用 控 制 台 应 用 (Console Application) 来 说 明 几 个 关 键 的 <strong>Prolog</strong><br />

元 素 。<br />

第 6.1 节 截 断<br />

我 们 常 常 需 要 强 制 程 序 回 溯 , 在 找 到 一 个 答 案 之 后 , 再 去 寻 找 另 外 一 个 , 在 前 面 我 们 介 绍 过<br />

的 fail 就 是 起 这 样 功 能 。 但 是 相 反 , 有 时 候 , 我 们 却 不 需 要 它 回 溯 , 不 需 要 再 去 寻 找 下 一 条<br />

规 则 、 寻 求 另 外 的 答 案 , 这 是 我 们 就 要 用 到 “ 截 断 ”(cut), 我 们 在 <strong>Prolog</strong> 里 , 用 叹 号 “!”<br />

来 表 示 。 比 如 以 下 的 阶 乘 递 归 算 法 描 述 :<br />

factorial(0)→1<br />

factorial(n) → n ×factorial(n -1)<br />

用 Horn 子 句 来 描 述 , 可 以 表 达 为 :<br />

fact(N, 1) :- N


图 6.1.1 创 建 控 制 台 应 用<br />

然 后 Build/Build, 编 辑 main.pro 文 件 , 这 个 文 件 开 始 的 内 容 如 下 :<br />

implement main<br />

open core<br />

constants<br />

className = "main".<br />

classVersion = "".<br />

clauses<br />

classInfo(className, classVersion).<br />

clauses<br />

run():-<br />

console::init(),<br />

succeed(). % place your own code here<br />

end implement main<br />

经 过 编 辑 后 main.pro 如 下 所 示 :<br />

implement main<br />

open core<br />

class predicates<br />

fact:(integer N, integer Res) procedure (i,o).<br />

clauses<br />

classinfo("facfun", "1.0").<br />

fact(N, 1) :- N


fact(X, F),<br />

stdio::write(" 控 制 台 应 用 输 出 结 果<br />

stdio::nl,<br />

!,<br />

run().<br />

run(). % 按 Ctrl+Break 结 束<br />

end implement main<br />

", " 输 入 数 字 = ",X, " 其 阶 乘 = ", F),<br />

goal<br />

mainExe::run(main::run ).<br />

运 行 后 , 我 们 输 入 一 个 数 字 , 就 得 到 一 个 阶 乘 结 果 如 图 6.1.2 所 示 , 直 至 我 们 同 时 按 下 Ctrl<br />

键 和 右 上 角 的 Break 键 结 束 。<br />

图 6.1.2 阶 乘 计 算 结 果<br />

第 6.2 节 表<br />

表 是 一 群 具 有 相 似 特 征 的 元 素 的 集 合 体 , 我 们 把 其 相 似 的 特 征 成 为 元 素 的 类 型 , 而 整 个 表 用<br />

方 括 号 括 起 来 、 用 逗 号 相 间 隔 的 形 式 来 表 示 , 其 类 型 用 元 素 类 型 后 加 星 号 “*” 来 表 示 。 每<br />

个 不 是 空 表 的 表 , 都 会 有 表 头 和 表 尾 ; 空 表 是 没 有 一 个 元 素 的 表 。 例 如 :<br />

List Type Head Tail<br />

[3, 4, 5, 6, 7] integer* 3 [4, 5, 6, 7]<br />

["wo3", "ni3", "ta1"] string* "wo3" ["ni3", "ta1"]<br />

[4] integer* 4 []<br />

[3.4, 5.6, 2.3] real* 3.4 [5.6, 2.3]<br />

我 们 可 以 把 任 何 不 是 空 表 的 表 写 作 :<br />

[Head | Tail ]<br />

其 中 ,head 是 一 个 元 素 ,Tail 是 一 个 表 。 所 以 , 你 可 以 看 到 下 面 的 匹 配 过 程 :


Pattern List X Xs<br />

[X|Xs] [3.4,5.6,2.3] X = 3.4 Xs = [5.6,2.3]<br />

[X|Xs] [5.6,2.3] X = 5.6 Xs=[2.3]<br />

[X|Xs] [2.3] X = 2.3 Xs = []<br />

[X|Xs] [] Does not match.<br />

同 样 , 我 们 可 以 写 出 更 多 的 元 素 , 要 求 表 匹 配 , 从 而 进 行 删 选 , 比 如 我 们 假 定 表 形 式 为 [X1,X2<br />

| Xs], 这 就 是 说 , 至 少 有 两 个 元 素 的 表 , 那 么 ,2 个 元 素 以 下 的 表 , 就 不 匹 配 :<br />

Pattern List X1,X2 Xs<br />

[X1,X2|Xs] [3,5,2,7] X1 = 3,X2 = 5 Xs = [2,7]<br />

[X1,X2|Xs] [2,7] X1 = 2,X2 = 7 Xs=[]<br />

[X1,X2|Xs] [7] Does not match.<br />

[X1,X2|Xs] [] Does not match.<br />

我 们 通 过 实 例 来 体 会 表 的 处 理 , 如 下 建 立 一 个 应 用 “avg”:<br />

General<br />

Project Name: avg<br />

UI Strategy: console<br />

先 Build 一 下 , 然 后 编 辑 main.pro 最 后 如 下 所 示 :<br />

implement main<br />

open core, console<br />

domains<br />

rList= real*.<br />

class predicates<br />

len:(rList, real) procedure (i, o).<br />

clauses<br />

classInfo("avg", "1.0").<br />

len([], 0) :- !.<br />

len([_X|Xs], 1.0+S) :- len(Xs, S).<br />

run():-<br />

console::init(),<br />

List= read(),<br />

len(List, A),<br />

write(A), nl,<br />

run().


end implement main<br />

goal<br />

mainExe::run(main::run).<br />

运 行 后 如 图 6.2.1 所 示 :<br />

图 6.2.1 正 确 输 入 一 个 实 数 表 , 则 返 回 长 度 数 字<br />

但 是 当 不 是 表 , 或 者 不 是 实 数 表 , 程 序 会 出 错 返 回 。 下 面 我 们 还 可 以 加 入 sum/2 来 计 算 所 有<br />

元 素 的 和 :<br />

sum([], 0) :- !.<br />

sum([X|Xs], S+X) :- sum(Xs, S).<br />

我 们 分 析 一 下 sum([3.4, 5.6, 2.3], S) 的 计 算 经 过 :<br />

1. sum([3.4, 5.6, 2.3], S) 匹 配<br />

sum([X|Xs], S+X) .- sum(Xs, S), with X=3.4, Xs=[5.6, 2.3],<br />

得 到 sum([3.4, 5.6, 2.3], S[5.6,2.3] + 3.4) .- sum([5.6, 2.3], S[5.6,2.3])<br />

2. sum([5.6, 2.3], S[5.6,2.3]) 匹 配<br />

sum([X|Xs], S+X) .- sum(Xs, S), with X=5.6, Xs=[2.3],<br />

得 到 sum([5.6, 2.3], S[2.3] + 5.6) .- sum([2.3], S[2.3])<br />

3. sum([2.3], S[2.3]) 匹 配<br />

sum([X|Xs], S+X) .- sum(Xs, S), with X=2.3, Xs=[],<br />

得 到 sum([2.3], S[] + 2.3) .- sum([], S[])<br />

4. sum([], S[]) 匹 配 sum([], 0.0) 得 到 S[] = 0.<br />

我 们 可 以 让 程 序 写 出 过 程 如 下 图 6.2.2 所 示 。


图 6.2.2 计 算 一 个 表 的 元 素 之 和 的 过 程<br />

相 应 的 程 序 如 下 所 示 :<br />

implement main<br />

open core, console<br />

domains<br />

rList= real*.<br />

class predicates<br />

len:(rList, real) procedure (i, o).<br />

sum:(rList, real) procedure (i, o).<br />

clauses<br />

classInfo("avg", "1.0").<br />

len([], 0) :- !.<br />

len([_X|Xs], 1.0+S) :- len(Xs, S).<br />

sum([], 0) :- !.<br />

sum([X|Xs], S+X) :-<br />

write("now list = ", [X|Xs],"\n"),<br />

sum(Xs, S),<br />

write("now result =", S, "\n").


un():-<br />

console::init(),<br />

List= read(),<br />

len(List, A),<br />

write(A), nl,<br />

sum(List,X),<br />

write("Final result = ", X),nl,<br />

run().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

当 然 , 我 们 还 可 以 用 另 外 一 种 办 法 来 计 算 元 素 之 和 :<br />

add([], A, A).<br />

add([X|Xs], A, S) :- add(Xs, X+A, S).<br />

但 是 我 们 调 用 的 时 候 需 要 调 用 add(List,0,Sum)。 而 说 明 此 谓 词<br />

add:(rList, real, real) procedure (i, i, o).<br />

程 序 如 下 :<br />

implement main<br />

open core, console<br />

domains<br />

rList= real*.<br />

class predicates<br />

len:(rList, real) procedure (i, o).<br />

add:(rList, real, real) procedure (i, i, o).<br />

clauses<br />

classInfo("avg", "1.0").<br />

len([], 0) :- !.<br />

len([_X|Xs], 1.0+S) :- len(Xs, S).<br />

add([],A,A ) :- !.<br />

add([X|Xs],A , Sum) :-


write("original list=", [X|Xs],"result =", A,"\t\t now result=", A+X, "\n"),<br />

add(Xs, A+X, Sum).<br />

run():-<br />

console::init(),<br />

List= read(),<br />

len(List, A),<br />

write(A), nl,<br />

add(List,0,X),<br />

write("Final result = ", X),nl,<br />

run().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

运 行 结 果 如 图 6.2.3 所 示 。<br />

图 6.2.3 另 外 一 种 计 算 方 法<br />

第 6.3 节 表 相 关 的 几 个 话 题<br />

表 示 一 个 很 重 要 的 计 算 工 具 , 我 们 这 里 要 介 绍 几 个 比 较 重 要 的 表 相 关 的 计 算 话 题 。<br />

简 化 Reduction<br />

一 个 表 , 可 以 用 于 计 算 每 个 元 素 的 和 , 这 个 过 程 , 被 称 为 简 化 过 程 (reduction), 因 为 这 个 过<br />

程 是 把 输 入 的 维 数 减 少 了 。 我 们 可 以 把 表 看 成 是 一 维 数 据 , 而 和 结 果 是 零 维 数 据 , 求 和 过 程<br />

是 1 维 变 0 维 的 过 程 。 有 两 种 方 法 可 以 达 成 目 标 : 递 归 式 简 化 (recursive) 和 重 复 式 简 化<br />

(iterative)。 我 们 展 示 这 两 种 方 法 如 下 :<br />

%Recursive reduction<br />

class predicates<br />

sum:(real*, real) procedure (i, o).


clauses<br />

sum([], 0) :- !.<br />

sum([X|Xs], S+X) :- sum(Xs, S).<br />

递 归 式 是 假 定 知 道 表 尾 的 结 果 , 推 理 获 得 当 前 表 的 结 果 , 以 此 类 推 , 而 总 是 有 一 个 表 的 结 果<br />

是 可 知 的 , 然 后 回 退 计 算 出 最 终 结 果 ; 重 复 式 就 是 不 断 重 复 累 加 的 过 程 , 更 接 近 人 们 计 算 的<br />

逻 辑 。<br />

%Iterative reduction<br />

red([], A, A) :- !.<br />

red([X|Xs], A, S) :- red(Xs, X+A, S).<br />

sumation(Xs, S) :- red(Xs, 0.0, S).<br />

当 我 们 希 望 计 算 元 素 的 乘 积 时 , 可 以 替 换 得 到 如 下 程 序 :<br />

%Iterative reduction<br />

red([], A, A) :- !.<br />

red([X|Xs], A, S) :- red(Xs, X*A, S).<br />

product(Xs, S) :- red(Xs, 0.0, S).<br />

我 们 看 到 , 比 较 两 个 代 码 , 它 们 有 很 大 的 相 似 性 : 除 了 “X+A” 和 “X*A” 的 差 别 之 外 , 两<br />

个 计 算 过 程 几 乎 相 似 。 在 <strong>Visual</strong> <strong>Prolog</strong> 里 , 我 们 就 可 以 采 用 更 高 一 级 的 算 法 描 述 , 来 实 现 两<br />

种 不 同 算 法 的 统 一 。 我 们 定 义 red/4, 代 码 如 下 :<br />

implement main<br />

open core, console<br />

domains<br />

pp2 = (real Argument1, real Argument2) -> real ReturnType.<br />

clauses<br />

classInfo("main", "hi_order").<br />

class predicates<br />

sum : pp2.<br />

prod : pp2.<br />

red : (pp2, real*, real, real) procedure (i, i, i, o).<br />

clauses<br />

sum(X, Y)= Z :- Z=X+Y.<br />

prod(X, Y)= Z :- Z=X*Y.<br />

red(_P, [], A, A) :- !.<br />

red(P, [X|Xs], A, Ans) :-<br />

red(P, Xs, P(X, A), Ans).<br />

run():- console::init(),<br />

List =read(),<br />

red(prod, List, 1, Product),<br />

red(sum, List, 0, Sum),<br />

write("\n 您 输 入 的 实 数 表 是 ", List,


"\n 其 各 个 元 素 的 乘 积 结 果 =", Product,<br />

"\n 其 各 个 元 素 的 累 加 结 果 =", Sum),<br />

nl,<br />

run().<br />

end implement main<br />

goal<br />

mainExe::run(main::run<br />

我 们 运 行 一 下 , 可 以 看 到 运 行 结 果 如 下 , 当 你 输 入 一 个 实 数 表 , 得 到 不 同 的 输 出 结 果 , 如 图<br />

6.3.1 所 示 。<br />

图 6.3.1 更 高 形 式 的 统 一 算 法<br />

ZIP<br />

一 个 很 有 名 的 算 法 是 , 求 两 个 表 的 点 积 (dotproduct), 算 法 描 述 如 下 :<br />

class predicates<br />

dotproduct:(real*, real*, real) procedure (i, i, o).<br />

clauses<br />

dotproduct([], _, 0) :- !.<br />

dotproduct(_, [], 0) :- !.<br />

dotproduct([X|Xs], [Y|Ys], X*Y+R) :- dotproduct(Xs, Ys, R).<br />

这 里 , 我 们 分 析 X*Y+R, 实 际 上 , 可 以 看 作 两 个 数 相 乘 、 两 个 数 相 加 的 组 合 。 这 样 , 我 们<br />

可 以 利 用 前 面 的 成 果 , 写 出 另 外 一 种 算 法 , 设 计 一 个 zip/4:<br />

zip(_P, [], _, []) :- !.<br />

zip(_P, _, [], []) :- !.<br />

zip(P, [X|Xs], [Y|Ys], [Z|As]) :-<br />

Z= P(X, Y),<br />

zip(P, Xs, Ys, As).


得 到 乘 积 表 后 , 在 计 算 这 个 表 的 元 素 相 加 的 结 果 , 利 用 前 面 的 代 码 :<br />

dotproduct(Xs, Ys, D) :-<br />

zip(prod, Xs, Ys, Z),<br />

red(sum, Z, 0, D).<br />

我 们 最 后 设 计 代 码 如 下 :<br />

implement main<br />

open core, console<br />

domains<br />

pp2 = (real Argument1, real Argument2) -> real ReturnType.<br />

clauses<br />

classInfo("main", "hi_order").<br />

class predicates<br />

sum : pp2.<br />

prod : pp2.<br />

red : (pp2, real*, real, real) procedure (i, i, i, o).<br />

zip : (pp2, real*, real*, real*) procedure (i, i, i, o).<br />

dotproduct : ( real*, real*, real) procedure ( i, i, o).<br />

clauses<br />

sum(X, Y)= Z :- Z=X+Y.<br />

prod(X, Y)= Z :- Z=X*Y.<br />

red(_P, [], A, A) :- !.<br />

red(P, [X|Xs], A, Ans) :-<br />

red(P, Xs, P(X, A), Ans).<br />

zip(_P, [], _, []) :- !.<br />

zip(_P, _, [], []) :- !.<br />

zip(P, [X|Xs], [Y|Ys], [Z|As]) :-<br />

Z= P(X, Y),<br />

zip(P, Xs, Ys, As).<br />

dotproduct(Xs, Ys, D) :-<br />

zip(prod, Xs, Ys, Z),<br />

red(sum, Z, 0, D).<br />

run():- console::init(),<br />

write("\n 请 输 入 第 一 个 实 数 表 :"),<br />

List1 =read(),<br />

write(" 请 输 入 第 二 个 实 数 表 :"),<br />

List2 =read(),<br />

dotproduct(List1, List2, Product),<br />

write("\n 您 输 入 的 实 数 表 是 ", List1, " 和 ", List2,<br />

"\n 其 点 积 结 果 =", Product),<br />

nl,<br />

run().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).


运 行 后 如 图 6.3.2 所 示 。<br />

图 6.3.2 计 算 两 个 表 的 点 积<br />

第 6.4 节 字 符 串 操 作<br />

这 里 我 们 将 通 过 几 个 字 符 串 操 作 的 例 子 , 展 示 如 何 在 <strong>Visual</strong> <strong>Prolog</strong> 里 处 理 数 据 结 构 问 题 。<br />

Fronttoken concatlist concat/2/3<br />

Fronttoken 是 一 个 用 途 广 泛 的 字 符 串 操 作 谓 词 。 看 看 下 面 的 描 述 的 结 果 会 是 怎 样 ?<br />

frontToken("break December", T, R)<br />

实 际 上 我 们 得 到 T="break"、R=" December"。 这 里 我 们 设 计 一 个 小 程 序 , 来 展 示 fronttoken<br />

的 效 果 , 运 行 结 果 如 图 6.4.1 所 示 。<br />

implement main<br />

open core, string, console<br />

class predicates<br />

tokenize:(string, string_list) procedure (i, o).<br />

clauses<br />

classInfo("main", "string_example").<br />

tokenize(S, [T|Ts]) :-<br />

frontToken(S, T, R),<br />

!,<br />

tokenize(R, Ts).<br />

tokenize(_, []).<br />

run():-<br />

console::init(),


L= ["it ", "was ", "in ", "the ",<br />

"bleak ", "December!"],<br />

S=concatList(L),<br />

UCase= toUpperCase(S),<br />

RR= concat("case: ", Ucase),<br />

R1= concat("It is ", "upper ", RR),<br />

write(R1), nl,<br />

tokenize(R1, Us),<br />

write(Us),<br />

_X = readline(), nl.<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

这 里 用 到 concat/2、concat/3, 它 们 是 把 2 个 或 三 个 字 符 串 按 顺 序 连 接 在 一 起 , 中 间 不 会 有 任<br />

何 增 添 。 而 concatlist 则 是 把 字 符 串 表 的 每 个 元 素 按 , 按 照 顺 序 连 接 在 一 起 , 而 且 字 符 串 和<br />

字 符 串 之 间 , 增 加 一 个 空 格 作 为 间 隔 , 形 成 一 个 字 符 串 。toUpperCase 是 把 字 符 串 的 所 有 字<br />

符 转 换 成 为 大 写 字 母 。<br />

图 6.4.1 fronttoken 把 字 符 串 分 解 成 一 个 一 个 所 谓 的 token<br />

toterm<br />

我 们 还 常 常 希 望 把 输 入 的 代 表 某 个 命 令 或 功 能 的 字 符 串 , 转 换 成 一 个 可 执 行 的 术 语 (term),<br />

这 时 , 我 们 常 常 用 到 toTerm。 下 面 的 例 子 里 ,Sx= stdio::readLine() 从 命 令 行 读 取 一 个 字 符 串<br />

到 Sx 里 然 后 toTerm 把 这 个 字 符 串 转 换 成 实 数 , 这 里 hasdomain(real, IX) 是 为 了 确 保 toTerm<br />

转 换 的 结 果 是 我 们 要 的 实 数 。 运 行 结 果 如 图 6.4.2 所 示 。<br />

implement main<br />

open core<br />

clauses<br />

classinfo("main", "toTerm_example").<br />

run():-<br />

console::init(),<br />

Sx= stdio::readLine(),<br />

hasdomain(real, IX),<br />

IX= toTerm(Sx),<br />

stdio::write("\n 你 输 入 的 数 字 ", IX," 的 平 方 值 是 :", IX^2,"\n"),<br />

run().


end implement main<br />

goal<br />

mainExe::run(main::run)<br />

图 6.4.2 输 入 的 字 符 串 转 换 为 实 数 , 计 算 平 方 值<br />

hasdomain<br />

hasdomain 甚 至 于 可 以 接 受 类 似 于 表 等 更 复 杂 的 数 据 类 型 , 下 面 的 例 子 , 我 们 制 定 一 个 整 数<br />

表 , 注 意 的 是 hasdomain 不 接 受 “*” 的 表 表 示 方 法 , 这 里 我 们 不 能 够 用 “integer*”, 而 要<br />

用 core::integer_list, 这 是 预 先 定 义 的 整 数 表 数 据 类 型 。<br />

implement main<br />

open core<br />

clauses<br />

classInfo("main", "hasdomain_example").<br />

class predicates<br />

sum:(integer*, integer) procedure (i, o).<br />

clauses<br />

sum([], 0) :- !.<br />

sum([X|Xs], S+X) :- sum(Xs, S).<br />

run():- console::init(),<br />

hasdomain(integer_list, Xs),<br />

Xs= toTerm(stdio::readLine()),<br />

sum(Xs, Sum),<br />

stdio::write("\n 你 输 入 的 整 数 表 ", Xs,<br />

" \n 其 各 元 素 和 是 :", Sum,"\n\n"),<br />

run().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

一 旦 我 们 输 入 的 与 预 先 规 定 的 不 符 , 则 程 序 出 错 退 出 。 程 序 运 行 如 图 6.4.3 所 示 。


图 6.4.3 输 入 整 数 表 并 计 算 表 中 各 元 素 的 和<br />

tostring<br />

我 们 知 道 怎 么 把 输 入 的 串 转 化 成 <strong>Prolog</strong> 的 数 据 类 型 , 但 是 有 时 候 , 我 们 也 需 要 把 <strong>Prolog</strong> 内<br />

的 类 型 数 据 转 换 成 字 符 串 来 表 达 。 这 时 就 需 要 tostring 谓 词 了 。 下 面 这 个 例 子 演 示 如 何 把 实<br />

数 表 转 换 成 字 符 串 , 并 与 其 他 字 符 串 合 并 , 然 后 显 示 出 来 , 后 面 的 完 全 是 字 符 串 操 作 。 只 是<br />

为 了 证 明 已 经 转 换 成 了 字 符 串 。<br />

implement main<br />

open core<br />

clauses<br />

classInfo("main", "toString_example").<br />

class predicates<br />

sum:(integer*, integer) procedure (i, o).<br />

clauses<br />

sum([], 0) :- !.<br />

sum([X|Xs], S+X) :- sum(Xs, S).<br />

run():- console::init(),<br />

hasdomain(integer_list, Xs),<br />

Xs= toTerm(stdio::readLine()),<br />

sum(Xs, Sum),<br />

stdio::write("\n Your input is a number list : ", Xs,<br />

" \n All the list member's sum is : ", Sum,"\n"),<br />

Str= toString(Xs),<br />

Msg= string::concat("String representation is : ", Str),<br />

stdio::write(Msg),<br />

stdio::nl,<br />

run().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

可 以 看 到 运 行 结 果 如 图 6.4.4 所 示 , 我 们 故 意 把 把 输 入 表 增 加 了 很 多 空 格 , 但 是 一 旦 表 达 成<br />

<strong>Prolog</strong> 的 整 数 表 , 其 格 式 是 固 定 的 。 为 了 证 明 这 个 整 数 表 是 可 以 计 算 的 , 我 们 算 出 了 它 的 元


素 之 和 , 这 是 我 们 之 前 介 绍 过 的 一 种 运 算 。 这 里 , 如 果 是 定 义 实 数 表 , 则 编 译 时 会 出 问 题 。<br />

图 6.4.4 表 格 转 换 成 字 符 串<br />

format<br />

格 式 化 输 出 对 数 据 的 显 示 非 常 有 用 , 下 面 这 个 程 序 , 用 format 来 构 成 格 式 化 输 出 , 结 果 如<br />

图 6.4.5 所 示 。<br />

implement main<br />

clauses<br />

classInfo("main", "formatting").<br />

run():- console::init(),<br />

Str1= string::format("%8.3f\n %10.1e\n", 3.45678, 35678.0),<br />

Str2= string::format("%d\n %10d\n", 456, 18),<br />

Str3= string::format("%-10d\n %010d\n", 18, 18),<br />

stdio::write(Str1, Str2, Str3),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

注 意 这 里 用 到 一 些 规 定 :<br />

f 格 式 化 输 出 固 定 小 数 位 的 实 数 ( 如 123.4).<br />

e 用 科 学 记 数 法 表 示 的 实 数 ( 如 1.234e+002).<br />

g 用 f 或 e 的 最 短 的 格 式 输 出 实 数<br />

d 带 符 号 的 十 进 制 数 字<br />

u 无 符 号 的 整 数<br />

x 十 六 进 制 数 字<br />

o 十 进 制<br />

c 字 符<br />

B 用 <strong>Visual</strong> <strong>Prolog</strong> 二 进 制 类 型<br />

R 作 为 数 据 库 参 考 数 据 类 型<br />

P 作 为 程 序 参 数


s 作 为 字 符 串<br />

程 序 里 的 %8.3f 的 “%” 代 表 一 个 参 数 ,f 代 表 固 定 位 小 数 格 式 ,“8.3” 代 表 这 个 小 数 固 定<br />

为 8 个 数 字 位 长 度 , 而 小 数 部 分 占 3 个 位 长 度 , 你 看 到 时 进 行 了 四 舍 五 入 。“10.1e” 表 示 科<br />

学 记 数 法 表 示 小 数 点 后 部 分 用 1 位 表 示 , 整 个 数 字 用 10 个 位 表 示 。“%d”、“%10d” 输 出 的<br />

10 进 制 数 字 , 前 者 不 作 位 数 限 制 , 后 者 限 制 用 10 个 位 置 来 表 达 , 不 足 部 分 前 面 留 空 。 而<br />

“%-10d” 则 展 示 了 10 个 位 置 输 出 左 对 齐 的 情 况 ,“%010d” 展 示 了 10 个 位 置 输 出 , 空 余 部<br />

分 用 “0” 填 满 的 情 况 。<br />

图 6.4.5 数 据 格 式 化 输 出 展 示<br />

第 6.4.1 节 有 用 的 字 符 串 操 作 谓 词<br />

这 里 我 们 将 介 绍 几 个 有 用 的 字 符 串 操 作 谓 词 :adjustBehaviour、adjustSide、caseSensitivity,<br />

其 定 义 如 下 所 示 :<br />

domains<br />

adjustBehaviour =expand();cutRear();cutOppo<strong>site</strong>().<br />

adjustSide = left(); right().<br />

caseSensitivity =caseSensitive();caseInsensitive();casePreserve().<br />

predicates<br />

adjust : (string Src, charCount FieldSize, adjustSide Side)<br />

->string AdjustedString.<br />

adjust : (string Src, charCount FieldSize, string Padding,<br />

adjustSide Side) -> string AdjustedString.<br />

adjustLeft : (string Src, charCount FieldSize)<br />

->string AdjustedString.<br />

adjustLeft : (string Src, charCount FieldSize,<br />

string Padding) -> string AdjustedString.<br />

adjustRight : (string Src, charCount FieldSize)<br />

->string AdjustedString.<br />

adjustRight : (string Src, charCount FieldSize,<br />

string Padding) -> string AdjustedString.<br />

adjustRight : (string Src, charCount FieldSize, string Padding,<br />

adjustBehaviour Behaviour) -> string AdjustedString.<br />

它 们 已 经 定 义 在 string 模 块 里 了 。 我 们 用 下 面 的 程 序 演 示 其 中 一 个 谓 词 :


implement main<br />

clauses<br />

classInfo("main", "adjust_example").<br />

run():- console::init(),<br />

FstLn1=" 这 是 靠 右 格 式 , 前 面 有 空 ",<br />

Str1= string::adjust(FstLn1, 16, "◆", string::right),<br />

stdio::write(Str1), stdio::nl,<br />

FstLn2=" 这 是 靠 左 格 式 , 后 面 有 空 ",<br />

Str2= string::adjust(FstLn2, 16, "◆", string::left),<br />

stdio::write(Str2), stdio::nl,<br />

FstLn10=" 这 是 靠 右 格 式 , 前 面 有 空 ",<br />

Str10= string::adjust(FstLn10, 16, string::right),<br />

stdio::write(Str10), stdio::nl,<br />

FstLn20=" 这 是 靠 左 格 式 , 后 面 有 空 ",<br />

Str20= string::adjust(FstLn20, 16, string::left),<br />

stdio::write(Str20), stdio::nl,<br />

Str21= string::adjustright(FstLn20, 16, " "),<br />

stdio::write(Str21), stdio::nl,<br />

Str22= string::adjustleft(FstLn20, 16, " "),<br />

stdio::write(Str22), stdio::nl,<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

运 行 结 果 如 图 6.4.6 所 示 , 可 以 看 到 , 我 们 可 以 指 定 字 符 串 的 宽 度 、 对 齐 方 向 , 并 可 以 指 定<br />

空 余 位 置 用 什 么 字 符 串 填 满 。adjust/3 和 adjustleft/3 或 者 adjustright/3 的 空 格 填 余 空 相 当 。<br />

图 6.4.6 对 齐 和 填 空<br />

关 于 字 符 串 连 接 、 分 拆 、 生 产 的 相 关 谓 词 , 有 的 我 们 前 面 已 经 提 到 过 , 汇 总 起 来 有 :<br />

concat : (string First, string Last) ->


string Output procedure (i,i).<br />

concatList : (core::string_list Source) -><br />

string Output procedure (i).<br />

concatWithDelimiter : (core::string_list Source,<br />

string Delimiter) -> string Output procedure (i,i).<br />

create : (charCount Length) -> string Output procedure (i).<br />

create : (charCount Length, string Fill) -> string Output procedure (i,i).<br />

createFromCharList : (core::char_list CharList) -> string String.<br />

我 们 设 计 一 个 小 程 序 来 演 示 , 如 下 所 示 , 运 行 结 果 如 图 6.4.7 所 示 。<br />

implement main<br />

clauses<br />

classInfo("main", "stringops").<br />

run():-<br />

console::init(),<br />

SndLn= [ "murderous", "doomed",<br />

"that cost the Achaeans countless losses"],<br />

Str= string::concatWithDelimiter(SndLn, ", "),<br />

Rule= string::create(20, "-*&"),<br />

stdio::write("\n",SndLn,"\n",Str), stdio::nl,<br />

stdio::write(Rule), stdio::nl,<br />

_=stdio::readline().<br />

end implement main<br />

goal mainExe::run(main::run).<br />

图 6.4.7<br />

以 下 这 些 谓 词 , 都 是 字 符 串 处 理 很 重 要 的 工 具 :<br />

equalIgnoreCase : (string First, string Second) determ (i,i).<br />

front : (string Source, charCount Position, string First, string Last)<br />

procedure (i,i,o,o).<br />

frontChar : (string Source, char First, string Last) determ (i,o,o).<br />

frontToken : (string Source, string Token,<br />

string Remainder) determ (i,o,o).<br />

getCharFromValue : (core::unsigned16 Value) -> char Char.<br />

getCharValue : (char Char) -> core::unsigned16 Value.<br />

hasAlpha : (string Source) determ (i).<br />

hasDecimalDigits : (string Source) determ (i).


hasPrefix : (string Source, string Prefix, string Rest) determ (i,i,o).<br />

hasSuffix : (string Source, string Suffix, string Rest) determ (i,i,o).<br />

isLowerCase : (string Source) determ (i).<br />

isUpperCase : (string Source) determ (i).<br />

isWhiteSpace : (string Source) determ.<br />

lastChar : (string Source, string First, char Last) determ (i,o,o).<br />

length : (string Source) -> charCount Length procedure (i).<br />

replace : (string Source, string ReplaceWhat,<br />

string ReplaceWith, caseSensitivity Case) -><br />

string Output procedure.<br />

replaceAll : (string Source, string ReplaceWhat,<br />

string ReplaceWith) -> string Output.<br />

replaceAll : (string Source, string ReplaceWhat,<br />

string ReplaceWith, caseSensitivity Case) -><br />

string Output.<br />

search : (string Source, string LookFor) -><br />

charCount Position determ (i,i).<br />

search : (string Source, string LookFor, caseSensitivity Case) -><br />

charCount Position determ (i,i,i).<br />

split : (string Input, string Separators) -> string_list.<br />

split_delimiter : (string Source, string Delimiter) -><br />

core::string_list List procedure (i,i).<br />

subString : (string Source, charCount Position,<br />

charCount HowLong) -><br />

string Output procedure (i,i,i).<br />

toLowerCase : (string Source) -><br />

string Output procedure (i).<br />

toUpperCase : (string Source) -><br />

string Output procedure (i).<br />

trim : (string Source) -> string Output procedure (i).<br />

trimFront : (string Source) -> string Output procedure (i).<br />

trimInner : (string Source) -> string Output procedure (i).<br />

trimRear : (string Source) ->string Output procedure (i).<br />

我 们 举 一 个 小 例 子 如 下 , 其 运 行 结 果 如 图 6.4.8 所 示 。<br />

implement main<br />

clauses<br />

classInfo("main", "trim_example").<br />

run():-<br />

console::init(),<br />

Str= " I love <strong>Visual</strong> <strong>Prolog</strong>. How about you? ",<br />

T= string::trim(Str),<br />

stdio::write(T), stdio::nl,<br />

T1= string::trimInner(T),<br />

stdio::write(T1), stdio::nl,<br />

_=stdio::readline().


end implement main<br />

goal mainExe::run(main::run).<br />

图 6.4.8 把 字 符 串 内 多 余 的 空 格 去 掉<br />

第 6.5 节 逻 辑 小 知 识 : 谓 词 的 语 法<br />

Porolog 谓 词 描 述 p(t1, t2, … tn-1, tn), 通 常 记 做 p/n,n 是 这 个 谓 词 的 维 数 , 不 同 维 数 , 虽<br />

然 名 称 相 同 , 也 是 不 同 的 谓 词 。p 称 为 谓 词 的 名 或 符 号 ,t1, t2, … tn-1, tn 称 为 谓 词 的 term,<br />

也 可 以 称 为 其 参 数 。 这 些 参 数 可 以 是 函 数 性 的 符 号 , 也 可 以 是 字 符 串 、 符 号 、 数 字 等 等 。 如<br />

果 t1 和 t2 是 terms, 那 么 t1 = t2、t1 > t2、 t1 < t2、 t1>=t2、t1=< t2 等 描 述 就 可 以 构 成 公 式 。<br />

谓 词 有 严 密 的 语 法 , 从 而 保 证 逻 辑 推 理 的 严 谨 性 。<br />

第 七 章 文 本 编 辑 器<br />

本 章 学 习 怎 么 用 编 辑 器 控 件 editControl 模 块 建 立 一 个 文 本 编 辑 器 。<br />

第 7.1 节 建 立 文 本 编 辑 器 程 序<br />

我 们 建 立 一 个 Project 名 字 叫 做 “myEditor2008”<br />

Project Name: myEditor2008<br />

UI Strategy: Object-oriented GUI(pfc/gui)<br />

第 二 步 , 操 作 File/Add 选 择 “<strong>Visual</strong> <strong>Prolog</strong> 7.x\pfc\gui\controls\editorControl”, 出 现 如 图<br />

7.1.1 所 示 。


图 7.1.1 加 入 editorControl 控 件<br />

然 后 , 在 根 目 录 下 创 建 myEditor2008 为 名 字 的 Form。 然 后 Build/Build, 再 进 行 对 文 件<br />

myEditor2008.frm 的 编 辑 , 点 击 Controls 对 话 框 的 钥 匙 按 钮 如 图 7.1.2 所 示 , 增 加 Custom<br />

Control, 选 择 editorControl, 得 到 如 图 7.1.3 所 示 。<br />

图 7.1.2 增 加 Custom Control, 拉 出 位 置 框 后 进 入 选 择 对 话 框<br />

图 7.1.3 增 加 了 editorControl 后<br />

如 前 所 述 , 使 菜 单 项 File/New 可 用 , 并 增 加 如 下 代 码 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :- F= myEditor2008:new(S), F:show().


运 行 一 下 , 点 击 File/New, 弹 出 编 辑 器 对 话 框 。 可 以 看 到 这 个 编 辑 器 里 可 以 进 行 文 字 输 入 和<br />

编 辑 。 如 图 7.1.4 所 示 。<br />

第 7.2 节 文 件 保 存<br />

我 们 编 辑 myEditor2008.frm, 增 加 一 个 按 钮 “save”。 如 图 7.2.1 所 示 。 然 后 增 加<br />

clickResponder 为 onSaveClick, 然 后 双 击 , 增 加 代 码 如 下 :<br />

predicates<br />

onSaveClick : button::clickResponder.<br />

clauses<br />

onSaveClick(_Source) = button::defaultAction() :-<br />

Txt= editorControl_ctl:getEntireText(),<br />

FName= vpiCommonDialogs::getFileName("*.*",<br />

["Text", "*.txt"],<br />

"Save", [], ".", _X), !,<br />

file::writeString(FName, Txt).<br />

onSaveClick(_Source) = button::defaultAction().<br />

图 7.2.1 增 加 Save 按 钮 , 增 加 onSaveClick 事 件 处 理<br />

然 后 运 行 一 下 , 我 们 编 辑 一 些 内 容 , 如 图 7.2.2 所 示 , 然 后 按 save 按 钮 , 出 现 对 话 框 如 图 7.2.3<br />

所 示 。


图 7.2.2 编 辑 一 些 内 容 有 了 save 按 钮<br />

图 7.2.3 保 存 文 件 对 话 框 , 你 可 以 起 名 字 、 选 择 位 置<br />

图 7.2.4 在 相 应 的 位 置 , 我 们 发 现 了 刚 才 保 存 的 文 本 文 件<br />

第 7.3 节 文 件 调 入<br />

为 了 实 现 文 件 调 入 , 我 们 在 myEditor2008 对 话 框 里 增 加 “Load” 按 钮 。 如 图 7.3.1 所 示 。


图 7.3.1 增 加 Load 按 钮<br />

然 后 在 “Properties ” 对 话 框 , 设 置 clickResponder 为 onLoadClick, 如 图 7.3.2 所 示 ; 然 后<br />

双 击 该 项 目 , 在 myEditor2008.pro 里 增 加 代 码 如 下 :<br />

predicates<br />

onLoadClick : button::clickResponder.<br />

clauses<br />

onLoadClick(_Source) = button::defaultAction() :-<br />

FName= vpiCommonDialogs::getFileName("*.*",<br />

["Text", "*.txt"],<br />

"Load", [], ".", _X),<br />

file::existFile(FName),<br />

!,<br />

Str= file::readString(FName, _IsUnicodeFile),<br />

editorControl_ctl:pasteStr(1, Str).<br />

onLoadClick(_Source) = button::defaultAction().<br />

图 7.3.2 设 置 ClickResponder 为 onLoadClick<br />

然 后 运 行 一 下 , 我 们 点 击 Load 调 入 前 面 编 辑 过 的 文 件 my2008.txt”, 结 果 如 图 7.3.3 所 示 。<br />

图 7.3.3 调 入 了 原 来 存 过 的 文 件


第 7.4 节 编 辑 器 的 改 进<br />

我 们 希 望 当 myEditor2008 对 话 框 最 大 化 后 , 编 辑 框 也 跟 着 扩 大 , 这 样 就 要 变 化 设 置 , 把 原<br />

来 的 如 图 7.4.1 所 示 , 修 改 为 如 图 7.4.2 所 示 。<br />

图 7.4.1 原 来 编 辑 控 件 的 设 置<br />

图 7.4.2 四 个 角 都 和 对 话 框 的 形 状 关 联 起 来<br />

同 时 , 我 们 要 把 Load 和 Save 按 钮 的 设 置 改 成 如 图 7.4.3 所 示 。<br />

图 7.4.3 按 钮 Load 的 位 置 关 联 设 置 , 相 对 于 右 下 角 ,Save 也 一 样<br />

这 样 , 当 编 辑 对 话 框 最 大 化 后 , 我 们 看 到 运 行 结 果 如 图 7.4.4 所 示 。


图 7.4.4 编 辑 窗 口 最 大 化 后 的 效 果 , 位 置 随 之 改 变 , 而 之 前 , 很 不 理 想 。<br />

第 八 章 绘 图<br />

这 一 章 , 我 们 进 一 步 探 讨 onPaint 事 件 绘 图 的 相 关 话 题 。 首 先 我 们 创 建 一 个 带 Form 的 工 程 ,<br />

在 Form 上 绘 图 , 我 们 需 要 进 行 如 下 操 作 :<br />

◆ 创 建 project:<br />

Project Name: painting<br />

Object-oriented GUI (pfc/gui)<br />

◆ 在 根 目 录 创 建 paintPack 包<br />

◆ 创 建 canvas.frm 到 paintPack 包 里 , 并 Build/Build 程 序<br />

◆ 改 变 菜 单 选 项 使 任 务 菜 单 File/New 可 以 工 作 , 并 按 照 操 作 TaskWindow/Code<br />

Expert/Menu/TaskMenu/id_file/id_file_new 进 行 , 增 加 代 码 , 点 击 New 菜 单 项 显 示<br />

Form:<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

X= canvas::new(S), X:show().<br />

注 意 , 这 里 X= canvas::new(S) 创 建 一 个 实 体 X, 它 是 任 务 窗 口 S 的 子 窗 口 , 命 令 X:show()<br />

则 是 向 X 发 布 命 令 show(), 意 思 是 显 示 出 来 。 这 说 明 , 创 建 存 在 的 窗 口 实 体 , 不 一 定 是 显<br />

示 出 来 的 , 我 们 可 以 控 制 其 状 态 。 下 面 我 们 就 利 用 onPaint 在 canvas 里 绘 图 。<br />

第 8.1 节 onPainting 相 关 的 话 题<br />

在 上 面 的 基 础 上 , 我 们 在 paintPack 包 里 创 建 模 块 dopaint, 注 意 , 把 “create objects” 选 项<br />

不 选 ( 缺 省 是 选 ), 如 图 8.1.1 所 示 。


图 8.1.1 创 建 dopaint 模 块 , 不 选 “create objects”<br />

% File dopaint.cl<br />

class dopaint<br />

open core<br />

predicates<br />

bkg:(windowGDI).<br />

end class dopaint<br />

%File: dopaint.pro<br />

implement dopaint<br />

open core, vpiDomains<br />

clauses<br />

bkg(W) :-<br />

P= [pnt(0,0), pnt(10,80), pnt(150, 100)],<br />

W:drawPolygon(P).<br />

end implement dopaint<br />

操 作 canvas.frm/Edit/Events/Paintresponder/onPaint, 如 图 8.1.2 所 示 增 加 代 码 :<br />

predicates<br />

onPaint : drawWindow::paintResponder.<br />

clauses<br />

onPaint(_Source, _Rectangle, GDIObj) :-<br />

dopaint::bkg(GDIObj).<br />

运 行 一 下 , 看 到 如 图 8.1.3 所 示 :


图 8.1.3 画 出 三 角 形<br />

我 们 来 看 看 它 是 怎 么 绘 图 的 , 首 先 P= [pnt(0,0), pnt(10,80), pnt(150, 100)], 形 成 一 个 点 位 置<br />

构 成 的 表 , 然 后 W:drawPolygon(P) 按 照 P 的 规 定 来 画 出 多 边 形 图 形 。 如 果 我 们 要 把 三 角 形 绘<br />

成 红 色 的 , 就 要 进 一 步 改 进 程 序 。 这 时 我 们 用 到 brush/2:<br />

brush = brush( patStyle PatStyle, color Color).<br />

这 里 PatStyle 有 以 下 选 择 :<br />

◆pat_Solid: 满 色 实 心 填 充<br />

◆pat_Horz: 水 平 虚 线 填 充<br />

◆pat_Vert: 垂 直 虚 线 填 充<br />

◆pat_FDiag: 45 度 下 斜 线 填 充<br />

而 Color 有 color_red、color_blue、color_green 等 等 可 以 选 择 。 我 们 在 dopaint 里 修 改 绘 图 谓<br />

词 bkg 如 下 :<br />

bkg(W) :-<br />

Brush= brush(pat_Solid, color_Red),<br />

P= [pnt(0,0), pnt(10,80), pnt(150, 100)],<br />

W:setBrush(Brush),<br />

W:drawPolygon(P).<br />

得 到 图 8.1.4, 而 PatStyle 改 变 后 :<br />

图 8.1.4 红 色 和 更 改 PatStyle=FDiag 后 的 图 形<br />

我 们 希 望 画 一 个 椭 圆 , 并 把 一 个 矩 形 面 积 用 绿 色 涂 满 如 图 8.1.5 所 示 , 需 要 修 改 如 下 :<br />

bkg(W) :-<br />

R= rct(40, 60, 150, 200),<br />

W:drawEllipse(R),


R1= rct( 60, 90, 140, 130),<br />

W:clear(R1, color_Green).<br />

图 8.1.5 更 为 复 杂 的 操 作<br />

我 们 还 可 以 调 入 图 片 , 比 如 我 们 把 frog.bmp 放 在 paintPack 目 录 下 , 我 们 用 下 面 代 码 把 它 显<br />

示 在 (10,10) 开 始 处 ( 图 片 左 上 角 位 置 ):<br />

bkg(W) :-<br />

P= vpi::pictLoad("..\\paintPack\\frog.bmp"),<br />

W:pictDraw(P, pnt(10, 10), rop_SrcCopy).<br />

图 8.1.6 显 示 青 蛙<br />

这 里 pictLoad/1 起 到 图 片 载 入 功 能 , 载 入 内 存 后 , 图 片 的 代 码 是 P, 这 是 机 器 内 部 的 一 种 标<br />

识 , 绘 图 时 只 需 要 指 明 图 片 的 标 识 。<br />

图 8.1.7 鹰 的 黑 背 景 图 和 白 背 景 黑 的 剪 影 图<br />

我 们 把 鹰 显 示 在 青 蛙 上 面 , 如 图 8.1.7 所 示 , 是 为 了 显 示 不 带 背 景 的 鹰 的 图 案 。 要 去 掉 背 景<br />

必 须 采 取 这 样 的 方 法 , 先 制 作 一 个 黑 背 景 的 鹰 eagleBlackBack.bmp, 然 后 制 作 一 个 白 背 景 的<br />

鹰 外 形 的 黑 色 剪 影 图 eagleMaskWhiteBack.bmp, 通 过 先 把 剪 影 图 eagleMaskWhiteBack.bmp<br />

用 rop_SrcAnd 画 出 , 然 后 用 rop_SrcInvert 画 出 鹰 eagleBlackBack.bmp。 如 果 两 个 动 作 次 序<br />

相 反 , 则 结 果 就 不 同 了 , 如 图 8.1.8 所 示 。


图 8.1.7 画 出 不 带 背 景 的 鹰<br />

图 8.1.8 次 序 反 了 , 就 不 是 要 的 结 果 了<br />

通 过 定 义 一 个 鼠 标 事 件 来 记 录 鼠 标 点 击 位 置 , 我 们 可 以 通 过 下 面 这 个 程 序 , 实 现 , 鼠 标 点 击<br />

之 处 , 鹰 也 动 来 动 去 。<br />

% dopaint.pro<br />

implement dopaint<br />

open core, vpiDomains<br />

clauses<br />

bkg(W) :- P= vpi::pictLoad("..\\paintPack\\frog.bmp"),<br />

W:pictDraw(P, pnt(10, 10), rop_SrcCopy),<br />

Mask= vpi::pictLoad("..\\paintPack\\eagleMaskWhiteBack.bmp"),<br />

Eagle= vpi::pictLoad("..\\paintPack\\eagleBlackBack.bmp"),<br />

W:pictDraw(Mask,p,rop_SrcAnd),<br />

W:pictDraw(Eagle,p,rop_SrcInvert).<br />

rememberPnt(P):-<br />

p:=P.<br />

class facts<br />

p : pnt:=pnt(40,40).<br />

end implement dopaint<br />

% dopaint.cl<br />

class dopaint<br />

open core<br />

predicates


kg:(windowGDI).<br />

rememberPnt : (vpiDomains :: pnt).<br />

end class dopaint<br />

% canvas.pro 增 加 的 事 件 代 码<br />

predicates<br />

onPaint : drawWindow::paintResponder.<br />

clauses<br />

onPaint(_Source, _Rectangle, GDIObj) :-<br />

dopaint::bkg(GDIObj).<br />

predicates<br />

onMouseDown : drawWindow::mouseDownListener.<br />

clauses<br />

onMouseDown(S, Point, _ShiftControlAlt, _Button):-<br />

dopaint :: rememberPnt(Point),<br />

S : invalidate().<br />

第 8.2 节 Custom Control 的 应 用<br />

上 节 我 们 直 接 在 Form 上 绘 出 了 图 案 , 但 是 通 常 图 案 的 绘 制 , 可 以 用 Custom Control 的 方 式<br />

进 行 。 我 们 创 建 一 个 新 的 工 程 :<br />

Project Name: customcontrol<br />

Object-oriented GUI (pfc/gui)<br />

选 择 File/New in New Package, 进 入 Create Project Item 对 话 框 , 在 左 侧 选 择 Control 标 签 选<br />

项 , 把 控 件 的 名 字 填 作 canvas。Parent Directory 一 项 留 为 空 白 , 然 后 点 击 Create 按 钮 。 如 图 8.2.1<br />

所 示 , 得 到 结 果 如 图 8.2.2 所 示 。


图 8.2.1 创 建 Custom Control<br />

图 8.2.2 创 建 Canvas 后 的 目 录 树<br />

然 后 我 们 Build/Build, 把 canvas 控 件 登 记 入 IDE, 再 选 择 File/New in New Package, 进 入<br />

Create Project Item 对 话 框 , 在 左 侧 选 择 Form 标 签 选 项 , 把 控 件 的 名 字 填 作 Greeting。Parent<br />

Directory 一 项 留 为 空 白 , 然 后 点 击 Create 按 钮 。 在 编 辑 greeting 对 话 框 Form 时 , 我 们 先 如<br />

图 8.2.3 所 示 , 点 击 Controls 对 话 框 的 第 三 排 最 后 一 个 显 示 为 “custom control” 的 按 钮 , 在<br />

greeting 对 话 框 上 画 出 位 置 和 大 小 , 得 到 如 图 8.2.4 所 示 一 个 选 择 对 话 框 , 第 一 个 控 件 就 是<br />

我 们 刚 刚 创 建 的 canvas, 选 择 它 , 得 到 如 图 8.2.5 所 示 ( 大 小 调 整 后 )。<br />

图 8.2.3 选 择 custom control 按 钮<br />

图 8.2.4 选 择 我 们 创 建 的 canvas 控 件


图 8.2.5 在 greeting 对 话 框 里 插 入 控 件 canvas<br />

我 们 同 时 把 OK 按 钮 修 改 为 Hi 按 钮 , 并 改 变 为 普 通 按 钮 , 如 图 8.2.6 所 示 。<br />

图 8.2.6 修 改 OK 按 钮 为 Hi 普 通 按 钮<br />

设 置 Hi 按 钮 点 击 事 件 的 侦 听 器 如 图 8.2.7 所 示 , 并 在 greeting.pro 文 件 里 编 写 代 码 如 后 。<br />

图 8.2.7 设 置 Hi 按 钮 的 点 击 侦 听 器 为 onHiClick<br />

predicates<br />

onHiClick : button::clickResponder.<br />

clauses<br />

onHiClick(_Source) = button::defaultAction :-<br />

W= canvas_ctl :getVPIWindow(),<br />

X= convert(integer, math::random(100)),<br />

Y= convert(integer, math::random(100)),<br />

vpi::drawText(W, X , Y, "Hello!").


对 任 务 菜 单 项 File/New 进 行 修 改 使 之 可 用 , 并 用 Code Expert 增 加 代 码 到 taskWindow.pro 文<br />

件 里 如 下 , 运 行 图 如 图 8.2.8 所 示 。 按 下 Hi 按 钮 , 在 指 定 区 域 显 示 Hello, 位 置 是 随 机 的 ,<br />

但 是 不 会 超 出 我 们 画 的 边 界 , 这 个 边 界 只 有 在 编 辑 的 时 候 看 得 到 , 并 不 显 示 。<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

F= greeting::new(S),<br />

F:show().<br />

图 8.2.8 按 下 Hi 按 钮 , 显 示 Hello!<br />

第 九 章 数 据 类 型<br />

目 前 应 用 的 高 级 语 言 , 因 为 用 到 定 义 化 的 数 据 类 型 , 可 以 高 效 的 工 作 。<strong>Visual</strong> <strong>Prolog</strong> 编 译 器<br />

首 先 要 检 查 数 据 类 型 是 否 和 谓 词 或 者 函 数 的 预 先 定 义 内 容 相 符 ,x+y、x-y、a×b、p÷q 等 ,<br />

这 些 算 式 可 以 工 作 于 整 数 、 实 数 、 或 者 复 数 情 况 , 你 要 确 信 你 输 入 的 数 据 符 合 这 些 要 求 , 倘<br />

若 程 序 接 到 字 符 串 作 为 输 入 , 就 会 发 生 逻 辑 错 误 。 并 非 所 有 的 高 级 语 言 的 编 译 器 检 查 这 些 逻<br />

辑 错 误 , 但 是 <strong>Visual</strong> <strong>Prolog</strong> 确 实 这 样 做 了 , 从 而 避 免 错 误 的 机 会 提 前 了 。 这 要 求 我 们 对 数 据<br />

类 型 有 一 个 基 本 的 认 识 。<br />

第 9.1 节 原 始 数 据 类 型<br />

<strong>Visual</strong> <strong>Prolog</strong> 有 许 多 基 本 的 数 据 类 型 :<br />

integer ( 整 数 ): 3、45、 65、 34、 0x0000FA1B、 845 等 等 , 这 些 都 是 整 数 。 需 要 说<br />

“0x0000FA1B” 是 一 个 十 六 进 制 的 整 数 ,16 进 制 的 数 位 可 以 是 1、2、3、4、5、6、7、8、<br />

9、A、B、C、D、E、F。<br />

real ( 实 数 ): 3.45、3.1416、2.18E-18、1.6E-19 等 等 都 是 实 数 的 具 体 例 子 。<br />

string ( 字 符 串 ): "pencil"、"John McKnight"、"Cornell University" 带 西 文 引 号 的 一 串 字 符 。<br />

symbol ( 符 号 ): Na、Na_3、K、Kalium, 英 文 字 母 开 头 、 可 打 印 字 符 构 成 的 连 续 字 符 串 。<br />

符 号 看 起 来 和 字 符 串 很 近 似 , 但 是 它 们 的 存 储 格 式 不 同 。 符 号 可 以 放 在 检 索 表 里 ,<strong>Visual</strong>


<strong>Prolog</strong> 可 以 根 据 它 们 的 存 储 地 址 来 替 代 它 , 当 你 的 程 序 里 反 复 用 到 这 个 符 号 时 , 会 节 省 很 多<br />

存 储 内 存 。 下 面 的 例 子 展 示 如 何 用 到 这 几 个 类 型 。 运 行 结 果 如 图 9.1.1 所 示 。<br />

implement main<br />

open core<br />

constants<br />

className = "main".<br />

classVersion = "primtype1".<br />

clauses<br />

classInfo(className, classVersion).<br />

class predicates<br />

energy:(real, real, real) procedure (i, i, o).<br />

family:(symbol, string) procedure (i, o).<br />

clauses<br />

energy(N, Z, E) :- E= -2.18E-19*Z*Z/(N*N).<br />

family("Na", "alkaline metal") :- !.<br />

family(_, "unknown").<br />

run():- console::init(),<br />

energy(4, 1, E1),<br />

energy(2, 1, E2),<br />

DeltaE= E1-E2,<br />

stdio::write(DeltaE), stdio::nl,<br />

family("Na", F),<br />

stdio::write("Na", " is an ", F), stdio::nl,<br />

_ = stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 9.1.1 计 算 显 示 不 同 的 数 据 类 型<br />

第 9.2 节 集 合<br />

集 合 , 是 一 群 东 西 的 总 称 。 把 具 有 某 些 共 同 特 征 的 数 据 , 放 在 一 起 , 可 以 简 称 为 它 们 的 集 合 ,<br />

这 可 以 更 方 便 地 描 述 。 所 以 集 合 , 是 描 述 重 复 事 物 的 简 单 方 法 。<br />

第 9.3 节 数 字 的 集 合<br />

我 们 可 以 用 N 自 然 数 集 合 来 描 述 无 穷 无 尽 的 自 然 数 ,{0, 1, 2, … N, N+1…}。 用 Z 来 描 述 整


数 集 合 {… -N-1, -N, -N+1, …., -2, -1,0, 1, 2, … N, N+1…}。 有 了 集 合 的 概 念 , 我 们 可 以 写 出<br />

类 似 “3∈N” 来 表 达 “3 属 于 整 数 ” 这 样 的 论 述 。 我 们 创 建 一 个 程 序 来 展 示 一 个 表 的 描 述 ,<br />

在 <strong>Visual</strong> <strong>Prolog</strong> 里 , 表 , 就 是 近 似 于 集 合 的 概 念 。 注 意 这 里 tuple(integer,integer) 是 已 经 定<br />

义 好 的 数 据 类 型 , 如 果 改 用 其 他 , 则 不 可 。<br />

implement main<br />

open core<br />

clauses<br />

classInfo("main", "zermelo").<br />

run():-<br />

console::init(),<br />

Q= [tuple(X, Y) || X= std::fromTo(1, 2),<br />

Y= std::fromTo(1, 3)],<br />

stdio::nl,<br />

stdio::write(Q), stdio::nl, stdio::nl,<br />

foreach tuple(Num, Den)= list::getMember_nd(Q) do<br />

stdio::write(Num, "/", Den,", ")<br />

end foreach,<br />

stdio::nl,<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 9.3.1 展 示 一 个 自 动 形 成 的 表 , 及 其 操 作<br />

如 果 我 们 自 己 定 义 一 个 数 据 类 型 , 也 是 可 以 的 。 注 意 , 我 们 增 加 了 数 据 定 义 :<br />

domains<br />

fty=fty(integer, integer).<br />

程 序 改 变 后 如 下 所 示 :<br />

implement main<br />

open core<br />

domains<br />

fty=fty(integer, integer).<br />

clauses<br />

classInfo("main", "zermelo").<br />

run():-<br />

console::init(),


Q= [fty(X, Y) || X= std::fromTo(1, 2),<br />

Y= std::fromTo(1, 3)],<br />

stdio::nl,<br />

stdio::write(Q), stdio::nl, stdio::nl,<br />

foreach fty(Num, Den)= list::getMember_nd(Q) do<br />

stdio::write(Num, "/", Den,", ")<br />

end foreach,<br />

stdio::nl,<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 9.3.2 自 己 定 义 的 数 据 类 型 , 也 是 可 以 的<br />

第 9.4 节 实 数<br />

实 数 是 带 小 数 点 的 数 。 比 如 :3.1415926、100.34 等 等 , 在 <strong>Visual</strong> <strong>Prolog</strong> 里 它 用 real 表 示 。<br />

而 整 数 integer 的 范 围 是 -2147483648 ~ 2147483647 之 间 的 一 个 数 。string 字 符 串 类 型 必 须<br />

是 引 号 内 的 可 显 示 字 符 序 列 , 注 意 这 里 引 号 是 ASCII 码 引 号 , 中 文 引 号 是 不 能 识 别 的 。Char<br />

是 字 符 的 类 型 代 码 , 代 表 ’a’、’B’ 等 等 这 样 单 个 字 符 , 往 往 可 以 用 单 引 号 引 起 来 。<br />

第 9.5 节 格 式<br />

格 式 , 是 为 了 输 出 时 候 , 以 比 较 理 想 的 形 式 输 出 基 本 的 数 据 类 型 。 我 们 通 常 用 string::format/n<br />

来 描 述 希 望 的 格 式 , 前 面 我 们 介 绍 一 些 。 比 如 颜 色 的 代 码 是 十 六 进 制 数 据 , 比 如 :0x00FF0000<br />

(red 红 )、 0x0000FF00 (green 绿 ), and 0x000000FF (blue 蓝 ), 这 三 个 原 色 的 代 码 , 如 果 直<br />

接 拿 write 来 显 示 , 就 会 显 示 成 10 进 制 , 但 是 如 果 我 们 用 format 加 以 格 式 化 , 就 可 以 正 确<br />

显 示 了 。 演 示 程 序 如 下 所 示 , 运 行 结 果 如 图 9.5.1 所 示 。 注 意 , 输 入 的 时 候 , 必 须 用 引 号 把<br />

symbol 引 起 来 , 否 则 出 错 ; 如 果 用 stdio::eradline() 来 读 取 , 因 为 读 入 的 是 串 , 必 须 转 换 为<br />

symbol 才 可 以 。<br />

implement main<br />

open core<br />

constants<br />

className = "main".<br />

classVersion = "primtype2".<br />

clauses<br />

classInfo(className, classVersion).<br />

class predicates


color:(symbol, integer) procedure (i, o).<br />

clauses<br />

color("red", 0x00FF0000) :- !.<br />

color("blue", 0x000000FF) :- !.<br />

color("green", 0x0000FF00) :- !.<br />

color(_, 0x0).<br />

run():- console::init(),<br />

hasdomain(symbol , Name ),<br />

Name = console::read(),<br />

color(Name, C),<br />

S= string::format("%x\n", C),<br />

stdio::write(Name,"\t", C, "\tHex number : ", S), stdio::nl,<br />

run().<br />

%_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 9.5.1 有 格 式 和 没 有 格 式 的 区 别<br />

注 意 一 下 代 码 的 意 义 , 大 家 可 以 在 <strong>Visual</strong> <strong>Prolog</strong> 环 境 下 一 试 。<br />

S= string::format("Pi=%4.3f\n", 3.14159) S=”Pi= 3.142”<br />

S= string::format("%4.3e\n", 33578.3) S= “3.358e+004”<br />

S= string::format("Fac(%d)=%d", 5, 120) S= "Fac(5)=120"<br />

S= string::format("%s(%d)=%d", "f", 5, 120) S= "f(5)=120"<br />

第 9.6 节 域<br />

我 们 在 <strong>Visual</strong> <strong>Prolog</strong> 里 可 以 定 义 自 己 的 数 据 类 型 , 而 这 个 类 型 定 义 , 就 在 domains 字 段 里 进<br />

行 , 也 称 为 “ 域 ” 声 明 、“ 域 ” 定 义 。<br />

第 9.6.1 节 表<br />

我 们 定 义 表 , 是 在 基 本 类 型 后 面 增 加 “*” 星 号 来 表 示 的 :<br />

domains


eals= real*. % a list of reals<br />

integers= integer*. % a list of integers<br />

strings= string*. % a list of strings<br />

rr= reals*. % a list of lists of reals<br />

这 里 我 们 就 定 义 了 几 个 数 据 类 型 :reals、integers、strings、rr。 注 意 , 虽 然 reals 和 rr 看 似 一<br />

样 , 但 是 , 它 们 属 于 不 同 的 类 型 , 如 果 混 用 , 就 会 在 类 型 检 查 时 出 错 。<br />

第 9.6.2 节 函 子<br />

而 函 数 型 数 据 类 型 , 在 这 里 被 称 为 函 数 型 符 号 , 也 称 为 函 子 。 例 如 :<br />

author("Wellesley", "Barros", 1970)<br />

book( author("Peter", "Novig", 1960),<br />

"Artificial Intelligence")<br />

date("July", 15, 1875)<br />

entry("<strong>Prolog</strong>", "A functional language")<br />

index("Functors", 165)<br />

sunday<br />

这 些 都 可 以 称 为 函 子 。 我 们 可 以 这 么 定 义 数 据 类 型 :<br />

domains<br />

year= integer.<br />

day= sun; mon;tue; wed; thu; fri; sat; err.<br />

month= jan;feb;mar;apr;may;jun;jul;aug;sep;oct;nov;dec.<br />

month_code= m(month, integer).<br />

numberOfDays= nd(integer); february.<br />

我 们 设 计 一 个 计 算 某 一 天 是 星 期 几 的 程 序 , 计 算 December 15, 2008 是 星 期 几 , 运 行 结 果 如<br />

图 9.6.1 所 示 。 程 序 如 下 :<br />

implement main<br />

open core<br />

constants<br />

className = "main".<br />

classVersion = "dayOfWeek".<br />

domains<br />

year= integer.<br />

day= sun; mon;tue; wed; thu; fri; sat; err.<br />

month= jan;feb;mar;apr;may;jun;jul;aug;sep;oct;nov;dec.<br />

month_code= m(month, integer).<br />

numberOfDays= nd(integer); february.<br />

class predicates<br />

dayOfWeek:(integer, day) procedure (i, o).<br />

calculateDay:( string Month,


integer Day_of_the_month,<br />

year Y,<br />

day D) procedure (i, i, i, o).<br />

class facts<br />

monthCode:(string, month_code, numberOfDays).<br />

clauses<br />

classInfo(className, classVersion).<br />

monthCode("January", m(jan, 6), nd(31)).<br />

monthCode("February", m(feb, 2), february ).<br />

monthCode("March", m(mar, 2), nd(31)).<br />

monthCode("April", m(apr, 5), nd(30)).<br />

monthCode("May", m(may, 0), nd(31)).<br />

monthCode("June", m(jun, 3), nd(30)).<br />

monthCode("July", m(jul, 5), nd(31)).<br />

monthCode("August", m(aug, 1), nd(31)).<br />

monthCode("September", m(sep, 4), nd(30)).<br />

monthCode("October", m(oct, 6), nd(31)).<br />

monthCode("November", m(nov, 2), nd(30)).<br />

monthCode("December", m(dec, 4), nd(31)).<br />

dayOfWeek(0, sun) :- !.<br />

dayOfWeek(1, mon) :- !.<br />

dayOfWeek(2, tue) :- !.<br />

dayOfWeek(3, wed) :- !.<br />

dayOfWeek(4, thu) :- !.<br />

dayOfWeek(5, fri) :- !.<br />

dayOfWeek(6, sat) :- !.<br />

dayOfWeek(_, err).<br />

calculateDay(Month, MD, Year, D) :-<br />

Y= Year-2000, % Last digits of Year.<br />

monthCode(Month, m(_M, C), _), % Get month code.<br />

!,<br />

S= Y+Y div 4+C+MD,<br />

R= S mod 7, /* Day of the week as an integer<br />

between 0 (sunday) and 6 (saturday) */<br />

dayOfWeek(R, D). % Get functional symbol of the day<br />

calculateDay(_, _, _, err).<br />

run():-<br />

console::init(),<br />

calculateDay("December", 15, 2008, R),<br />

stdio::write("December 15, 2008\nthe day of the week is : ", R), stdio::nl,<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).


图 9.6.1 正 确 计 算 出 2008 年 12 月 5 日 是 星 期 一<br />

经 过 我 们 改 造 , 可 以 不 断 输 入 年 月 日 格 式 ” 2008-12-15”( 引 号 年 - 月 - 日 , 中 间 用 连 字 符 ), 改<br />

造 后 的 程 序 如 下 , 运 行 画 面 见 9.6.2 所 示 。<br />

implement main<br />

open core<br />

constants<br />

className = "main".<br />

classVersion = "dayOfWeek".<br />

domains<br />

year= integer.<br />

day= sun; mon;tue; wed; thu; fri; sat; err.<br />

month= jan;feb;mar;apr;may;jun;jul;aug;sep;oct;nov;dec.<br />

month_code= m(month, integer).<br />

numberOfDays= nd(integer); february.<br />

class predicates<br />

dayOfWeek:(integer, day) procedure (i, o).<br />

calculateDay:( string Month,<br />

integer Day_of_the_month,<br />

year Y,<br />

day D) procedure (i, i, i, o).<br />

class facts<br />

monthCode:(string, month_code, numberOfDays).<br />

clauses<br />

classInfo(className, classVersion).<br />

monthCode("January", m(jan, 6), nd(31)).<br />

monthCode("February", m(feb, 2), february ).<br />

monthCode("March", m(mar, 2), nd(31)).<br />

monthCode("April", m(apr, 5), nd(30)).<br />

monthCode("May", m(may, 0), nd(31)).<br />

monthCode("June", m(jun, 3), nd(30)).<br />

monthCode("July", m(jul, 5), nd(31)).<br />

monthCode("August", m(aug, 1), nd(31)).<br />

monthCode("September", m(sep, 4), nd(30)).<br />

monthCode("October", m(oct, 6), nd(31)).<br />

monthCode("November", m(nov, 2), nd(30)).<br />

monthCode("December", m(dec, 4), nd(31)).<br />

dayOfWeek(0, sun) :- !.<br />

dayOfWeek(1, mon) :- !.<br />

dayOfWeek(2, tue) :- !.<br />

dayOfWeek(3, wed) :- !.<br />

dayOfWeek(4, thu) :- !.


dayOfWeek(5, fri) :- !.<br />

dayOfWeek(6, sat) :- !.<br />

dayOfWeek(_, err).<br />

calculateDay(Month, MD, Year, D) :-<br />

Y= Year-2000, % Last digits of Year.<br />

monthCode(Month, m(_M, C), _), % Get month code.<br />

!,<br />

S= Y+Y div 4+C+MD,<br />

R= S mod 7, /* Day of the week as an integer<br />

between 0 (sunday) and 6 (saturday) */<br />

dayOfWeek(R, D). % Get functional symbol of the day<br />

calculateDay(_, _, _, err).<br />

class predicates<br />

get_date : (string, string, integer, integer) procedure (i,o,o,o).<br />

clauses<br />

get_date(S, M, D, Y):-<br />

A=string::split_delimiter(S,"-"),<br />

A = [Y1, M0,D1],<br />

string::front(Y1, 1,_,Y0),<br />

string::front(D1, string::length(D1)-1,D0,_),<br />

Y=toterm(Y0),<br />

D=toterm(D0),<br />

hasdomain(core::positive, X),<br />

X = toterm(M0),<br />

M=list::nth(X, ["X", "January", "February", "March",<br />

"April", "May", "June", "July", "August",<br />

"September", "October", "November", "December"]),<br />

!.<br />

get_date(_, "December", 15, 2008) .<br />

run():-<br />

console::init(),<br />

DateStr=stdio::readline(),<br />

get_date(DateStr,M, D, Y),<br />

calculateDay(M, D, Y, R),<br />

stdio::write(Y, " ", M, " ",D, " \nthe day of the week is : ", R), stdio::nl,<br />

!,<br />

run().<br />

%_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).


图 9.6.2 改 造 后 的 计 算 日 期 界 面<br />

第 十 章 如 何 用 <strong>Prolog</strong> 解 决 问 题<br />

这 一 章 , 我 们 展 示 , 如 何 用 <strong>Prolog</strong> 解 决 问 题 , 我 们 先 提 出 问 题 , 然 后 提 出 解 决 办 法 , 并 展<br />

示 解 决 过 程 , 目 的 是 让 读 者 更 好 认 识 <strong>Prolog</strong> 是 怎 么 解 决 问 题 的 。 我 们 这 里 用 到 的 例 子 , 全<br />

部 是 基 于 控 制 台 (Console) 程 序 , 避 免 了 图 形 界 面 的 许 多 不 必 要 的 界 面 设 置 , 直 接 触 及 <strong>Prolog</strong><br />

核 心 逻 辑 。 我 们 创 建 solveIn<strong>Prolog</strong> 工 程 如 图 10.1, 在 这 个 基 础 上 进 行 尝 试 。 建 立 之 后 , 然<br />

后 Build/Build 一 下 , 以 后 修 改 main.pro 文 件 内 容 即 可 。<br />

图 10.1 建 立 一 个 solveIn<strong>Prolog</strong> 工 程 , 选 择 Console 模 式<br />

第 10.1 节 搜 索 一 个 表 的 所 有 元 素<br />

这 个 问 题 相 对 简 单 , 我 们 利 用 表 的 描 述 [Head|Tail] 的 特 性 来 即 可 。 程 序 如 下 :<br />

implement main<br />

open core<br />

class predicates<br />

member:(integer, integer*) nondeterm anyflow.<br />

member:(string, string*) nondeterm anyflow.<br />

test:(string*) procedure (i).<br />

test:(integer*) procedure (i).<br />

clauses


classInfo("main", "searchList").<br />

member(H, [H|X]):-stdio::write("\n\t[",H,"|", X,"]").<br />

member(H, [X|T]) :- member(H, T),stdio::write("\t[",X,"|", T,"]").<br />

test(L) :-<br />

stdio::write("\n", L, "============>"),<br />

member(H, L),<br />

stdio::write("\n\t", H),<br />

fail<br />

or<br />

succeed().<br />

run():-<br />

console::init(),<br />

L= [2,3,4,5], test(L),<br />

stdio::nl,<br />

S= ["a", "b", "c"], test(S),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 10.1.1 搜 索 表 中 所 有 元 素 的 过 程<br />

注 意 , 这 里 在 声 明 谓 词 时 ,member/2、test/1 都 声 明 了 两 次 , 为 了 适 应 参 数 为 integer 和 string<br />

两 种 类 型 。 计 算 显 示 , 我 们 特 意 设 置 了 把 推 理 过 程 显 示 出 来 , 在 每 个 元 素 的 上 方 , 是 推 理 过<br />

程 , 推 理 过 程 是 倒 排 次 序 打 印 在 屏 幕 上 的 。 比 如 寻 找 [2,3,4,5] 的 第 四 个 元 素 “5”, 就 经 历 了<br />

4 次 尝 试 , 才 找 到 :(1) 尝 试 [2,3,4,5] 的 头 , 结 果 “2” 是 已 经 有 的 解 ;(2) 尝 试 [3,4,5] 的 头 ,<br />

结 果 “3” 也 是 已 经 有 的 解 ;(3) 尝 试 [4,5] 的 头 , 结 果 “4” 也 是 已 经 有 的 解 ;(4) 尝 试 [5]<br />

的 头 , 结 果 “5” 是 不 曾 有 的 解 , 成 功 。<br />

程 序 找 到 4 个 解 之 后 还 要 尝 试 寻 找 第 五 个 解 , 但 是 失 败 了 , 程 序 就 终 止 了 , 不 再 寻 求 更 多 的<br />

解 。 所 以 <strong>Visual</strong> <strong>Prolog</strong> 是 不 断 尝 试 的 过 程 , 不 断 自 我 验 证 的 过 程 。 寻 找 所 有 解 , 和 test/1 里


用 到 的 fail 有 关 。member/2 要 定 义 成 nondeterm 的 , 表 示 可 能 有 多 个 解 。<br />

第 10.2 节 寻 求 两 个 表 的 交 集 表<br />

如 果 一 个 元 素 属 于 表 L1, 同 时 它 也 属 于 表 L2, 那 么 它 就 是 这 两 个 表 共 同 的 元 素 , 它 们 共 同<br />

的 元 素 组 成 的 表 , 称 为 它 们 的 交 集 表 。 我 们 用 下 面 的 程 序 解 答 。<br />

implement main<br />

open core, stdio<br />

class predicates<br />

isec:(integer*, integer*, integer*) nondeterm anyFlow.<br />

memb:(integer, integer*) nondeterm anyFlow.<br />

tst:(integer*, integer*, integer*, string) procedure(i, i, i, o).<br />

findsec:(integer*, integer*, integer*) procedure (i, i, o).<br />

length:(integer*, integer) procedure (i, o).<br />

clauses<br />

classInfo("main", "intersection").<br />

memb(H, [H1|T]) :- H=H1; memb(H, T).<br />

isec(L1, L2, [H|U]) :- memb(H, L1), memb(H, L2), !,<br />

isec(L1, L2, U).<br />

isec(_, _, []).<br />

length([], 0) :- !.<br />

length([_|R], L+1) :- length(R, L).<br />

tst(L1, L2, U, R) :-<br />

findsec(L1, L2, S),<br />

length(U, LU), length(S, LS), LU= LS,<br />

isec(L1, L2, U), !, R= "yes"; R= "no".<br />

findsec([H|T], L, [H|U]) :-<br />

memb(H, L),<br />

write("\n\t","find ", H, " as a member ", [H|T]," ", L),<br />

!, findsec(T, L, U).<br />

findsec([X|T], L, U) :-<br />

write("\nfind ", X, " is not, then go to ... ","\t", T," ", L),<br />

findsec(T, L, U),!.<br />

findsec(_L1, _L2, []):-write("\n End search\n").<br />

run():-<br />

console::init(), L1= [3, 6, 4, 5],<br />

L2= [4, 5, 6], U1= [4, 6, 5], U2= [4, 3],<br />

tst(L1, L2, U2, Resp2), tst(L1, L2, U1, Resp1),<br />

write("\nDoes ",L1," and ", L2, " have intersection list of ",<br />

U2, " ? ", "\nAnswer = ", Resp1),<br />

write("\nDoes ",L1," and ", L2, " have intersection list of ",<br />

U1, " ? ", "\nAnswer = ", Resp2), nl,<br />

findsec(L1, L2, I),<br />

write("\nI have found ",L1," and ", L2, " have intersection list of ", I,"\n"),<br />

nl,<br />

_=readline().


end implement main<br />

goal<br />

mainExe::run(main::run).<br />

运 行 结 果 显 示 如 图 10.2.1 所 示 。 可 见 搜 索 交 集 的 过 程 , 就 是 拿 第 一 个 表 的 元 素 , 挨 个 地 询 问 ,<br />

它 是 否 属 于 第 二 个 表 ? 是 , 则 进 入 交 际 表 中 , 否 则 忽 略 该 元 素 , 进 行 下 一 个 , 直 至 表 的 所 有<br />

元 素 都 被 尝 试 过 。<br />

图 10.2.1 搜 索 表 的 交 集 过 程 。<br />

第 10.3 节 两 个 表 的 合 并<br />

把 两 个 表 的 元 素 合 在 一 起 。 程 序 如 下 所 示 , 注 意 app/3 的 定 义 语 句 是 这 样 写 的<br />

“app :(Elem* L1, Elem* L2, Elem* L3) nondeterm anyFlow. ”。 这 里 Elem* 是 一 个 预 定 义 的<br />

类 型 , 适 合 于 各 种 类 型 的 元 素 。<br />

implement main<br />

open core<br />

class predicates<br />

app:(Elem* L1, Elem* L2, Elem* L3)<br />

nondeterm anyFlow.<br />

test1:(string*) procedure (i).<br />

test2:(integer*, integer*) procedure (i, i).<br />

clauses<br />

classInfo("main", "append").<br />

app([], L, L).<br />

app([H|T], L, [H|U]) :- app(T, L, U).<br />

test1(U) :-<br />

stdio::write("\n",U,"\n"),<br />

app(L1, L2, U),<br />

stdio::write(L1, " ++ ", L2, " \t\t= ", U), stdio::nl, fail.<br />

test1(_).<br />

test2(L1, L2) :-


app(L1, L2, U),<br />

stdio::write(L1, " ++ ", L2, " = ", U), stdio::nl, fail.<br />

test2(_, _).<br />

run():- console::init(),<br />

test1(["a", "b", "c", "d"]), stdio::nl,<br />

test2([1, 2, 3], [4, 5, 6]),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 10.3.1 两 表 的 合 并 , 体 验 其 过 程<br />

第 10.4 节 表 的 倒 序 排 列<br />

同 样 利 用 表 的 [Head|Tail] 特 点 , 可 以 很 容 易 把 表 倒 过 来 排 列 , 代 码 见 下 , 运 行 结 果 见 图 10.4.1。<br />

/*******************************<br />

Project Name: reverse<br />

UI Strategy: console<br />

********************************/<br />

implement main<br />

open core<br />

class predicates<br />

rev:(integer_list, integer_list) procedure (i, o).<br />

rev1:(integer_list, integer_list, integer_list) procedure (i, i, o).<br />

clauses<br />

classInfo("main", "reverse").<br />

rev1([], L, L) :- !.<br />

rev1([H|T], L1, L) :- rev1(T, [H|L1], L).<br />

rev(L, R) :- rev1(L, [], R).<br />

run():-<br />

console::init(),<br />

hasdomain(integer_list, L),<br />

L=console::read(),<br />

rev(L, R), stdio::write("\n Reverse of ", L, " is \n\t ", R),<br />

stdio::nl,


un().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 10.4.1 输 入 一 个 整 数 表 , 得 到 其 倒 序 排 列 表<br />

如 果 对 rev1/3 进 行 适 当 改 造 如 下 代 码 , 可 以 显 示 过 程 , 如 图 10.4.2 所 示 。<br />

rev1([], L, L) :- !.<br />

rev1([H|T], L1, L) :-<br />

stdio::write("\n",[H|T], "---> ", [H|L1]),<br />

rev1(T, [H|L1], L) .<br />

rev(L, R) :- rev1(L, [], R).<br />

图 10.4.2 显 示 倒 序 排 列 的 创 造 过 程


第 10.5 节 表 的 分 割<br />

我 们 假 设 有 这 样 的 需 求 , 按 一 定 的 标 准 , 把 一 个 表 分 成 两 个 。 下 面 的 例 子 是 用 split/4 把 一 个<br />

表 分 成 大 的 、 小 的 两 个 子 表 。 程 序 如 下 , 运 行 推 演 过 程 如 图 10.5.1 所 示 。 可 见 这 个 定 义 适 用<br />

于 数 值 表 或 者 字 符 串 表 。<br />

implement main<br />

open core<br />

clauses<br />

classInfo("main", "quicksort").<br />

class predicates<br />

split:(E, E*, E*, E*) procedure (i, i, o, o).<br />

clauses<br />

split(P, [H|T], S, [H|B]) :-<br />

H>P, !,<br />

split(P, T, S, B),<br />

stdio::write("\n(", H, ">",P,") [",H,"|", T, "] Small",S, "Big", B).<br />

split(P, [H|T], [H|S], B) :- !,<br />

split(P, T, S, B),<br />

stdio::write("\n(", H,"


第 10.6 节 表 的 快 速 排 序<br />

基 于 上 边 我 们 设 计 的 把 表 分 成 大 小 两 个 split/4 以 及 append/3, 我 们 可 以 设 计 一 个 快 速 表 排 序<br />

的 程 序 sort/2: 首 先 按 照 第 一 个 元 素 , 用 split/4 把 表 分 成 大 小 两 个 表 ; 然 后 分 别 用 sort./2 对<br />

这 两 个 表 进 行 排 序 , 再 用 append/3 把 大 小 两 个 排 好 序 的 表 合 并 起 来 。 程 序 如 下 , 推 演 过 程<br />

及 运 行 结 果 如 图 10.6.1 所 示 。 这 个 适 用 于 数 字 表 和 文 字 表 等 各 种 可 以 比 较 大 小 的 表 。<br />

implement main<br />

open core<br />

clauses<br />

classInfo("main", "quicksort").<br />

class predicates<br />

split:(E, E*, E*, E*) procedure (i, i, o, o).<br />

app:(E*, E*, E*) procedure (i, i, o).<br />

sort:(E*, E*) procedure (i, o).<br />

clauses<br />

split(P, [H|T], S, [H|B]) :-<br />

H>P,<br />

!, split(P, T, S, B).<br />

split(P, [H|T], [H|S], B) :-<br />

!, split(P, T, S, B).<br />

split(_, [], [], []).<br />

app([], L, L) :- !.<br />

app([H|T], L, [H|U]) :- app(T, L, U).<br />

sort([P|L], S) :-<br />

!, split(P, L, Small, Big),<br />

stdio::write("\n[", P, "|", L, "] to ",P," S=", Small, " B=", Big),<br />

sort(Small, QSmall), stdio::write("\n>",P," ", Big, " =>", QBig),<br />

app(QSmall, [P|QBig], S).<br />

sort([], []).<br />

run():-<br />

console::init(),<br />

L= [5, 3, 7, 4, 5, 8, 2], stdio::write(L),<br />

sort(L, S), stdio::write("\n after sorted = ", S), stdio::nl,<br />

Strs= ["b", "a", "c"], stdio::write("\n\n",Strs),<br />

sort(Strs, Qs), stdio::write("\n after sorted =", Qs), stdio::nl,<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).


图 10.6.1 快 速 排 序 方 法<br />

第 10.7 节 一 年 有 多 少 天 ?<br />

通 常 一 年 有 365 天 , 但 是 每 四 年 就 会 出 现 闰 年 366 天 。 但 是 每 百 年 不 闰 年 , 每 四 百 年 闰 年 。<br />

程 序 如 下 , 运 行 图 10.7.1 所 示 。<br />

implement main<br />

open core<br />

class predicates<br />

no_of_days_in:(integer, integer) procedure (i, o).<br />

member:(integer, integer_list) nondeterm (o, i).<br />

test:(integer_list) procedure (i).<br />

clauses<br />

classInfo("numdays", "1.0").<br />

member(H, [H|_T]).<br />

member(H, [_|T]) :- member(H, T).<br />

no_of_days_in(Y, 366) :-<br />

0= Y mod 400,<br />

stdio::write(Y, "\n every 400 years "),!.<br />

no_of_days_in(Y, 366) :-<br />

0= Y mod 4,


stdio::write(Y, "\n every 4 years "),<br />

( not(0= Y mod 100),<br />

stdio::write(Y, "\n but not every 100 years "),!<br />

or stdio::write(Y, "\n same time every 100 years "),fail).<br />

no_of_days_in(Y, 365):-stdio::write(Y).<br />

test(L) :-<br />

member(X, L),<br />

no_of_days_in(X, N),<br />

stdio::write("\n Year ", X, " has ", N, " days."),<br />

stdio::nl, fail.<br />

test(_L).<br />

run():-<br />

console::init(),<br />

test([1975, 1900, 1800, 2008, 1976, 2000]),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 10.7.1 计 算 每 年 有 多 少 天<br />

第 10.8 节 地 图 填 色 问 题<br />

一 个 著 名 问 题 是 : 四 种 颜 色 可 以 为 任 意 的 地 图 涂 上 颜 色 , 而 任 何 相 邻 的 地 块 颜 色 都 不 相 同 ,<br />

这 就 是 著 名 的 四 色 定 理 。 我 们 设 计 一 个 程 序 , 假 设 L 表 是 描 述 地 图 上 不 同 块 相 邻 关 系 的 表 ,


其 元 素 用 nb(A,B) 来 描 述 ,A 和 B 相 邻 。 这 样 , 我 们 用 反 复 尝 试 的 方 法 , 为 不 同 的 块 设<br />

定 不 同 的 颜 色 。 下 面 的 程 序 求 得 一 个 解 , 我 们 假 设 有 A、B、C、D、E、F 六 块 地 ; 运 行 结<br />

果 如 图 10.8.1 所 示 。 我 们 可 以 增 加 、 减 少 颜 色 设 置 , 来 看 看 结 果 会 怎 么 样 ? 有 答 案 , 则 L<br />

会 是 颜 色 表 ; 否 则 返 回 空 表 , 表 示 没 有 答 案 。 改 变 颜 色 是 同 步 修 改<br />

colors= blue;yellow;red;green. 和 generateColor(R) :- R= blue; R= yellow; R= green; R= red.<br />

语 句 , 不 再 赘 述 。<br />

implement main<br />

open core, stdio<br />

domains<br />

colors= blue;yellow;red;green.<br />

neighbors= nb(colors, colors).<br />

map= neighbors*.<br />

class predicates<br />

aMap:(map) nondeterm anyFlow.<br />

test:(map) procedure anyFlow.<br />

generateColor:(colors) multi (o).<br />

clauses<br />

classInfo("main", "fourcolors").<br />

generateColor(R) :-<br />

R= blue;<br />

R= yellow;<br />

R= green;<br />

R= red.<br />

aMap([]).<br />

aMap([X|Xs]) :-<br />

X= nb(C1, C2),<br />

not(C1 = C2),<br />

aMap(Xs).<br />

class facts<br />

n : integer :=0.<br />

clauses<br />

test(L) :-<br />

n:=0,<br />

generateColor(A),<br />

generateColor(B),<br />

generateColor(C),<br />

generateColor(D),<br />

generateColor(E),<br />

generateColor(F),<br />

L= [ nb(A, B), nb(A, C), nb(A, E), nb(A, F),<br />

nb(B, C), nb(B, D), nb(B, E), nb(B, F),


nb(C, D), nb(C, F), nb(C, F)],<br />

n:=n+1,<br />

aMap(L) ,!<br />

; L= [].<br />

run():-<br />

console::init(),<br />

test(L),<br />

stdio::write("\n",n ,"\n", L),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 10.8.1 经 过 396 次 组 合 验 证 , 找 到 答 案<br />

我 们 可 以 修 改 程 序 , 看 看 到 底 有 多 少 种 填 色 方 案 。 就 本 题 而 言 , 运 行 结 果 告 诉 我 们 , 共 有<br />

96 种 方 案 。 修 改 后 程 序 如 下 , 运 行 结 果 如 图 10.8.2 所 示 , 显 示 了 每 一 种 答 案 的 尝 试 次 数 ,<br />

显 示 答 案 的 具 体 内 容 如 图 10.8.3 所 示 , 还 要 再 做 少 量 修 改 , 我 们 展 示 了 最 后 两 个 方 案 的 细 节 。<br />

implement main<br />

open core, stdio<br />

domains<br />

colors= blue;yellow;red;green.<br />

neighbors= nb(colors, colors).<br />

map= neighbors*.<br />

class predicates<br />

aMap:(map) nondeterm anyFlow.<br />

test:(map) nondeterm anyFlow. % multi anyFlow. %procedure anyFlow.<br />

generateColor:(colors) multi (o).<br />

clauses<br />

classInfo("main", "fourcolors").<br />

generateColor(R) :-<br />

R= blue;<br />

R= yellow;<br />

R= green;<br />

R= red.<br />

aMap([]).<br />

aMap([X|Xs]) :-<br />

X= nb(C1, C2),<br />

not(C1 = C2),<br />

aMap(Xs).


class facts<br />

n : integer :=0.<br />

k : integer :=0.<br />

clauses<br />

test(L) :-<br />

n:=0,<br />

generateColor(A),<br />

generateColor(B),<br />

generateColor(C),<br />

generateColor(D),<br />

generateColor(E),<br />

generateColor(F),<br />

L= [ nb(A, B), nb(A, C), nb(A, E), nb(A, F),<br />

nb(B, C), nb(B, D), nb(B, E), nb(B, F),<br />

nb(C, D), nb(C, F), nb(C, F)],<br />

%write(n," "),<br />

n:=n+1,<br />

aMap(L) . % ,!<br />

%; L= [].<br />

run():-<br />

console::init(),<br />

k:=0,<br />

test(L),<br />

k:=k+1,<br />

N=string::format("%2 -%3 ",k, n),<br />

stdio::write("\n\n", N ,"\n", L), %), stdio::nl,<br />

n:=0,<br />

fail.<br />

run():-<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 10.8.2 枚 举 出 一 共 96 种 结 果 和 它 们 验 证 的 次 数


图 10.8.3 第 95、96 结 果 的 细 节 显 示<br />

第 10.9 节 翻 子 游 戏<br />

有 6 个 硬 币 , 正 面 朝 上 , 每 次 有 一 个 硬 币 可 以 发 口 令 , 它 自 己 状 态 不 变 , 而 其 他 五 个 得 令 则<br />

状 态 反 转 。 问 怎 样 做 才 能 使 六 个 硬 币 朝 下 ? 下 面 是 程 序 , 运 行 结 果 见 图 10.9.1 所 示 。<br />

implement main<br />

open core, stdio<br />

domains<br />

class predicates<br />

change : (integer*, integer* , integer* ) determ (i,i,o).<br />

who_order : (integer* ) multi (o).<br />

test : () procedure.<br />

clauses<br />

classInfo("main", "turn").<br />

class facts<br />

n : integer :=0.<br />

nowStatus : integer* :=[].<br />

clauses<br />

who_order(A):-<br />

A=[1,0,0,0,0,0];<br />

A=[0,1,0,0,0,0];<br />

A=[0,0,1,0,0,0];<br />

A=[0,0,0,1,0,0];<br />

A=[0,0,0,0,1,0];<br />

A=[0,0,0,0,0,1].<br />

change(B,A,[]):-(A=[];B=[]),!.<br />

change([A|Ra],[B|Rb], [C|X]):-<br />

(B=1,A=1, C=1;<br />

B=1, A=0, C=0;<br />

B=0,A=0, C=1;<br />

B=0, A=1, C=0),<br />

!,<br />

change(Ra,Rb, X).


test():-<br />

Start= [1,1,1,1,1,1],<br />

End=[0,0,0,0,0,0],<br />

stdio::write("\n\t ", Start, " \t => \t", End, "\n"),<br />

nowStatus:=Start,<br />

n:=0,<br />

who_order(A),<br />

change(nowStatus, A, NewStatus),<br />

n:=n+1,<br />

stdio::write("\n", n ,"\t", nowStatus,"\t", A, "\t-->", NewStatus),<br />

nowStatus:=NewStatus,<br />

nowStatus=End, !;<br />

stdio::write("\n There is no solution........." ).<br />

run():-<br />

console::init(),<br />

test(),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 10.9.1 六 次 反 转 就 可 以 实 现 1 代 表 正 面 , 还 代 表 发 口 令 者<br />

第 10.10 节 NIM 游 戏<br />

NIM 游 戏 的 一 种 经 典 表 述 为 :<br />

有 n 堆 火 柴 , 每 堆 各 有 若 干 根 。 两 人 轮 流 取 出 火 柴 , 每 次 取<br />

出 的 根 数 不 限 但 至 少 取 1 根 , 每 次 也 只 能 从 1 堆 里 取 火 柴 。<br />

谁 最 后 把 火 柴 取 完 , 谁 就 是 获 胜 者 。 问 如 何 才 能 保 证 获 胜 ?<br />

获 胜 策 略 已 由 美 国 数 学 家 C.L.Bouton 分 析 完 成 , 用 到 的 是 二 进 制 和 平 衡 状 态 概 念 。 其 结 论<br />

是 :<br />

(1) 如 果 一 开 始 火 柴 的 总 根 数 转 化 成 二 进 制 后 各 位 数 上 的<br />

数 字 和 都 是 偶 数 时 , 则 是 平 衡 状 态 , 后 取 者 必 胜 。 最<br />

简 单 的 平 衡 态 是 (1,1), 即 2 堆 火 柴 , 每 堆 各 1 根 。<br />

(2) 如 果 开 始 时 火 柴 的 状 态 处 于 不 平 衡 状 态 , 先 取 者 必 胜 ,<br />

其 策 略 是 取 完 后 使 火 柴 根 数 保 持 为 平 衡 状 态 。 最 简 单


的 不 平 衡 态 是 (1), 即 1 根 火 柴 。<br />

Nim 游 戏 是 组 合 游 戏 (Combinatorial Games) 的 一 种 , 准 确 来 说 , 属 于 “Impartial Combinatorial<br />

Games”( 以 下 简 称 ICG)。<br />

满 足 以 下 条 件 的 游 戏 是 ICG( 可 能 不 太 严 谨 ):<br />

1、 有 两 名 选 手 ;<br />

2、 两 名 选 手 交 替 对 游 戏 进 行 移 动 (move), 每 次 一 步 , 选 手 可 以 在<br />

( 一 般 而 言 ) 有 限 的 合 法 移 动 集 合 中 任 选 一 种 进 行 移 动 ;<br />

3、 对 于 游 戏 的 任 何 一 种 可 能 的 局 面 , 合 法 的 移 动 集 合 只 取 决 于 这<br />

个 局 面 本 身 , 不 取 决 于 轮 到 哪 名 选 手 操 作 、 以 前 的 任 何 操 作 、<br />

骰 子 的 点 数 或 者 其 它 什 么 因 素 ;<br />

4、 如 果 轮 到 某 名 选 手 移 动 , 且 这 个 局 面 的 合 法 的 移 动 集 合 为 空 ( 也<br />

就 是 说 此 时 无 法 进 行 移 动 ), 则 这 名 选 手 负 。 根 据 这 个 定 义 ,<br />

很 多 日 常 的 游 戏 并 非 ICG。 例 如 象 棋 就 不 满 足 条 件 3, 因 为 红<br />

方 只 能 移 动 红 子 , 黑 方 只 能 移 动 黑 子 , 合 法 的 移 动 集 合 取 决 于<br />

轮 到 哪 名 选 手 操 作 。<br />

通 常 的 Nim 游 戏 的 定 义 是 这 样 的 : 有 若 干 堆 石 子 , 每 堆 石 子 的 数 量 都 是 有 限 的 , 合 法 的 移<br />

动 是 “ 选 择 一 堆 石 子 并 拿 走 若 干 颗 ( 不 能 不 拿 )”, 如 果 轮 到 某 个 人 时 所 有 的 石 子 堆 都 已 经 被<br />

拿 空 了 , 则 判 负 ( 因 为 他 此 刻 没 有 任 何 合 法 的 移 动 )。<br />

这 游 戏 看 上 去 有 点 复 杂 , 先 从 简 单 情 况 开 始 研 究 吧 。 如 果 轮 到 你 的 时 候 , 只 剩 下 一 堆 石 子 ,<br />

那 么 此 时 的 必 胜 策 略 肯 定 是 把 这 堆 石 子 全 部 拿 完 一 颗 也 不 给 对 手 剩 , 然 后 对 手 就 输 了 。 如 果<br />

剩 下 两 堆 不 相 等 的 石 子 , 必 胜 策 略 是 通 过 取 多 的 一 堆 的 石 子 将 两 堆 石 子 变 得 相 等 , 以 后 如 果<br />

对 手 在 某 一 堆 里 拿 若 干 颗 , 你 就 可 以 在 另 一 堆 中 拿 同 样 多 的 颗 数 , 直 至 胜 利 。 如 果 你 面 对 的<br />

是 两 堆 相 等 的 石 子 , 那 么 此 时 你 是 没 有 任 何 必 胜 策 略 的 , 反 而 对 手 可 以 遵 循 上 面 的 策 略 保 证<br />

必 胜 。 如 果 是 三 堆 石 子 „„ 好 像 已 经 很 难 分 析 了 , 看 来 我 们 必 须 要 借 助 一 些 其 它 好 用 的 ( 最<br />

好 是 程 式 化 的 ) 分 析 方 法 了 , 或 者 说 , 我 们 最 好 能 够 设 计 出 一 种 在 有 必 胜 策 略 时 就 能 找 到 必<br />

胜 策 略 的 算 法 。<br />

我 们 要 用 到 表 的 合 并 谓 词 :<br />

implement main<br />

open core<br />

domains<br />

ms= integer*.<br />

hs= ms*.<br />

class predicates<br />

append:(E*, E*, E*) nondeterm anyFlow.<br />

clauses<br />

classInfo("main", "nimgame").<br />

append([], Y, Y).<br />

append([U|X], Y, [U|Z]) :- append(X, Y, Z).<br />

clauses<br />

run():-


console::init(),<br />

(append([[1],[1],[1],[1],[1]], [[1],[1],[1]], L), !,<br />

stdio::write(L), stdio::nl;<br />

succeed()),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

增 加 从 一 个 表 里 取 走 一 些 元 素 的 takesome/3 谓 词 , 看 看 它 的 运 行 结 果 如 图 10.10.1 所 示 :<br />

implement main<br />

open core<br />

domains<br />

ms= integer*.<br />

hs= ms*.<br />

class predicates<br />

append:(E*, E*, E*) nondeterm anyFlow.<br />

test:() procedure.<br />

takesome:(ms, hs, hs) nondeterm anyFlow.<br />

clauses<br />

classInfo("main", "nimgame").<br />

append([], Y, Y).<br />

append([U|X], Y, [U|Z]) :- append(X, Y, Z).<br />

takesome([_X], V, V).<br />

takesome([_X, X1|Y], V, [[X1|Y]|V]).<br />

takesome([_X|T], V, Y) :- takesome(T, V, Y).<br />

test() :-<br />

L= [1, 1, 1, 1, 1],<br />

V= [[1,1]],<br />

stdio::write("\n The original : ", L, " ", V,"\n"),<br />

takesome(L, V, Resp),<br />

stdio::write(Resp), stdio::nl,<br />

fail; succeed().<br />

clauses<br />

run():-<br />

console::init(),<br />

test(),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).


图 10.10.1 从 最 前 面 的 一 堆 里 , 取 走 至 少 一 个 、 最 多 整 个 火 柴 棒<br />

我 们 再 增 加 一 个 谓 词 move/2, 表 明 从 一 个 状 态 到 另 一 个 状 态 。 比 如 从 [[1,1,1],[1,1]]<br />

中 取 走 火 柴 棒 , 有 多 少 种 取 法 呢 ? 程 序 如 下 , 运 行 结 果 如 图 10.10.2 所 示 :<br />

implement main<br />

open core<br />

domains<br />

ms= integer*.<br />

hs= ms*.<br />

class predicates<br />

append:(E*, E*, E*) nondeterm anyFlow.<br />

test:() procedure.<br />

takesome:(ms, hs, hs) nondeterm anyFlow.<br />

move:(hs, hs) nondeterm anyFlow.<br />

clauses<br />

classInfo("main", "nimgame").<br />

append([], Y, Y).<br />

append([U|X], Y, [U|Z]) :- append(X, Y, Z).<br />

takesome([_X], V, V).<br />

takesome([_X, X1|Y], V, [[X1|Y]|V]).<br />

takesome([_X|T], V, Y) :- takesome(T, V, Y).<br />

move(X, Y) :-<br />

append(U, [X1|V], X),<br />

takesome(X1, V, R),<br />

append(U, R, Y).<br />

class facts<br />

n : integer :=1.<br />

clauses<br />

test() :-<br />

L= [[1, 1, 1], [1, 1]],<br />

n:=1,<br />

stdio::write("Possible moves from ", L, ": \n"),<br />

move(L, Resp),<br />

stdio::write(n,"\t", Resp, "\n"),<br />

n:=n+1,<br />

fail;<br />

succeed(),stdio::write("Total ",n-1, " methods\n\n").


clauses<br />

run():-<br />

console::init(),<br />

test(),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 10.10.2 有 5 种 取 法 , 第 一 个 表 有 3 种 , 第 二 个 表 有 2 种 。<br />

下 面 为 寻 求 取 胜 方 法 , 设 立 谓 词 winningMove/2, 从 而 寻 求 有 可 能 制 胜 的 方 法 , 程 序 如 下 ,<br />

运 行 图 如 图 10.10.3 所 示 。<br />

implement main<br />

open core<br />

domains<br />

ms= integer*.<br />

hs= ms*.<br />

class predicates<br />

append:(E*, E*, E*) nondeterm anyFlow.<br />

test:() procedure.<br />

takesome:(ms, hs, hs) nondeterm anyFlow.<br />

move:(hs, hs) nondeterm anyFlow.<br />

winningMove:(hs, hs) nondeterm (i, o).<br />

clauses<br />

classInfo("main", "nimgame").<br />

append([], Y, Y).<br />

append([U|X], Y, [U|Z]) :- append(X, Y, Z).<br />

takesome([_X], V, V).<br />

takesome([_X, X1|Y], V, [[X1|Y]|V]).<br />

takesome([_X|T], V, Y) :- takesome(T, V, Y).<br />

move(X, Y) :-<br />

append(U, [X1|V], X),<br />

takesome(X1, V, R),<br />

append(U, R, Y).<br />

winningMove(X, Y) :-<br />

move(X, Y),<br />

not(winningMove(Y, _)).


class facts<br />

n : integer :=1.<br />

clauses<br />

test() :-<br />

L= [[1, 1, 1], [1, 1]],<br />

n:=1,<br />

stdio::write("Possible moves from ", L, ": \n"),<br />

winningMove(L, Resp),<br />

stdio::write(n,"\t", Resp, "\n"),<br />

n:=n+1,<br />

fail;<br />

succeed(),stdio::write("Total ",n-1, " methods\n\n").<br />

clauses<br />

run():-<br />

console::init(),<br />

test(),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 10.10.3 找 到 制 胜 方 法 是 从 第 一 堆 里 取 走 一 个<br />

下 面 的 程 序 是 我 们 的 交 互 版 本 , 运 行 结 果 如 图 10.10.4 所 示 。<br />

implement main<br />

open core<br />

domains<br />

ms= integer*.<br />

hs= ms*.<br />

class predicates<br />

append:(E*, E*, E*) nondeterm anyFlow.<br />

takesome:(ms, hs, hs) nondeterm anyFlow.<br />

move:(hs, hs) nondeterm anyFlow.<br />

winningMove:(hs, hs) nondeterm (i, o).<br />

clauses<br />

classInfo("main", "nimgame").<br />

append([], Y, Y).<br />

append([U|X], Y, [U|Z]) :- append(X, Y, Z).<br />

takesome([_X], V, V).<br />

takesome([_X, X1|Y], V, [[X1|Y]|V]).<br />

takesome([_X|T], V, Y) :- takesome(T, V, Y).


move(X, Y) :-<br />

append(U, [X1|V], X),<br />

takesome(X1, V, R),<br />

append(U, R, Y).<br />

winningMove(X, Y) :-<br />

move(X, Y),<br />

not(winningMove(Y, _)).<br />

class predicates<br />

iwin:(hs) determ (i).<br />

youwin:(hs, hs) determ (i, o).<br />

game:(hs) determ (i).<br />

clauses<br />

iwin([]) :- !,stdio:: write("I win\n").<br />

youwin(L, L2) :- winningMove(L, L2), !.<br />

youwin(_L, _L2) :- stdio::write("You win\n"), fail.<br />

class facts<br />

n : integer :=1.<br />

clauses<br />

game(L1) :-<br />

stdio::write("Your move: "),<br />

L= console::read(),<br />

move(L1, LTest),<br />

L= LTest,<br />

youwin(L, L2), !,<br />

stdio:: write("My move: "),<br />

stdio::write(L2, "\n"),<br />

not(iwin(L2)),<br />

game(L2).<br />

clauses<br />

run():-<br />

console::init(),<br />

L= [[1, 1, 1], [1, 1]],<br />

stdio::write("Start : ", L, "\n"),<br />

( game(L),!;<br />

succeed()),<br />

_=stdio::readline(),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).


图 10.10.4 交 互 的 下 棋 过 程


图 10.10.5 不 同 的 初 始 状 态 , 它 总 能 取 胜<br />

这 个 原 理 可 以 用 于 二 人 对 弈 的 其 它 棋 类 。<br />

第 十 一 章 事 实<br />

事 实 , 是 一 种 只 有 头 、 没 有 尾 的 Horn 句 子 。 事 实 , 可 以 在 运 行 时 , 动 态 的 插 入 、 删 除 , 这<br />

也 暗 示 了 它 可 以 被 编 辑 、 修 改 。 我 们 需 要 用 facts 或 者 class facts 来 声 明 事 实 , 而 不 是 用<br />

predicates 来 声 明 。<br />

第 11.1 节 数 据 库 记 录 的 生 成 和 保 存<br />

下 面 我 们 来 制 作 一 个 记 录 我 们 拥 有 的 书 籍 信 息 的 数 据 库 应 用 , 来 看 看 , 怎 么 应 用 事 实 。 程 序<br />

如 下 , 运 行 结 果 如 图 11.1.1 所 示 。 我 们 记 录 了 书 名 - 出 版 社 、 书 名 - 作 者 两 种 信 息 , 并 把 记 录<br />

保 存 在 文 件 bibliography.fac 里 。<br />

implement main<br />

open core<br />

domains<br />

name= string.<br />

author= n1(name); n2(name, name); etal(name).<br />

publisher= string.<br />

title= string.<br />

volume= journal(title, publisher); book(title, author).<br />

class facts - bib<br />

num:integer := 0.<br />

item:(volume, integer) nondeterm.<br />

class predicates<br />

addItem:(volume).<br />

prtDataBase:().


clauses<br />

classInfo("main", "facttest").<br />

addItem(V) :-<br />

num := num+1,<br />

assert(item(V, num)).<br />

prtDataBase() :-<br />

item(V, I),<br />

stdio::write(I, " ", V), stdio::nl,<br />

fail.<br />

prtDataBase().<br />

clauses<br />

run():-<br />

console::init(),<br />

addItem(journal("AI in focus", "MIT Press")),<br />

addItem(book( "Databases in <strong>Prolog</strong>",<br />

n1("Wellesley Barros"))),<br />

file::save("bibliography.fac", bib),<br />

prtDataBase(),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 11.1.1 数 据 库 操 作 、 记 录 输 入 、 保 存 、 显 示<br />

我 们 打 开 保 存 的 数 据 库 文 件 “bibliography.fac” 可 以 看 到 如 图 11.1.2 所 示 的 记 录 :<br />

图 11.1.2 保 存 的 数 据 库 记 录


第 11.2 节 数 据 库 记 录 文 件 的 读 取<br />

有 了 这 个 保 存 的 文 件 , 我 们 就 可 以 直 接 对 其 进 行 读 取 和 显 示 。 我 们 利 用 上 节 生 成 的 数 据 记 录<br />

文 件 , 进 行 读 取 和 显 示 , 程 序 如 下 , 运 行 结 果 如 图 11.2.1 所 示 。<br />

implement main<br />

open core<br />

domains<br />

name= string.<br />

author= n1(name); n2(name, name); etal(name).<br />

publisher= string.<br />

title= string.<br />

volume= journal(title, publisher); book(title, author).<br />

class facts - bib<br />

num:integer := 0.<br />

item:(volume, integer) nondeterm.<br />

class predicates<br />

prtDataBase:().<br />

clauses<br />

classInfo("main", "factread").<br />

prtDataBase() :-<br />

item(V, I),<br />

stdio::write(I, " ", V), stdio::nl,<br />

fail.<br />

prtDataBase().<br />

clauses<br />

run():-<br />

console::init(),<br />

F="bibliography.fac",<br />

file::consult(F, bib),<br />

stdio::write("We have read facts from file ",<br />

F, ".\nIt contains records as:\n\n"),<br />

prtDataBase(),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 11.2.1 读 取 数 据 库 文 件 并 显 示 其 记 录


我 们 把 数 据 库 文 件 读 入 内 存 用 到 file::consult("bibliography.fac", bib)。 这 是 关 键 。<br />

第 11.3 节 file 模 块 : 读 写 字 符 串 readString/writeString<br />

前 面 我 们 存 取 数 据 到 文 件 里 时 , 都 会 用 到 file 模 块 。 这 一 节 我 们 重 点 介 绍 这 个 模 块 的 一 些 必<br />

要 的 谓 词 和 特 性 。<br />

我 们 常 常 要 对 文 本 文 件 进 行 读 写 操 作 , 过 去 计 算 机 内 存 很 宝 贵 , 所 以 每 次 读 取 文 件 的 一 部 分<br />

内 容 进 行 操 作 。 如 今 内 存 很 富 裕 , 我 们 可 以 把 整 个 文 件 读 取 到 一 个 字 符 串 里 , 利 用 string 模<br />

块 的 强 大 处 理 能 力 进 行 处 理 , 然 后 再 把 整 个 字 符 串 , 写 到 一 个 文 件 里 。 而 在 文 件 内 容 和 字 符<br />

串 间 传 递 信 息 的 谓 词 是 :<br />

readString:(string FileName, boolean IsUnicodeFile)<br />

-> string String procedure (i,o).<br />

writeString:( string Filename, string Source,<br />

boolean IsUnicodeFile) procedure (i,i,i).<br />

这 个 版 本 是 可 以 指 定 内 容 是 否 是 unicode 的 编 码 形 式 。 如 果 无 需 指 定 这 个 选 项 , 我 们 通 常 用<br />

这 两 个 谓 词 :<br />

readString : (string FileName) -> string String procedure (i).<br />

writeString : (string Filename, string Source) procedure (i,i).<br />

下 面 我 们 在 运 行 目 录 里 建 立 一 个 test.txt 文 件 如 图 11.3.1 所 示 , 然 后 , 我 们 通 过 如 下 程 序 读<br />

取 其 内 容 并 转 换 成 全 部 大 写 字 母 , 保 存 在 upper.txt 文 件 里 。 运 行 图 如 图 11.3.2 所 示 。


图 11.3.1 在 \exe 目 录 里 建 立 文 件 test.txt<br />

implement main<br />

open core<br />

constants<br />

className = "main".<br />

classVersion = "filetest".<br />

clauses<br />

classInfo(className, classVersion).<br />

clauses<br />

run():-<br />

console::init(),<br />

Str= file::readString("test.txt", Unicode),<br />

S_Upper= string::toUpperCase(Str),<br />

file::writeString("upper.txt", S_Upper, Unicode),<br />

stdio::write(Str,"\n\n", S_Upper,"\n\n Save the result in upper.txt \n"),<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

图 11.3.2 读 取 文 件 内 容 显 示 , 转 换 成 为 全 部 大 写 字 母 后 显 示 , 保 存 在 upper.txt 里<br />

在 运 行 目 录 里 , 我 们 发 现 多 了 一 个 文 件 upper.txt, 打 开 看 看 其 内 荣 如 图 11.3.3 所 示 。


图 11.3.3 生 成 的 大 写 字 母 的 版 本 , 保 存 在 upper.txt 里<br />

第 11.4 节 常 数<br />

在 <strong>Visual</strong> <strong>Prolog</strong> 里 , 我 们 常 常 可 以 用 有 意 义 的 字 符 串 , 代 替 某 些 数 字 , 把 它 们 当 作 常 数 使 用 ,<br />

这 样 省 去 了 书 写 、 记 忆 的 麻 烦 。 在 菜 单 系 统 里 , 系 统 也 是 把 菜 单 项 目 用 一 个 统 一 的 命 名 方 法 ,<br />

把 其 内 部 代 码 用 常 数 形 式 表 示 的 。 下 面 的 例 子 我 们 我 们 来 感 受 一 下 菜 单 项 目 常 数 的 使 用 。 这<br />

些 系 统 生 成 的 常 数 存 在 根 目 录 的 resourceIdentifiers.res 里 。<br />

创 建 一 个 新 工 程 :<br />

Project Name: constantTest<br />

UI Strategy: Object-oriented GUI (pfc/gui)<br />

选 择 Create a New Form in New Package 创 建 textWin Form。 然 后 Build/Build 这 个 工 程 , 然<br />

后 在 textWin 的 Properties 对 话 框 Properties 里 选 择 Menu 项 目 设 置 TaskMenu, 在 Events 里 ,<br />

选 择 MenuItemListener 增 加 onMenuItem 菜 单 项 侦 听 器 。 如 图 11.4.1 所 示 。<br />

图 11.4.1 增 加 菜 单 项 目 侦 听 器<br />

然 后 双 击 , 在 textwin.pro 里 增 加 代 码 如 下 :<br />

predicates<br />

onMenuItem : window::menuItemListener.<br />

clauses


onMenuItem(_Source, M) :-<br />

Item= resourceIdentifiers::id_help_contents,<br />

M= Item, !,<br />

stdio::write("\nHello A").<br />

onMenuItem(_Source, _M):-stdio::write("\nHello B").<br />

按 照 这 个 操 作 :<br />

TaskWindow.win/CodeExpert/Menu/TaskMenu/id_file/id_file_new<br />

把 taskmenu 的 File/New 菜 单 变 成 可 以 执 行 的 , 增 加 代 码 如 下 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

X= textWin::new(S), X:show().<br />

执 行 一 下 看 看 , 当 我 们 点 击 Help 菜 单 下 的 不 同 项 目 , 在 Form 获 得 焦 点 情 况 下 , 运 行 结 果<br />

如 图 11.4.2 所 示 。<br />

图 11.4.2 不 同 的 菜 单 项 目 的 反 应<br />

第 十 二 章 class 和 object<br />

第 12.1 节 存 取 社 会 保 障 号<br />

关 于 class 和 object, 我 们 从 例 子 里 来 搞 清 楚 它 们 。 我 们 先 建 一 个 project 如 下 要 求 :<br />

Project Name: classexample


UI Strategy: console<br />

然 后 ,Build/Build 该 project, 之 后 增 加 一 个 新 的 class, 名 字 为 account, 注 意 一 定 不 要 不 选<br />

Creates Objects 选 项 如 图 12.1 所 示 。 我 们 修 改 account.i 文 件 增 加 代 码 后 如 下 所 示 :<br />

图 12.1 创 建 class 名 为 account, 记 得 要 保 留 选 项 Create Objects 啊<br />

interface account<br />

open core<br />

predicates<br />

ssN:(string SocialSecurity) procedure (o).<br />

setSocialSecurity:(string SSN) procedure(i).<br />

deposit:(real).<br />

end interface account<br />

当 我 们 调 用 方 法 A= account::new() 时 ,account 就 会 产 生 一 个 新 的 object 我 们 把 它 命 名 为 A,<br />

我 们 可 以 针 对 这 个 新 的 object A, 进 行 信 息 操 作 , 比 如 我 们 把 一 个 社 会 保 障 号 码 发 送 给 A,<br />

就 要 这 么 调 用 :<br />

A:setSocialSecurity("AA345"),<br />

之 后 , 我 们 可 以 从 A 里 获 得 社 会 保 障 号 码 :<br />

A:ssN(SocialSecurity),<br />

可 见 class 是 模 块 的 描 述 , 而 object 是 实 体 。 一 个 更 理 论 化 , 一 个 更 具 体 。 一 个 更 通 用 , 一<br />

个 更 个 性 。<br />

然 后 我 们 修 改 account.pro 文 件 , 得 到 这 样 的 代 码 :<br />

implement account<br />

open core<br />

facts - customer


funds:real := 3.0.<br />

personalData:(string Name, string SocialSecurity) single.<br />

clauses<br />

classInfo("account", "1.0").<br />

personalData("", "").<br />

ssN(SS) :- personalData(_, SS).<br />

setSocialSecurity(SSN) :-<br />

personalData(N, _),<br />

assert(personalData( N, SSN)).<br />

deposit(Value) :-<br />

funds := funds+Value.<br />

end implement account<br />

然 后 , 我 们 修 改 main.pro 代 码 如 下 :<br />

implement main<br />

open core<br />

clauses<br />

classInfo("classTest", "class 1.0").<br />

clauses<br />

run():-<br />

console::init(),<br />

A= account::new(), A:setSocialSecurity("AA345"),<br />

A:ssN(SocialSecurity),<br />

stdio::write(SocialSecurity), stdio::nl,<br />

_=stdio::readline().<br />

end implement main<br />

goal<br />

mainExe::run(main::run).<br />

运 行 之 后 , 显 示 了 我 们 输 入 的 AA345 这 个 号 码 。 证 明 这 个 号 码 已 经 存 储 在 这 个 实 体 里 。<br />

第 12.2 节 Object 的 事 实<br />

Object 的 特 性 是 靠 他 的 事 实 存 储 的 特 性 来 表 现 的 , 这 也 是 实 体 (object) 的 特 征 。 前 面 我 们<br />

定 义 了 :<br />

facts - customer<br />

funds:real := 3.0.<br />

personalData:(string Name, string SocialSecurity) single.<br />

这 就 是 指 明 了 account 实 体 的 主 要 特 征 值 。 不 同 的 值 , 代 表 不 同 的 实 体 。 而 界 面 谓 词 , 存 在


account.i 里 , 是 来 存 取 这 些 特 征 值 的 。<br />

interface account<br />

open core<br />

predicates<br />

ssN:(string SocialSecurity) procedure (o).<br />

setSocialSecurity:(string SSN) procedure(i).<br />

deposit:(real).<br />

end interface account<br />

第 十 三 章 乌 龟 爬 和 语 言 处 理<br />

第 13.1 节 乌 龟 爬 行<br />

乌 龟 爬 过 之 后 会 留 下 痕 迹 , 我 们 就 设 计 一 个 程 序 , 记 录 痕 迹 , 仿 佛 乌 龟 爬 过 。 建 立 一 个<br />

Project, 如 下 描 述 :<br />

General<br />

Project name: turtle<br />

UI Strategy: Object-oriented GUI (pfc/gui)<br />

Target type: Exe<br />

然 后 在 新 的 包 里 建 立 一 个 Form, 名 字 为 canvas。 执 行 File/New in Existing Package,<br />

在 canvas 目 录 下 建 立 一 个 class 名 字 为 curve, 注 意 把 “Create objects” 选 项 去 掉 。Build/Build<br />

这 个 Project。<br />

执 行 “TaskWindow.win/CodeExpert/Menu/TaskMnu/id_file/id_file_new” 和<br />

“TaskWindow.mnu/Edit/File/New/Enable” 操 作 , 使 File|New 菜 单 项 目 可 操 作 , 并 增 加<br />

代 码 如 下 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

X= canvas::new(S),<br />

X:show().<br />

编 辑 curve.cl, 使 代 码 如 下 :<br />

class curve


open core, vpiDomains<br />

predicates<br />

classInfo : core::classInfo.<br />

drawCurve:(windowHandle).<br />

end class curve<br />

编 辑 curve.pro 文 件 , 代 码 如 下 :<br />

implement curve<br />

open core, vpi, vpiDomains, math<br />

class predicates<br />

star:(windowHandle, integer, real, integer) procedure (i, i, i, i).<br />

clauses<br />

classInfo("plotter/curve", "1.0").<br />

star(_Win, 0, _A, _L) :- !.<br />

star(Win, N, A, L) :-<br />

turtle::right(A),<br />

turtle::forward(Win, L),<br />

star(Win, N-1, A, L).<br />

drawCurve(Win) :- star(Win, 10, 3*pi/5, 40).<br />

end implement curve<br />

执 行 “File/New in New Package” 创 建 一 个 新 的 包 “turtleGraphics”, 注 意 目 录 选 择<br />

为 “..\..\vispro” 根 目 录 下 , 在 这 个 包 下 面 , 创 建 一 个 class 名 字 为 “turtle”。 这 个 class 的 代 码<br />

turtle.cl 如 下 :<br />

class turtle<br />

open core, vpiDomains<br />

predicates<br />

classInfo : core::classInfo.<br />

forward:(windowHandle, integer) procedure.<br />

move:(integer) procedure.<br />

right:(real) procedure.<br />

left:(real) procedure.<br />

end class turtle<br />

而 turtle.pro 代 码 如 下 :<br />

implement turtle<br />

open core, vpiDomains, vpi, math<br />

class facts


turtle:(pnt, real) single.<br />

clauses<br />

classInfo("turtleGraphics/turtle", "1.0").<br />

turtle(pnt(80, 80), -pi/2).<br />

forward(Win, L) :-<br />

turtle(P1, Facing),<br />

P1= pnt(X1, Y1),<br />

X2= math::round( X1+ L*cos(Facing)),<br />

Y2= math::round(Y1+L*sin(Facing)),<br />

P2= pnt(X2, Y2),<br />

drawline(Win, P1, P2),<br />

assert(turtle(P2, Facing)).<br />

move(L) :-<br />

turtle(P1, Facing),<br />

P1= pnt(X1, Y1),<br />

X2= round( X1+ L*cos(Facing)),<br />

Y2= round(Y1+L*sin(Facing)),<br />

P2= pnt(X2, Y2), assert(turtle(P2, Facing)).<br />

right(A) :-<br />

turtle(P1, Facing),<br />

NewAngle= Facing+A,<br />

assert(turtle(P1, NewAngle)).<br />

left(A) :-<br />

turtle(P1, Facing),<br />

NewAngle= Facing-A,<br />

assert(turtle(P1, NewAngle)).<br />

end implement turtle<br />

接 下 来 , 执 行 “canvas.frm/Events/PaintRespont-onPaint” 并 双 击 鼠 标 , 在 canvas.pro 里<br />

增 加 代 码 :<br />

predicates<br />

onPaint : drawWindow::paintResponder.<br />

clauses<br />

onPaint(S, _Rectangle, _GDIObject) :-<br />

W= S:getVPIWindow(),<br />

curve::drawCurve(W).<br />

现 在 我 们 执 行 一 下 , 执 行 File/New 菜 单 选 项 得 到 图 如 13.1.1 所 示 。


图 13.1.1 第 一 个 龟 行 轨 迹<br />

第 13.2 节 乌 龟 的 状 态 描 述<br />

这 里 , 乌 龟 的 状 态 用 两 个 变 量 来 描 述 :<br />

class facts<br />

turtle:(pnt, real) single.<br />

clauses<br />

turtle(pnt(80, 80), -pi/2).<br />

一 个 是 当 前 的 位 置 坐 标 , 一 个 是 角 度 朝 向 , 用 于 下 一 个 位 置 的 确 立 和 运 动 方 向 的 确 立 。<br />

在 turtle 模 块 里 , 设 计 了 前 行 谓 词 forward/2, 这 是 要 画 线 的 :<br />

forward(Win, L) :-<br />

turtle(P1, Facing),<br />

P1= pnt(X1, Y1),<br />

X2= math::round( X1+ L*cos(Facing)),<br />

Y2= math::round(Y1+L*sin(Facing)),<br />

P2= pnt(X2, Y2),<br />

drawline(Win, P1, P2),<br />

assert(turtle(P2, Facing)).<br />

而 move/1 则 是 移 动 位 置 不 画 线 , 左 、 右 行 的 left/1、right/1 是 改 变 方 向 , 也 是 不 画 线 。<br />

right(A) 表 示 增 加 角 度 ,left(A) 表 示 减 少 角 度 值 。<br />

move(L) :-<br />

turtle(P1, Facing),<br />

P1= pnt(X1, Y1),<br />

X2= round( X1+ L*cos(Facing)),<br />

Y2= round(Y1+L*sin(Facing)),<br />

P2= pnt(X2, Y2), assert(turtle(P2, Facing)).


ight(A) :-<br />

turtle(P1, Facing),<br />

NewAngle= Facing+A,<br />

assert(turtle(P1, NewAngle)).<br />

left(A) :-<br />

turtle(P1, Facing),<br />

NewAngle= Facing-A,<br />

assert(turtle(P1, NewAngle)).<br />

而 我 们 主 要 控 制 画 线 的 curve 模 块 里 drawCurve/1 在 这 里 这 样 描 述 : 进 行 10 次 这 样 的 动<br />

作 , 每 次 增 加 角 度 3*pi/5, 径 向 长 度 为 40, 然 后 向 前 画 线 , 如 此 反 复 直 至 10 次 结 束 。 通 过<br />

修 改 角 度 , 次 数 , 我 们 可 以 画 出 不 同 的 规 则 图 形 ; 通 过 修 改 长 度 , 我 们 可 以 改 变 图 形 大 小 尺<br />

寸 。<br />

star(_Win, 0, _A, _L) :- !.<br />

star(Win, N, A, L) :-<br />

turtle::right(A),<br />

turtle::forward(Win, L),<br />

star(Win, N-1, A, L).<br />

drawCurve(Win) :- star(Win, 10, 3*pi/5, 40).<br />

第 13.3 节 画 出 其 他 的 规 则 图 形<br />

其 实 根 据 上 面 的 分 析 , 我 们 只 要 改 变 角 度 、 次 数 , 就 可 以 画 出 不 同 的 规 则 图 形 。<br />

图 13.3.1 drawCurve(Win) :- star(Win, 3, 2*pi/3,80). 画 出 正 三 角 形


图 13.3.2 drawCurve(Win) :- turtle::left(pi/2),star(Win, 3, 2*pi/3,80). 则 画 出 正 的 正 三 角 形<br />

注 意 , 我 们 绘 图 开 始 时 并 没 有 指 定 起 始 位 置 , 这 样 turtle 的 起 始 位 置 是 在 这 个 实 体 里 ,<br />

起 始 位 置 就 是 上 一 次 绘 图 的 最 后 位 置 。 请 你 尝 试 着 点 击 canvas 的 右 上 角 关 闭 这 个 对 话 框 ,<br />

然 后 在 点 击 File/New 生 成 新 的 canvas, 如 此 反 复 , 看 看 有 什 么 不 同 。<br />

图 13.3.3 drawCurve(Win) :- turtle::left(pi/2),star(Win, 5, 2*pi/5,40). 五 边 形<br />

图 13.3.4 drawCurve(Win) :- turtle::right(1*pi/10), star(Win, 5, 4*pi/5,80). 五 角 星


第 13.4 节 保 证 每 次 画 出 的 是 一 样 的 图 形<br />

我 们 通 过 在 turtle 模 块 里 增 加 设 置 起 始 位 置 的 谓 词 setstart/2, 来 保 证 每 次 起 始 位 置 相 同 , 这<br />

样 就 可 以 保 证 每 次 画 出 的 图 形 是 一 样 的 。 如 图 13.4.1 所 示 , 而 且 我 们 还 可 以 指 定 在 什 么 位 置<br />

画 图 , 可 以 同 时 画 出 若 干 个 图 如 图 13.4.2 所 示 。<br />

图 13.4.1 画 出 一 致 的 图 形<br />

图 13.4.2 在 不 同 的 位 置 , 画 出 不 同 的 图 形<br />

修 改 后 的 程 序 文 件 如 下 所 示 。curve.cl 没 有 变 化 ,curve.pro 如 下 所 示 :<br />

implement curve<br />

open core, vpi, vpiDomains, math<br />

class predicates<br />

star:(windowHandle, integer, real, integer) procedure (i, i, i, i).<br />

clauses<br />

classInfo("plotter/curve", "1.0").<br />

star(_Win, 0, _A, _L) :- !.


star(Win, N, A, L) :-<br />

turtle::right(A),<br />

turtle::forward(Win, L),<br />

star(Win, N-1, A, L).<br />

drawCurve(Win) :-<br />

turtle::setstart(pnt(110, 50), -pi/2),<br />

turtle::right(1*pi/10),<br />

star(Win, 5, 4*pi/5,80),<br />

turtle::setstart(pnt(180, 120), -pi/2),<br />

turtle::left(pi/2),<br />

star(Win, 3, 2*pi/3,80).<br />

end implement curve<br />

而 turtle.cl 如 下 所 示 :<br />

class turtle<br />

open core, vpiDomains<br />

predicates<br />

classInfo : core::classInfo.<br />

forward : (windowHandle, integer) procedure.<br />

move : (integer) procedure.<br />

right :(real) procedure.<br />

left : (real) procedure.<br />

setstart : (pnt, real) procedure.<br />

end class turtle<br />

文 件 turtle.pro 如 下 所 示 :<br />

implement turtle<br />

open core, vpiDomains, vpi, math<br />

class facts<br />

turtle:(pnt, real) single.<br />

clauses<br />

classInfo("turtleGraphics/turtle", "1.0").<br />

turtle(pnt(80, 80), -pi/2).<br />

forward(Win, L) :-<br />

turtle(P1, Facing),<br />

P1= pnt(X1, Y1),<br />

X2= math::round( X1+ L*cos(Facing)),<br />

Y2= math::round(Y1+L*sin(Facing)),<br />

P2= pnt(X2, Y2),<br />

drawline(Win, P1, P2),<br />

assert(turtle(P2, Facing)).<br />

move(L) :-<br />

turtle(P1, Facing),<br />

P1= pnt(X1, Y1),<br />

X2= round( X1+ L*cos(Facing)),<br />

Y2= round(Y1+L*sin(Facing)),


P2= pnt(X2, Y2), assert(turtle(P2, Facing)).<br />

right(A) :-<br />

turtle(P1, Facing),<br />

NewAngle= Facing+A,<br />

assert(turtle(P1, NewAngle)).<br />

left(A) :-<br />

turtle(P1, Facing),<br />

NewAngle= Facing-A,<br />

assert(turtle(P1, NewAngle)).<br />

setstart(P1, NewAngle):-<br />

assert(turtle(P1, NewAngle)).<br />

end implement turtle<br />

第 13.5 节 递 归<br />

和 自 然 数 相 关 的 谓 词 , 我 们 这 样 来 描 述 递 归 方 法 :<br />

(1) 证 明 它 对 于 0 是 正 确 的 。<br />

(2) 如 果 它 对 于 N-1 是 正 确 的 , 那 么 对 于 N 它 也 是 正 确 的 。<br />

在 前 面 的 绘 图 代 码 里 , 我 们 看 到 star/4 就 是 这 样 :<br />

star(_Win, 0, _A, _L) :- !.<br />

star(Win, N, A, L) :-<br />

turtle::right(A),<br />

turtle::forward(Win, L),<br />

star(Win, N-1, A, L).<br />

而 前 面 我 们 计 算 阶 乘 时 用 到 的 fact/2 也 是 这 样 :<br />

clauses<br />

fact(0, 1) :- !.<br />

fact(N, N*F) :-<br />

fact(N-1, F).<br />

在 <strong>Visual</strong> <strong>Prolog</strong> 里 , 甚 至 于 所 有 的 <strong>Prolog</strong> 语 言 里 , 递 归 是 一 个 经 常 用 到 的 方 法 , 它 使<br />

复 杂 问 题 得 到 简 化 的 证 明 方 法 , 而 把 冗 长 的 过 程 交 给 速 度 很 快 的 计 算 机 来 重 复 进 行 。


第 13.6 节 利 用 递 归 方 法 画 复 杂 曲 线<br />

通 过 改 变 前 面 curve.pro 内 容 , 我 们 可 以 画 出 更 为 复 杂 的 曲 线 , 这 就 是 递 归 方 法 产 生 的 奇 异<br />

效 果 。 程 序 如 下 :<br />

implement curve<br />

open core, vpi, vpiDomains, math<br />

class predicates<br />

peano:(windowHandle, integer, real, integer) procedure (i, i, i, i).<br />

clauses<br />

classInfo("plotter/curve", "1.0").<br />

peano(_Win, N, _A, _H) :- N


图 13.6.2 改 变 为 turtle::setstart(pnt(100, 80), -pi/4) 画 出 倾 斜 的 曲 线<br />

我 们 做 一 个 有 趣 的 实 验 , 让 我 们 体 会 更 多 。 首 先 在 curve.cl 里 增 加 谓 词 addN/0:<br />

predicates<br />

classInfo : core::classInfo.<br />

drawCurve:(windowHandle).<br />

addN : () procedure.<br />

然 后 在 curve.pro 里 增 加 :<br />

class facts<br />

n:integer :=1.<br />

clauses<br />

addN():-n:=n+1,stdio::write(n).<br />

并 修 改 drawCurve/1 为 如 下 :<br />

drawCurve(Win) :-<br />

turtle::setstart(pnt(100, 80), -pi/2),<br />

turtle::move(-60), peano(Win, n, pi/2, 5).<br />

最 后 双 击 canvas.frm 进 入 Properties/Events/MouseDblListener-onMouseDbl, 在 双 击 增 加<br />

代 码 如 下 到 canvas.pro 里 :<br />

predicates<br />

onMouseDbl : drawWindow::mouseDblListener.<br />

clauses<br />

onMouseDbl(Source, _Point, _ShiftControlAlt, _Button):-<br />

stdio :: write("\n You have double clicked, N ="),<br />

curve :: addN(),<br />

Source:invalidate().<br />

们 可 以 看 到 当 N 为 不 同 值 时 , 所 画 曲 线 的 情 况 。 当 我 们 每 次 在 canvas 对 话 框 上 双 击 鼠<br />

标 ,Message 窗 口 就 会 显 示 当 前 N 是 几 , 双 击 一 次 增 加 1, 图 形 也 随 之 刷 新 。 运 行 图 如 图 13.6.3


所 示 。<br />

图 13.6.3a N=1 图 13.6.3b N=2<br />

图 13.6.3c N=3 图 13.6.3d N=4<br />

图 13.6.3e N=5 图 13.6.3f N=6


图 13.6.3g N=7 图 13.6.3h N=8<br />

第 13.7 节 语 言 处 理<br />

<strong>Prolog</strong> 最 大 的 特 点 是 可 以 进 行 语 言 处 理 。 下 面 我 们 我 们 按 步 骤 这 样 操 作 , 建 立 一 个 查 字<br />

典 提 供 语 言 帮 助 的 小 程 序 。<br />

按 顺 序 操 作 (1) 创 建 一 个 language 为 名 字 的 GUI Project。( 2)File/New in new package<br />

创 建 editor 包 ; (3) 在 editor 包 里 增 加 editorControl 模 块 。 具 体 操 作 方 法 是 :<br />

File/Add—“\pfc\gui\controls\editorControl\editorcontrol.pack”<br />

接 着 Build/Build。( 4) 在 editor 下 创 建 txtWin 对 话 框 Form, 并 在 其 中 增 加 Custom Control,<br />

选 择 editorControl (5) 在 editor 下 创 建 class “grammar”, 去 掉 Create Objects 的 选 项 。<br />

文 件 grammar.cl 内 容 如 下 :<br />

class grammar<br />

open core<br />

predicates<br />

classInfo : core::classInfo.<br />

dic:(string).<br />

end class grammar<br />

而 grammar.pro 文 件 内 容 如 下 :<br />

implement grammar<br />

open core<br />

clauses<br />

classInfo( "editor/grammar", "1.0").<br />

dic(S) :-<br />

stdio::write(S), stdio::nl.<br />

end implement grammar


使 菜 单 项 File/New 为 可 用 , 并 增 加 File/New 点 击 事 件 代 码 ( 操 作 方 法 为 “TaskWindow.win<br />

/Code Expert/Menu/TaskMenu/id_file/id_file_new”):<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :- X= txtWin::new(S), X:show().<br />

接 下 来 , 编 辑 txtWin.frm, 点 击 Help 按 钮 , 选 择 其 Events 对 话 框 , 如 图 13.7.1 所 示 , 设 置<br />

该 按 钮 的 点 击 反 应 器 为 onHelpClick 双 击 增 加 代 码 到 txtwin.pro 里 如 下 所 示 :<br />

predicates<br />

onHelpClick : button::clickResponder.<br />

clauses<br />

onHelpClick(_Source) = button::defaultAction :-<br />

editorControl_ctl:getSelection(Start, End),<br />

Txt=editorControl_ctl:getText(Start, End),<br />

grammar::dic(Txt).<br />

图 13.7.1 增 加 Help 按 钮 的 代 码<br />

打 开 txtWin.pro, 看 到 原 来 new/1 谓 词 如 下 :<br />

clauses<br />

new(Parent):-<br />

formWindow::new(Parent),<br />

generatedInitialize().<br />

我 们 把 它 修 改 为 :<br />

new(Parent):-<br />

formWindow::new(Parent),<br />

generatedInitialize(),<br />

PossibleName= "Language.txt",<br />

tryGetFileContent(PossibleName, InputStr, _FileName), !,<br />

XX=string::concat("Here: ", InputStr),


editorControl_ctl:pasteStr(XX).<br />

增 加 谓 词 tryGetFileContent/3 读 取 源 文 件 到 编 辑 器 里 。 该 谓 词 定 义 如 下 :<br />

class predicates<br />

tryGetFileContent:(<br />

string FileName,<br />

string InputStr,<br />

string UpdatedFileName) procedure (i, o, o).<br />

clauses<br />

tryGetFileContent( "", "", "LsF.txt") :- !.<br />

tryGetFileContent(FileName, InputStr, FileName) :-<br />

trap(file::existFile(FileName), _TraceId, fail), !,<br />

InputStr= file::readString(FileName, _).<br />

tryGetFileContent(FileName, "", FileName).<br />

这 里 假 设 我 们 是 对 拉 丁 文 进 行 帮 助 , 我 们 预 先 把 文 件 language.txt 编 辑 好 , 放 在 当 前 的 exe<br />

目 录 里 。 放 入 下 面 文 字 如 图 13.7.2 所 示 , 这 是 程 序 运 行 如 图 13.7.3 所 示 , 可 以 把 文 件 调 入 编<br />

辑 器 , 并 且 在 选 中 单 词 后 , 按 钮 help 按 下 , 会 在 Message 窗 口 显 示 该 单 词 。<br />

图 13.7.2 拉 丁 文 原 文


图 13.7.3 先 测 试 选 中 文 字 的 显 示<br />

这 时 我 们 修 改 grammar.pro 文 件 如 下 :<br />

implement grammar<br />

open core, string<br />

class predicates<br />

strTok:(string, string_list) procedure (i, o).<br />

addExplanation:(string, string) procedure (i, o).<br />

class facts<br />

entry:(string, string).<br />

clauses<br />

classInfo( "editor/grammar", "1.0").<br />

entry("que", "that").<br />

entry("sine", "without").<br />

entry("es", "am, are, is, was, were, to be").<br />

entry("de", "of").<br />

entry("et", "and").<br />

addExplanation(T, R) :-<br />

entry(T, Meaning), !,<br />

R= format("%s= %s\n", T, Meaning).<br />

addExplanation(_T, "").<br />

strTok(S, [T1|L]) :-<br />

frontToken(S, T, Rest), !,<br />

addExplanation(T, T1),<br />

strTok(Rest, L).<br />

strTok(_S, []).<br />

dic(S) :- strTok(S, SList),<br />

Resp= concatList(SList),<br />

stdio::write(Resp), stdio::nl.<br />

end implement grammar<br />

再 运 行 , 可 以 看 到 , 它 能 够 对 选 中 的 文 字 加 以 解 释 。 如 图 13.7.4 所 示 。


图 13.7.4 对 原 文 进 行 解 释 , 小 小 的 帮 助 技 巧<br />

第 十 四 章 L- 系 统<br />

人 们 总 想 办 法 寻 求 如 何 简 单 描 绘 植 物 。 这 里 我 们 通 过 一 个 实 例 来 说 明 。<br />

第 14.1 节 画 一 棵 树<br />

首 先 创 建 一 个 GUI Project “Lsys”, 在 其 根 目 录 下 创 建 包 “aristid”; 在 “aristid” 下 创 建<br />

Form “canvas”, 另 外 创 建 一 个 class “draw”, 不 选 择 “Create Objects”。 使 File/New 可<br />

用 , 并 用 Code Expert 增 加 代 码 在 taskWindow.pro 里 如 下 所 示 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

X= canvas::new(S), X:show().<br />

文 件 draw.cl 内 容 如 下 :<br />

class draw<br />

open core, vpiDomains<br />

predicates<br />

classInfo : core::classInfo.<br />

tree:(windowHandle).<br />

end class draw<br />

文 件 draw.pro 内 容 如 下 :<br />

implement draw<br />

open core, vpiDomains, vpi, math<br />

clauses<br />

classInfo("plotter/draw", "1.0").<br />

domains<br />

command = t(commandList); f(integer);<br />

r(integer); m.<br />

commandList= command*.<br />

class facts<br />

pos:(real Delta, real Turn) single.<br />

grammar:(commandList) single.<br />

class predicates<br />

move:(windowHandle, pnt, real, commandList)


procedure (i, i, i, i).<br />

derive:(commandList, commandList)<br />

procedure (i, o).<br />

mv:(windowHandle, pnt, real, command, pnt, real)<br />

procedure (i,i, i, i, o, o).<br />

iter:(integer, commandList, commandList)<br />

procedure (i, i, o).<br />

clauses<br />

pos(4.0, 0.1745329).<br />

grammar([f(1)]).<br />

iter(0, S, S) :- !.<br />

iter(I, InTree, OutTree) :-<br />

derive(InTree, S),<br />

iter(I-1, S, OutTree).<br />

derive([], []) :- !.<br />

derive([f(0)|Rest], [f(0),f(0)|S]) :- !,<br />

derive(Rest, S).<br />

derive([f(_)|Rest],<br />

[f(0), t([r(1),f(1)]), f(0),<br />

t([r(-1),f(1)]), r(1), f(1)|S]) :- !,<br />

derive(Rest, S).<br />

derive([t(Branches)|Rest], [t(B)|S]) :- !,<br />

derive(Branches, B),<br />

derive(Rest, S).<br />

derive([X|Rest], [X|S]) :- derive(Rest, S).<br />

mv(Win, P1, Facing, f(_), P2, Facing) :- !,<br />

pos(Displacement, _Turn),<br />

P1= pnt(X1, Y1),<br />

X2= round( X1+ Displacement*cos(Facing)),<br />

Y2= round( Y1+Displacement*sin(Facing)),<br />

P2= pnt(X2, Y2),<br />

drawline(Win, P1, P2).<br />

mv(_Win, P1, Facing, m, P2, Facing) :- !,<br />

pos(Displacement, _Turn),<br />

P1= pnt(X1, Y1),<br />

X2= round( X1+ Displacement*cos(Facing)),<br />

Y2= round(Y1+Displacement*sin(Facing)),<br />

P2= pnt(X2, Y2).<br />

mv(_Win, P1, Facing, r(Direction), P1, F) :- !,<br />

pos(_Displacement, Turn),<br />

F= Facing+ Direction*Turn.<br />

mv(Win, P1, Facing, t(B), P1, Facing) :-<br />

move(Win, P1, Facing, B).<br />

move(_Win, _P1, _Facing, []) :- !.<br />

move(Win, P1, Facing, [X|Rest]) :-<br />

mv(Win, P1, Facing, X, P, F),<br />

move(Win, P, F, Rest).<br />

tree(Win) :- grammar(Commands),<br />

iter(5, Commands, C),<br />

Facing= -pi/2,<br />

Point= pnt(100, 250),


move(Win, Point, Facing, C).<br />

end implement draw<br />

先 Build/Build, 然 后 , 编 辑 canvas.frm, 在 Events 对 话 框 里 增 加 onPaint 为 paintResponder<br />

的 事 件 处 理 谓 词 , 双 击 后 在 canvas.pro 里 增 加 代 码 如 下 :<br />

predicates<br />

onPaint : drawWindow::paintResponder.<br />

clauses<br />

onPaint(S, _Rectangle, _GDIObject) :-<br />

draw::tree(S:getVPIWindow()).<br />

运 行 一 下 , 见 图 14.1.1 所 示 。<br />

图 14.1.1 画 出 一 棵 树<br />

第 14.2 节 如 何 才 能 画 一 棵 树<br />

通 过 修 改 tree/1 谓 词 :<br />

tree(Win) :- grammar(Commands),<br />

iter(5, Commands, C),<br />

stdio::write("\nThe commands list is :\n",Commands,"==>", C),<br />

Facing= -pi/2,<br />

Point= pnt(100,100),<br />

move(Win, Point, Facing, C).<br />

我 们 看 到 ,grammar/1 是 提 供 了 初 始 命 令 , 由 iter/3 来 形 成 最 后 的 绘 图 命 令 , 我 们 让 形 成 的<br />

命 令 列 表 显 示 出 来 , 它 们 是 一 串 串 命 令 序 列 如 下 所 示 ( 部 分 )。 其 中 那 个 数 字 代 表 复 杂 程 度 。<br />

[f(1)]==><br />

[f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),


t([r(1),f(0),f(0),f(0),f(0),f(0),f(0),f(0),f(0),<br />

t([r(1),f(0),f(0),f(0),f(0),<br />

t([r(1),f(0),f(0),<br />

t([r(1),f(0),<br />

t([r(1),f(1)]),f(0),<br />

t([r(-1),f(1)]),r(1),f(1)]),f(0),f(0),<br />

t([r(-1),f(0),<br />

t([r(1),f(1)]),f(0),<br />

t([r(-1),f(1)]),r(1),f(1)]),r(1),f(0),<br />

t([r(1),f(1)]),f(0),<br />

t([r(-1),f(1)]),r(1),f(1)]),f(0),f(0),f(0),f(0),<br />

t([r(-1),f(0),f(0),<br />

t([r(1),f(0),<br />

t([r(1),f(1)]),f(0),<br />

t([r(-1),f(1)]),r(1),f(1)]),f(0),f(0),<br />

t([r(-1),f(0),<br />

……….<br />

而 pos(4.0, 0.1745329). 是 指 明 了 步 长 和 倾 角 。 我 们 执 行 iter(1, Commands, C), 看 到 画 树 的 命<br />

令 序 列 是 :[f(0),t([r(1),f(1)]),f(0),t([r(-1),f(1)]),r(1),f(1)]。 根 据 :<br />

mv(Win, P1, Facing, f(_), P2, Facing) :- !,<br />

pos(Displacement, _Turn),<br />

P1= pnt(X1, Y1),<br />

X2= round( X1+ Displacement*cos(Facing)),<br />

Y2= round( Y1+Displacement*sin(Facing)),<br />

P2= pnt(X2, Y2),<br />

drawline(Win, P1, P2).<br />

mv(_Win, P1, Facing, m, P2, Facing) :- !,<br />

pos(Displacement, _Turn),<br />

P1= pnt(X1, Y1),<br />

X2= round( X1+ Displacement*cos(Facing)),<br />

Y2= round(Y1+Displacement*sin(Facing)),<br />

P2= pnt(X2, Y2).<br />

mv(_Win, P1, Facing, r(Direction), P1, F) :- !,<br />

pos(_Displacement, Turn),<br />

F= Facing+ Direction*Turn.<br />

mv(Win, P1, Facing, t(B), P1, Facing) :-<br />

move(Win, P1, Facing, B).<br />

可 以 解 读 为 f 代 表 画 线 ,r 代 表 改 变 方 向 , 其 内 数 字 代 表 方 位 改 变 的 方 向 ( 加 角 度 还 是 减 角<br />

度 ),t 代 表 分 枝 。 所 以 这 段 命 令 可 以 解 读 为 : 向 上 画 一 段 , 然 后 画 一 个 分 枝 , 再 上 画 一 段 ,<br />

另 外 一 个 方 向 画 分 枝 , 改 变 方 向 , 画 一 段 。 把 段 长 度 改 为 40, 比 如 pos(40.0, 0.1745329). 如


下 图 14.2.1 所 示 。<br />

图 14.2.1 画 3 个 分 枝 最 简 单 的 图<br />

复 杂 的 图 不 过 是 这 些 的 反 复 而 已 如 图 14.2.3 所 示 。N=2, 如 图 14.2.2 所 示 。 命 令 是 :<br />

[f(0),f(0),<br />

t([r(1),<br />

f(0),<br />

t([r(1),f(1)]),<br />

f(0),<br />

t([r(-1),f(1)]),<br />

r(1),f(1)]),<br />

f(0),f(0),<br />

t([r(-1),f(0),<br />

t([r(1),f(1)]),<br />

f(0),<br />

t([r(-1),f(1)]),<br />

r(1),f(1)]),<br />

r(1),f(0),<br />

t([r(1),f(1)]),<br />

f(0),<br />

t([r(-1),f(1)]),<br />

r(1),f(1)]。<br />

图 14.2.2 N=2 仿 佛 两 个 N=1 的 树 形 的 复 合


图 14.2.5 N=5 时 , 仿 佛 有 5 层 这 样 的 树 复 合 而 成<br />

第 14.3 节 画 出 不 同 的 树<br />

修 改 derive/1, 可 以 改 变 分 枝 的 方 法 , 就 可 以 画 出 不 同 的 树 。 原 来 :<br />

derive([f(_)|Rest],<br />

[f(0), t([r(1),f(1)]), f(0),<br />

t([r(-1),f(1)]), r(1), f(1)|S]) :- !,<br />

derive(Rest, S).<br />

我 们 修 改 为 :<br />

derive([f(_)|Rest],<br />

[f(0), t([r(1),f(1),f(0),r(-1),f(1)]), f(0),<br />

t([r(-1),f(1)]), r(1), f(1)|S]) :- !,<br />

derive(Rest, S).<br />

树 就 成 了 如 图 14.3.1 所 示 形 状 。


图 14.3.1 改 变 树 的 形 状<br />

第 十 五 章 游 戏<br />

如 果 说 普 通 的 程 序 设 计 是 写 文 章 , 那 么 编 写 游 戏 程 序 , 就 像 写 诗 一 样 有 难 度 。 本 章 我 们 试 图<br />

体 验 初 步 的 游 戏 设 计 , 也 是 为 了 证 明 <strong>Visual</strong> <strong>Prolog</strong> 的 超 凡 能 力 : 游 戏 它 也 胜 任 。<br />

第 15.1 节 游 戏 描 述<br />

设 计 之 前 , 要 明 确 你 的 目 标 。 在 这 里 , 我 们 要 设 计 这 样 一 个 游 戏 :<br />

(1) 有 一 个 虫 子<br />

(2) 在 一 个 板 子 上 爬<br />

(3) 我 们 可 以 通 过 上 下 左 右 来 控 制 它 的 运 动 方 向<br />

(4) 虫 子 触 壁 , 则 结 束 ; 时 间 长 , 得 分 多 。<br />

第 15.2 节 游 戏 设 计 前 的 分 析<br />

我 们 需 要 一 个 虫 子 的 图 像 , 需 要 设 计 一 个 Form, 要 在 Form 上 设 置 边 界 、 设 置 上 下 左 右 控<br />

制 方 向 的 按 钮 。 要 能 显 示 计 分 、 显 示 状 态 。 程 序 逻 辑 分 成 两 部 分 : 一 个 描 述 虫 子 的 状 态 和 控<br />

制 虫 子 运 动 的 模 块 , 一 个 计 分 和 判 断 的 模 块 。 我 们 还 需 要 定 时 器 , 让 虫 子 不 停 地 刷 新 状 态 ,<br />

不 停 前 进 。<br />

第 15.3 节 游 戏 编 程<br />

创 建 Project “game”:


Project Name: game<br />

UI Strategy: Object-Oriented GUI (pfc/vpi)<br />

TargetType: Exe<br />

Base Directory: C\vip70<br />

Sub-Directory: game<br />

创 建 一 个 Package “playground”, 在 其 下 , 首 先 创 建 Form “lawn”, 设 计 成 图 15.3.1 所 示<br />

样 子 。<br />

图 15.3.1 游 戏 窗 口<br />

然 后 , 使 File/New 菜 单 项 可 用 , 并 增 加 其 代 码 到 taskWindow.pro 里 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :- X= lawn::new(S), X:show().<br />

接 下 来 , 在 playground 下 创 建 class “objState”, 保 留 “Create Objects” 选 项 。 该 模 块 需 要<br />

objstate.i 文 件 。 该 模 块 代 码 如 下 一 一 列 举 。<br />

文 件 “objstate.i” 的 代 码 :<br />

interface objstate<br />

open core, vpiDomains<br />

predicates<br />

init:().<br />

turn:(integer, integer).<br />

mov:().<br />

segm:(rct) nondeterm (o).<br />

end interface objstate<br />

文 件 “objstate.cl” 的 代 码 如 下 :<br />

class objstate : objstate


end class objstate<br />

文 件 “objstate.pro” 的 代 码 如 下 :<br />

implement objstate<br />

open core, vpiDomains<br />

facts<br />

w:(integer, pnt) nondeterm. % worm<br />

d:(integer, integer) single. % direction<br />

clauses<br />

init() :-<br />

assert(w(1, pnt(110, 100))),<br />

assert(w(2, pnt(120, 100))), assert(w(3, pnt(130, 100))),<br />

assert(w(4, pnt(140, 100))), assert(w(5, pnt(140, 100))).<br />

d(10, 0).<br />

mov() :-<br />

retract(w(1, P)), P= pnt(X1, Y1),<br />

d(DX, DY), P1= pnt(X1+DX, Y1+DY), assert(w(1, P1)),<br />

retract(w(2, P2)), assert(w(2, P)),<br />

retract(w(3, P3)), assert(w(3, P2)),<br />

retract(w(4, P4)), assert(w(4, P3)),<br />

retract(w(5, _)), assert(w(5, P4)), !.<br />

mov().<br />

segm(rct(X, Y, X+10, Y+10)) :- w(_, pnt(X, Y)).<br />

turn(DX, DY) :- assert(d(DX, DY)).<br />

end implement objstate<br />

在 playground 下 创 建 class “draw”, 这 次 去 掉 “Create Objects” 选 项 。 其 代 码 如 下 。 文 件<br />

“draw.cl” 代 码 :<br />

class draw<br />

open core, vpiDomains<br />

predicates<br />

classInfo : core::classInfo.<br />

snapshot:(windowGDI Win, objstate).<br />

end class draw<br />

文 件 “draw.pro” 代 码 :<br />

implement draw<br />

open core, vpiDomains, vpi<br />

class predicates<br />

draw:(windowHandle, objstate) procedure.<br />

clauses<br />

classInfo("playground/draw", "1.0").<br />

draw(W, S) :- S:segm(Rectangle),<br />

vpi::drawEllipse(W, Rectangle), fail.<br />

draw(_W, _S).


snapshot(Win, S) :-<br />

S:mov(), !,<br />

Rectangle= rct(0, 0, 200, 200),<br />

W= pictOpen(Rectangle),<br />

draw(W, S),<br />

Pict= pictClose(W),<br />

Win:pictDraw(Pict, pnt(10, 10), rop_SrcCopy).<br />

end implement draw<br />

增 加 一 个 class “click” 来 处 理 定 时 器 工 作 。 不 选 “Create Objects”。 文 件 “click.cl” 代 码 如<br />

下 :<br />

class click<br />

open core, vpiDomains<br />

predicates<br />

bip:(window).<br />

kill:().<br />

end class click<br />

文 件 “click.pro” 代 码 如 下 :<br />

implement click<br />

open core<br />

class facts<br />

tm:vpiDomains::timerId := null.<br />

clauses<br />

bip(W) :- tm := W:timerSet(500).<br />

kill() :- vpi::timerKill(tm).<br />

end implement click<br />

然 后 ,Build/Build 一 下 , 编 辑 lawn.frm, 进 行 如 下 设 置 :<br />

图 15.3.2 关 闭 动 作 设 置<br />

如 图 15.3.2 所 示 , 双 击 增 加 代 码 :<br />

predicates<br />

onDestroy : window::destroyListener.


clauses<br />

onDestroy(_Source) :- click::kill().<br />

图 15.3.3 显 示 动 作 设 置<br />

如 图 15.3.3 所 示 , 双 击 增 加 代 码 如 下 :<br />

predicates<br />

onShow : window::showListener.<br />

class facts<br />

stt:objstate := objstate::new().<br />

clauses<br />

onShow(Parent, _CreationData) :-<br />

stt := objstate::new(),<br />

stt:init(), click::bip(Parent).<br />

图 15.3.4 设 置 Up 按 钮 的 代 码<br />

如 图 15.3.4 所 示 , 双 击 增 加 按 钮 Up 的 代 码 如 下 :<br />

predicates<br />

onUpClick : button::clickResponder.<br />

clauses<br />

onUpClick(_S) = button::defaultAction() :-<br />

stt:turn(0, -10).


图 15.3.5 向 下 按 钮 动 作 设 置<br />

如 图 15.3.5 所 示 , 双 击 增 加 代 码 如 下 :<br />

predicates<br />

onDownClick : button::clickResponder.<br />

clauses<br />

onDownClick(_S) = button::defaultAction() :-<br />

stt:turn(0, 10).<br />

图 15.3.6 向 左 按 钮 动 作 设 置<br />

如 图 15.3.6 所 示 , 双 击 增 加 代 码 如 下 :<br />

predicates<br />

onLeftClick : button::clickResponder.<br />

clauses<br />

onLeftClick(_S) = button::defaultAction() :-<br />

stt:turn(-10, 0).<br />

图 15.3.7 向 右 按 钮 动 作 设 置<br />

如 图 15.3.7 所 示 , 双 击 增 加 代 码 如 下 :


predicates<br />

onRightClick : button::clickResponder.<br />

clauses<br />

onRightClick(_S) = button::defaultAction() :-<br />

stt:turn(10, 0).<br />

图 15.3.8 定 时 器 动 作 设 置<br />

如 图 15.3.8 所 示 , 双 击 增 加 代 码 如 下 :<br />

predicates<br />

onTimer : window::timerListener.<br />

clauses<br />

onTimer(_Source, _TimerID) :-<br />

stt:mov(),<br />

R= rct(10, 10, 210, 210),<br />

invalidate(R).<br />

图 15.3.9 重 画 动 作 设 置<br />

如 图 15.3.9 所 示 , 双 击 增 加 代 码 如 下 :<br />

predicates<br />

onPaint : drawWindow::paintResponder.<br />

clauses<br />

onPaint(_Source, _Rectangle, GDIObject) :-<br />

draw::snapshot(GDIObject, stt).


运 行 图 如 图 15.3.10 所 示 。<br />

图 15.3.10 运 行 如 图 所 示<br />

第 15.4 节 游 戏 的 说 明<br />

在 draw.pro 里 ,snapshot/2 是 绘 出 虫 子 所 在 位 置 的 主 要 谓 词 。 每 次 执 行 它 , 首 先 执 行 S:mov(),<br />

它 代 表 虫 子 的 移 动 , 先 根 据 目 前 的 方 向 d(Dx,Dy), 来 进 行 状 态 改 变 , 然 后 , 画 出 它 的 位 置<br />

图 。<br />

snapshot(Win, S) :-<br />

S:mov(), !,<br />

Rectangle= rct(0, 0, 200, 200),<br />

W= pictOpen(Rectangle),<br />

draw(W, S),<br />

Pict= pictClose(W),<br />

Win:pictDraw(Pict, pnt(10, 10), rop_SrcCopy).<br />

ectangle= rct(0, 0, 200, 200), W= pictOpen(Rectangle) 语 句 , 是 在 Rectangle 正 方 形 区 域 内 ,<br />

开 辟 一 个 绘 图 区 域 , 代 号 为 W;draw(W, S) 则 是 在 这 个 绘 图 区 域 内 绘 图 , 也 即 绘 出 虫 子 的 位<br />

置 状 态 , 画 出 5 个 小 圆 圈 。 它 们 的 实 现 是 靠 draw/2 谓 词 实 现 的 :<br />

draw(W, S) :- S:segm(Rectangle),<br />

vpi::drawEllipse(W, Rectangle), fail.<br />

draw(_W, _S).<br />

draw/2 利 用 S:segm(Rectangle) 获 得 目 前 虫 子 的 位 置 正 方 形 一 个 10×10 的 正 方 形 , 然 后 调 用<br />

画 出 矩 形 中 之 椭 圆 方 法 , 绘 出 虫 子 的 5 个 圆 的 一 个 , 用 fail 强 制 回 溯 , 画 出 所 有 的 5 个 部 分 。<br />

而 S:segm(Rectangle) 描 述 了 虫 子 的 各 部 分 位 置 , 在 objState.pro 里 :<br />

segm(rct(X, Y, X+10, Y+10)) :- w(_, pnt(X, Y)).


每 次 刷 新 ,mov/0, 都 会 按 照 d/2 的 规 定 步 长 和 方 向 , 来 修 改 各 部 分 位 置 : 头 为 新 位 置 , 其<br />

他 部 分 后 边 的 集 成 前 边 一 节 的 位 置 。init/0 设 定 了 虫 子 初 始 位 置 。<br />

clauses<br />

init() :-<br />

assert(w(1, pnt(110, 100))),<br />

assert(w(2, pnt(120, 100))), assert(w(3, pnt(130, 100))),<br />

assert(w(4, pnt(140, 100))), assert(w(5, pnt(140, 100))).<br />

d(10, 0).<br />

mov() :-<br />

retract(w(1, P)), P= pnt(X1, Y1),<br />

d(DX, DY), P1= pnt(X1+DX, Y1+DY), assert(w(1, P1)),<br />

retract(w(2, P2)), assert(w(2, P)),<br />

retract(w(3, P3)), assert(w(3, P2)),<br />

retract(w(4, P4)), assert(w(4, P3)),<br />

retract(w(5, _)), assert(w(5, P4)), !.<br />

mov().<br />

所 以 可 以 看 到 , 上 下 左 右 控 制 按 钮 , 只 是 改 变 d/2 而 已 。<br />

predicates<br />

onDestroy : window::destroyListener.<br />

clauses<br />

onDestroy(_Source) :- click::kill().<br />

predicates<br />

onShow : window::showListener.<br />

class facts<br />

stt:objstate := objstate::new().<br />

clauses<br />

onShow(Parent, _CreationData) :-<br />

stt := objstate::new(),<br />

stt:init(), click::bip(Parent).<br />

predicates<br />

onUpClick : button::clickResponder.<br />

clauses<br />

onUpClick(_S) = button::defaultAction() :-<br />

stt:turn(0, -10).<br />

predicates<br />

onDownClick : button::clickResponder.<br />

clauses<br />

onDownClick(_S) = button::defaultAction() :-<br />

stt:turn(0, 10).<br />

predicates<br />

onLeftClick : button::clickResponder.


clauses<br />

onLeftClick(_S) = button::defaultAction() :-<br />

stt:turn(-10, 0).<br />

predicates<br />

onRightClick : button::clickResponder.<br />

clauses<br />

onRightClick(_S) = button::defaultAction() :-<br />

stt:turn(10, 0).<br />

predicates<br />

onTimer : window::timerListener.<br />

clauses<br />

onTimer(_Source, _TimerID) :-<br />

stt:mov(),<br />

R= rct(10, 10, 210, 210),<br />

invalidate(R).<br />

定 时 器 的 设 置 是 在 click.pro 里 :<br />

class facts<br />

tm:vpiDomains::timerId := null.<br />

clauses<br />

bip(W) :- tm := W:timerSet(500).<br />

kill() :- vpi::timerKill(tm).<br />

而 这 样 就 把 定 时 器 和 lawn.frm 关 联 起 来 :<br />

predicates<br />

onShow : window::showListener.<br />

class facts<br />

stt:objstate := objstate::new().<br />

clauses<br />

onShow(Parent, _CreationData) :-<br />

stt := objstate::new(),<br />

stt:init(), click::bip(Parent).<br />

第 15.5 节 游 戏 的 改 进<br />

我 们 可 以 修 改 使 虫 子 到 达 边 界 , 就 停 止 不 动 。 在 objState.pro 里 修 改 mov/0 谓 词 如 下 :<br />

mov() :-<br />

w(1, P),


P= pnt(X1, Y1), d(DX, DY),<br />

Xp1=X1+DX, Yp1=Y1+DY,<br />

not(Xp1199),not(Yp1199),<br />

P1= pnt(Xp1,Yp1 ),<br />

retract(w(1, P)),assert(w(1, P1)),<br />

retract(w(2, P2)), assert(w(2, P)),<br />

retract(w(3, P3)), assert(w(3, P2)),<br />

retract(w(4, P4)), assert(w(4, P3)),<br />

retract(w(5, _)), assert(w(5, P4)), !.<br />

mov().<br />

注 意 我 们 增 加 了 边 界 判 断 语 句 not(Xp1199),not(Yp1199).<br />

而 我 们 重 新 设 计 lawn.frm 增 加 显 示 分 数 的 score 控 件 , 如 图 15.5.1 所 示 。<br />

图 15.5.1 增 加 计 分 控 件<br />

然 后 修 改 lawn.pro 里 的 onPaint/3 如 下 :<br />

predicates<br />

onPaint : drawWindow::paintResponder.<br />

clauses<br />

onPaint(_Source, _Rectangle, GDIObject) :-<br />

draw::snapshot(GDIObject, stt),<br />

stt : getscore(X), Xx=string::format("%",X),<br />

score_ctl : settext(Xx).<br />

修 改 文 件 objState.i 如 下 , 增 加 getScore/1 谓 词 :<br />

interface objstate<br />

open core, vpiDomains<br />

predicates<br />

init:().<br />

turn:(integer, integer).<br />

mov:().<br />

segm:(rct) nondeterm (o).


getscore:(integer)procedure (o).<br />

end interface objstate<br />

文 件 ibjStae.pro 里 修 改 move/0, 增 加 score 事 实 , 增 加 语 句 getScore/1, 修 改 后 文 件 如 下 :<br />

implement objstate<br />

open core, vpiDomains<br />

facts<br />

w:(integer, pnt) nondeterm. % worm<br />

d:(integer, integer) single. % direction<br />

score :integer :=0.<br />

clauses<br />

init() :-<br />

assert(w(1, pnt(110, 100))),<br />

assert(w(2, pnt(120, 100))), assert(w(3, pnt(130, 100))),<br />

assert(w(4, pnt(140, 100))), assert(w(5, pnt(140, 100))).<br />

d(10, 0).<br />

mov() :-<br />

w(1, P),<br />

P= pnt(X1, Y1), d(DX, DY),<br />

Xp1=X1+DX, Yp1=Y1+DY,<br />

not(Xp1199),not(Yp1199),<br />

P1= pnt(Xp1,Yp1 ),<br />

retract(w(1, P)),assert(w(1, P1)),<br />

retract(w(2, P2)), assert(w(2, P)),<br />

retract(w(3, P3)), assert(w(3, P2)),<br />

retract(w(4, P4)), assert(w(4, P3)),<br />

retract(w(5, _)), assert(w(5, P4)),<br />

score:=score+100,<br />

!.<br />

mov():-<br />

score:=0,<br />

stdio::write("\nChange, change, it comes to the boundary!\n Score cleared.").<br />

segm(rct(X, Y, X+10, Y+10)) :- w(_, pnt(X, Y)).<br />

turn(DX, DY) :- assert(d(DX, DY)).<br />

clauses<br />

getscore(score).<br />

end implement objstate<br />

这 样 运 行 起 来 如 图 15.5.2 所 示 。


图 15.5.2 带 计 分 牌 的 虫 子 爬 游 戏<br />

第 15.6 节 实 体 事 实 Object Facts<br />

我 们 通 常 在 一 个 class 里 声 明 class facts, 比 如 前 面 用 到 的 :<br />

class facts<br />

city:(string Name, pnt Position).<br />

conn:(pnt, pnt).<br />

clauses<br />

city("Salt Lake", pnt(30, 40)).<br />

city("Logan", pnt(100, 120)).<br />

city("Provo", pnt(100, 160)).<br />

conn(pnt(30, 40) , pnt(100, 120)).<br />

conn(pnt(100, 120), pnt(100, 160)).<br />

这 些 事 实 , 可 以 被 class 里 所 有 的 obejcts 实 体 用 到 。 实 体 谓 词 (obejct predicates), 通 常 是 指<br />

那 些 在 *.i 文 件 里 声 明 的 那 些 谓 词 , 如 果 改 变 了 这 些 事 实 , 则 , 模 块 里 所 有 实 体 都 会 接 受 到<br />

这 种 改 变 。 这 样 , 比 如 前 面 的 虫 子 游 戏 , 如 果 用 class facts 来 保 存 虫 子 状 态 , 则 当 一 个 游 戏<br />

结 束 , 另 外 一 个 要 重 新 开 始 , 会 发 现 虫 子 不 在 初 始 状 态 位 置 。 不 同 的 实 体 之 间 , 会 共 享 class<br />

facts。 最 好 的 办 法 是 声 明 成 “facts”, 而 不 是 “class facts”。<br />

facts<br />

w:(integer, pnt) nondeterm. % worm<br />

d:(integer, integer) single. % direction<br />

clauses<br />

init() :- assert(w(1, pnt(110, 100))),


d(10, 0).<br />

assert(w(2, pnt(120, 100))), assert(w(3, pnt(130, 100))),<br />

assert(w(4, pnt(140, 100))), assert(w(5, pnt(140, 100))).<br />

所 以 说 ,“facts” 声 明 的 事 实 , 可 以 称 为 实 体 事 实 “Objects Facts”, 它 们 是 实 体 私 有 的 ,<br />

而 不 是 同 一 类 共 有 的 特 性 。 这 一 点 , 对 于 深 入 理 解 class 很 重 要 。<br />

第 十 六 章 几 个 有 趣 的 部 件<br />

这 一 章 , 我 们 介 绍 结 果 有 趣 的 部 件 。<br />

第 16.1 节 TabControl 的 应 用<br />

创 建 一 个 Project, 要 求 如 下 :<br />

Name: tabForm<br />

UI Stategy: Object GUI<br />

然 后 , 点 击 菜 单 项 File/Add, 从 <strong>Prolog</strong> 安 装 位 置 的 目 录 开 始 , 找 到 如 下 文 件 :<br />

pfc\gui\controls\tabControl\tabControl.pack<br />

得 到 如 图 16.1.1 所 示 的 情 形 。<br />

图 16.1.1 加 入 tabControl 到 当 前 程 序


第 16.1.1 节 创 建 控 件<br />

点 击 File/New in Existing Package, 然 后 选 择 左 边 的 Controls 选 项 , 建 立 一 个 控 件 “helloTab”,<br />

如 图 16.1.2 所 示 。<br />

图 16.1.2 建 立 控 件 helloTab<br />

增 加 一 个 Edit 控 件 、 一 个 按 钮 , 如 图 16.1.3 所 示 , 设 计 helloTab 控 件 。<br />

图 16.1.3 设 计 helloTab 控 件 , 当 前 文 件 结 构 如 图 16.1.4 所 示<br />

图 16.1.4 程 序 文 件 树 图 状 态<br />

然 后 ,Build/Build 一 下 程 序 , 这 时 程 序 文 件 结 构 图 如 16.1.5 所 示 。 然 后 双 击 进 入 编 辑<br />

helloTab.ctl, 对 按 钮 “Your Name” 进 行 ClickResponder 事 件 处 理 器 设 置 为 onYourNameClick,<br />

增 加 程 序 代 码 到 helloTab.pro 里 , 如 下 :


predicates<br />

onYourNameClick : button::clickResponder.<br />

clauses<br />

onYournameClick(_Source) = button::defaultAction :-<br />

Name= edit_ctl:getText(),<br />

Ans= string::concat("Hello, ", Name, "!\n"),<br />

stdio::write(Ans).<br />

图 16.1.6 设 置 ClickResponder 为 onYourNameClick<br />

图 16.1.5 Build 后 的 文 件 结 构 图<br />

和 这 个 控 件 创 建 方 法 一 样 , 我 们 创 建 一 个 “fibTab” 控 件 如 图 16.1.6 所 示 。 为 Fib 按 钮 增 加<br />

代 码 如 下 所 示 :<br />

class predicates<br />

fibo:(integer, integer) procedure (i, o).<br />

clauses<br />

fibo(N, F) :-<br />

if N


Num= edit_ctl:getText(),<br />

I= toTerm(Num), fibo(I, F),<br />

Ans= string::format("fibo(%d)= %d", I, F),<br />

edit_ctl:setText(Ans).<br />

图 16.1.6 fibTab 的 设 计 样 式<br />

第 16.1.2 使 用 TabControl<br />

我 们 已 经 创 建 了 两 个 控 件 , 我 们 希 望 通 过 TabControl 把 他 们 组 合 在 一 起 。 首 先 创 建 一 个 名 字<br />

为 tab 的 Form。 并 通 过 插 入 Custom Control 的 方 法 把 TabControl 放 在 对 话 框 里 , 如 图 16.1.7<br />

所 示 。<br />

图 16.1.7 增 加 TabControl 到 tab Form 里<br />

Build/Build 一 下 程 序 , 在 目 录 树 里 点 击 tab.pro, 右 边 会 显 示 其 谓 词 列 表 , 如 图 16.1.8 所 示 双<br />

击 new/1 谓 词 , 然 后 进 入 tab.pro, 发 现 new/1 原 来 是 这 样 的 :<br />

clauses<br />

new(Parent):-<br />

formWindow::new(Parent),<br />

generatedInitialize().


图 16.1.8 双 击 tab.pro 的 new 谓 词 , 进 入 该 文 件 相 应 谓 词 位 置<br />

我 们 修 改 new/1 的 代 码 为 如 下 :<br />

clauses<br />

new(Parent):-<br />

formWindow::new(Parent),<br />

generatedInitialize(),<br />

% add tabControl content<br />

Page1 = tabPage::new(),<br />

Tab1 = helloTab::new(Page1:getContainerControl()),<br />

Page1:setText(Tab1:getText()),<br />

tabControl_ctl:addPage(Page1),<br />

Page2 = tabPage::new(),<br />

Tab2 = fibTab::new(Page2:getContainerControl()),<br />

Page2:setText(Tab2:getText()),<br />

tabControl_ctl:addPage(Page2),<br />

succeed.<br />

然 后 修 改 任 务 菜 单 File/New 使 它 能 够 操 作 , 并 增 加 代 码 如 下 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

W= tab::new(S), W:show().<br />

运 行 一 下 , 点 击 File/New, 看 看 我 们 得 到 如 图 16.1.9 所 示 。


图 16.1.9 得 到 2 个 页 面 的 Tab<br />

图 16.1.10 第 一 个 页 面 输 入 名 字 按 按 钮 得 到 问 候 , 第 二 个 页 面 可 以 对 输 入 数 字 进 行 运 算<br />

第 16.2 节 ListControl 的 应 用<br />

为 了 学 习 语 言 , 西 方 语 言 有 很 多 语 气 、 时 态 的 变 化 比 如 :<br />

Latin<br />

Spanish<br />

Present Imperfect Present Imperfect<br />

amo amabam amo amaba<br />

amas amabas amas amabas<br />

amat amabat ama amaba<br />

amamus amabamus amamos amabamos<br />

我 们 来 设 计 一 个 对 照 查 询 系 统 。<br />

我 们 创 建 一 个 “myTab” 的 GUI project, 然 后 创 建 一 个 “tabPackage” 包 , 在 其 下 建 立 一 个<br />

控 件 “verbTab”, 如 图 16.2.1 所 示 ( 因 为 要 使 用 editorControl, 所 以 在 应 用 Custom Control 之<br />

前 要 用 File/Add 加 入 “pfc\gui\controls\editorControl\editorControl.pack”)。 注 意 , 这 些<br />

控 件 的 代 码 名 称 分 别 设 置 为 :<br />

listVerb_ctl : listEdit.<br />

mood_ctl : editControl.<br />

tense_ctl : editControl.<br />

check_ctl : button.<br />

editorControl_ctl : editorcontrol.


图 16.2.1 verbTab 的 设 计<br />

我 们 如 图 16.2.2 所 示 , 对 verbTab 控 件 的 showListener 进 行 设 置 , 并 双 击 后 增 加 如 下 代 码 :<br />

predicates<br />

onShow : window::showListener.<br />

clauses<br />

onShow(_Source, _Data) :-<br />

VerbList= listVerb_ctl : tryGetVpiWindow(),<br />

verbs::allVerbs(L),<br />

vpi::lboxAdd(VerbList, L),<br />

vpi::lboxSetSel(VerbList, 0, 1), !,<br />

verbs:: get_verb_state(Tense, Mood),<br />

mood_ctl:setText(Mood),<br />

tense_ctl:setText(Tense).<br />

onShow(_Source, _Data).<br />

图 16.2.2 verbTab 的 显 示 事 件 设 置<br />

关 于 listEdit Control 的 使 用 , 一 下 几 点 需 要 知 道 :<br />

获 得 控 件 相 对 应 的 Window, 要 用 :<br />

“VerbList= listVerb_ctl : tryGetVpiWindow()”<br />

要 获 得 当 前 选 中 的 项 目 的 索 引 要 用 :<br />

“IV= vpi::lboxGetSelIndex(VerbList)”


删 除 某 一 个 索 引 号 对 应 的 项 目 , 要 用 :<br />

“vpi::lboxDelete (VerbList, IV)”<br />

要 获 得 列 表 中 所 有 的 项 目 总 数 , 则 用 到 :<br />

“C= vpi::lboxCountAll(VerbList)”。<br />

当 然 , 我 们 还 要 给 check 按 钮 增 加 代 码 :<br />

predicates<br />

onCheckClick : button::clickResponder.<br />

clauses<br />

onCheckClick(_Source) = button::defaultAction :-<br />

Txt= editorControl_ctl:getEntireText(),<br />

Verb= listVerb_ctl:getText(),<br />

VerbList= listVerb_ctl : tryGetVpiWindow(),<br />

IV= vpi::lboxGetSelIndex(VerbList), !,<br />

Mood= mood_ctl:getText(),<br />

Tense= tense_ctl:getText(),<br />

verbs::pres_root_conj(Verb, Tense, Mood, L),<br />

if template::check_it(Txt, L, Ans) then<br />

editorControl_ctl:pasteStr(" "),<br />

vpi::lboxDelete (VerbList, IV) ,<br />

C= vpi::lboxCountAll(VerbList),<br />

if C= 0 then<br />

verbs::next_verb_state(),<br />

verbs::get_verb_state(NewTense, NewMood),<br />

mood_ctl:setText(NewMood),<br />

tense_ctl:setText(NewTense),<br />

verbs::allVerbs(NewList),<br />

vpi::lboxAdd(VerbList, NewList)<br />

else<br />

succeed()<br />

end if,<br />

vpi::lboxSetSel(VerbList, 0, 1)<br />

else<br />

template::check_present(Txt, L, Ans),<br />

editorControl_ctl:pasteStr(Ans)<br />

end if.<br />

onCheckClick(_Source) = button::defaultAction.


图 16.2.3 设 置 check 按 钮 的 点 击 动 作 侦 听 器<br />

我 们 建 立 另 外 一 个 Control, 名 字 叫 做 “declensionTab”, 如 图 16.2.4 所 示 。 这 个 控 件 , 它 和<br />

前 面 建 立 的 verbTab 模 样 近 似 , 我 们 可 以 用 Copy 的 方 法 , 复 制 某 些 控 件 及 其 布 局 , 方 法 是<br />

打 开 前 者 按 住 Ctrl 键 , 用 鼠 标 点 击 所 有 控 件 , 选 中 后 按 “Ctrl+C”, 到 新 的 对 话 框 里 , 按<br />

“Ctrl+V”。 这 是 一 个 技 巧 。<br />

图 16.2.4 declensionTab 控 件 的 布 局 设 计<br />

增 加 check 按 钮 的 ClickResponder 为 onCheckClick, 代 码 如 下 :<br />

predicates<br />

onCheckClick : button::clickResponder.<br />

clauses<br />

onCheckClick(_Source) = button::defaultAction :-<br />

Txt= editorControl_ctl:getEntireText(),<br />

Num= num_ctl:getText(),<br />

W= word_list_ctl:getText(),<br />

WordList=word_list_ctl: tryGetVpiWindow(),<br />

IV= vpi::lboxGetSelIndex( WordList), !,<br />

cases::declension(W, Num, L),<br />

if template::check_it(Txt, L, Ans) then<br />

editorControl_ctl:pasteStr(" "),<br />

vpi::lboxDelete (WordList, IV) ,<br />

C= vpi::lboxCountAll(WordList),<br />

if C=0 then


cases::next_number_state(),<br />

cases::get_number_state(NewNum),<br />

num_ctl:setText(NewNum),<br />

cases::allWords(NewWs),<br />

vpi::lboxAdd(WordList, NewWs)<br />

else<br />

succeed()<br />

end if,<br />

vpi::lboxSetSel(WordList, 0, 1)<br />

else<br />

template::check_present(Txt, L, Ans),<br />

editorControl_ctl:pasteStr(Ans)<br />

end if.<br />

onCheckClick(_Source) = button::defaultAction.<br />

而 整 个 控 件 显 示 ShowListener 设 为 onShow, 代 码 如 下 :<br />

predicates<br />

onShow : window::showListener.<br />

clauses<br />

onShow(_Source, _Data) :-<br />

cases::get_number_state(Number),<br />

num_ctl:setText(Number),<br />

WordList= word_list_ctl : tryGetVpiWindow(),<br />

cases::allWords(Ws),<br />

vpi::lboxAdd(WordList,Ws),<br />

vpi::lboxSetSel(WordList, 0, 1), !.<br />

onShow(_Source, _Data).<br />

注 意 这 里 几 个 控 件 的 代 码 名 字 为 :<br />

facts<br />

check_ctl : button.<br />

editorControl_ctl : editorcontrol.<br />

word_list_ctl : listEdit.<br />

num_ctl : editControl.<br />

同 时 , 我 们 还 要 建 立 第 三 个 控 件 “pronunciationTab”。 设 计 如 图 16.2.5 所 示 。


图 16.2.5 pronunciationTab 控 件 增 加 play 按 钮<br />

这 个 控 件 onShow 代 码 如 下 :<br />

predicates<br />

onShow : window::showListener.<br />

clauses<br />

onShow(_Source, _Data) :-<br />

EditListHandle= listEdit_ctl : tryGetVpiWindow(), !,<br />

vpi::lboxAdd(EditListHandle, ["joubert", "newtoni", "newtonii", "newtoniii"]).<br />

onShow(_Source, _Data).<br />

而 按 钮 play 的 onPlayClick 代 码 如 下 :<br />

predicates<br />

onPlayClick : button::clickResponder.<br />

clauses<br />

onPlayClick(_Source) = button::defaultAction :-<br />

File= listEdit_ctl:getText(),<br />

_ = playSound(File,<br />

null, /*snd_nodefault*/ snd_async+snd_filename).<br />

控 件 内 的 两 个 部 件 命 名 :<br />

facts<br />

listEdit_ctl : listEdit.<br />

play_ctl : button.<br />

这 个 控 件 完 成 从 列 表 框 里 选 择 声 音 文 件 然 后 播 放 , 播 放 声 音 。 用 到 playSound/3 谓 词 , 但 这<br />

是 一 个 外 部 程 序 , 我 们 需 要 在 控 件 开 头 声 明 :<br />

resolve playSound externally<br />

然 后 还 要 增 加 如 下 定 义 :<br />

domains<br />

soundFlag = unsigned32.


class predicates<br />

playSound : (string Sound,<br />

pointer HModule,<br />

soundFlag SoundFlag)<br />

-> booleanInt Success<br />

language apicall.<br />

constants<br />

snd_sync : soundFlag = 0x0000.<br />

/* play synchronously (default) */<br />

snd_async : soundFlag = 0x0001.<br />

/* play asynchronously */<br />

snd_nodefault : soundFlag = 0x0002.<br />

/* silence (!default) if sound not found */<br />

snd_memory : soundFlag = 0x0004.<br />

/* Sound points to a memory file */<br />

snd_loop : soundFlag = 0x0008.<br />

/* loop the sound until next sndplaysound */<br />

snd_nostop : soundFlag = 0x0010.<br />

/* don't stop any currently playing sound */<br />

snd_nowait : soundFlag = 0x00002000.<br />

/* don't wait if the driver is busy */<br />

snd_alias : soundFlag = 0x00010000.<br />

/* name is a registry alias */<br />

snd_alias_id : soundFlag = 0x00110000.<br />

/* alias is a predefined id */<br />

snd_filename : soundFlag = 0x00020000.<br />

/* name is file name */<br />

snd_resource : soundFlag = 0x00040004.<br />

/* name is resource name or atom */<br />

snd_purge : soundFlag = 0x0040.<br />

/* purge non-static events for task */<br />

snd_application : soundFlag = 0x0080.<br />

/* look for application specific association */<br />

只 有 这 样 , 播 放 声 音 才 能 正 常 工 作 , 它 是 调 用 Windows API 程 序 。 这 个 例 子 对 于 我 们 有 很<br />

重 要 的 参 考 价 值 。 然 后 创 建 一 个 “query”Form, 其 中 , 和 第 16.1 节 一 样 ,File/Add 加 入<br />

tabControl, 然 后 Build/Build 一 下 啊 , 在 编 辑 query.frm, 插 入 tabControl, 其 界 面 如 图 16.2.6<br />

所 示 。


图 16.2.6 query 插 入 tabControl<br />

把 query.pro 里 new/1 语 句 修 改 为 如 下 :<br />

clauses<br />

new(Parent):-<br />

formWindow::new(Parent),<br />

generatedInitialize(),<br />

Page1= tabPage::new(),<br />

_Pronunciation = pronunciationTab::new(Page1:getContainerControl()),<br />

Page1:setText("Pronunciation"),<br />

tabControl_ctl:addPage(Page1),<br />

Page2= tabPage::new(),<br />

_Declension = declensionTab::new(Page2:getContainerControl()),<br />

Page2:setText("Declension"),<br />

tabControl_ctl:addPage(Page2),<br />

Page3= tabPage::new(),<br />

_Verb = verbTab::new(Page3:getContainerControl()),<br />

Page3:setText("Verbs"),<br />

tabControl_ctl:addPage(Page3).<br />

我 们 还 要 增 加 几 个 相 应 的 模 块 ,verbs、cases、template, 它 们 都 要 去 掉 “Create Objects” 选<br />

项 。 其 相 应 的 代 码 如 下 罗 列 :<br />

% verbs.cl<br />

class verbs<br />

open core<br />

predicates<br />

classInfo : core::classInfo.<br />

% @short Class information predicate.<br />

% @detail This predicate represents information predicate of this class.<br />

% @end<br />

read:(string File) procedure (i).<br />

allVerbs:(string* L) procedure (o).<br />

pres_root_conj:(string, string, string, string*) procedure (i, i, i, o).


get_verb_state:(string, string) procedure (o, o).<br />

next_verb_state:() procedure.<br />

end class verbs<br />

%verbs.pro<br />

implement verbs<br />

open core, string<br />

constants<br />

className = "verbs/verbs".<br />

classVersion = "".<br />

domains<br />

conj= prima; secunda; tertia; quarta; mixta.<br />

class facts - verb_base<br />

verb:(string, string, string, string*) nondeterm.<br />

presRoot:(string, string, conj) nondeterm.<br />

verb_state:(string, string).<br />

class predicates<br />

dic_entry:(string) nondeterm (o).<br />

pres_ind:(string, string*) procedure (i, o).<br />

pres_subj:(string, string*) procedure (i, o).<br />

imperf_ind:(string, string*) procedure (i, o).<br />

imperf_subj:(string, string*) procedure (i, o).<br />

clauses<br />

classInfo(className, classVersion).<br />

verb_state("pres", "ind").<br />

verb_state("pres", "subj").<br />

verb_state("imperf", "ind").<br />

verb_state("imperf", "subj").<br />

pres_root_conj(V, "pres", "ind", L) :- !, pres_ind(V, L).<br />

pres_root_conj(V, "imperf", "ind", L) :- !, imperf_ind(V, L).<br />

pres_root_conj(V, "pres", "subj", L) :- !, pres_subj(V, L).<br />

pres_root_conj(V, "imperf", "subj", L) :- !, imperf_subj(V, L).<br />

pres_root_conj(_, _, _, []).<br />

pres_ind(V, L) :- verb(V, "ind", "pres", L), !.<br />

pres_ind(V, L) :- presRoot(V, Rt, prima), !,<br />

A1= concat(Rt, "O"), A2= concat(Rt, "AS"), A3= concat(Rt, "AT"),<br />

A4= concat(Rt, "AMUS"), A5= concat(Rt, "ATIS"), A6= concat(Rt, "ANT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_ind(V, L) :- presRoot(V, Rt, secunda), !,<br />

A1= concat(Rt, "O"), A2= concat(Rt, "S"), A3= concat(Rt, "T"),<br />

A4= concat(Rt, "MUS"), A5= concat(Rt, "TIS"), A6= concat(Rt, "NT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_ind(V, L) :- presRoot(V, Rt, tertia), !,<br />

A1= concat(Rt, "O"), A2= concat(Rt, "IS"), A3= concat(Rt, "IT"),<br />

A4= concat(Rt, "IMUS"), A5= concat(Rt, "ITIS"), A6= concat(Rt, "UNT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_ind(V, L) :- presRoot(V, Rt, quarta), !,<br />

A1= concat(Rt, "IO"), A2= concat(Rt, "IS"), A3= concat(Rt, "IT"),


A4= concat(Rt, "IMUS"), A5= concat(Rt, "ITIS"), A6= concat(Rt, "IUNT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_ind(V, L) :- presRoot(V, Rt, mixta), !,<br />

A1= concat(Rt, "IO"), A2= concat(Rt, "IS"), A3= concat(Rt, "IT"),<br />

A4= concat(Rt, "IMUS"), A5= concat(Rt, "ITIS"), A6= concat(Rt, "IUNT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_ind(_V, []).<br />

pres_subj(V, L) :- verb(V, "subj", "pres", L), !.<br />

pres_subj(V, L) :- presRoot(V, Rt, prima), !,<br />

A1= concat(Rt, "EM"), A2= concat(Rt, "ES"), A3= concat(Rt, "ET"),<br />

A4= concat(Rt, "EMUS"), A5= concat(Rt, "ETIS"), A6= concat(Rt, "ENT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_subj(V, L) :- presRoot(V, Rt, secunda), !,<br />

A1= concat(Rt, "AM"), A2= concat(Rt, "AS"), A3= concat(Rt, "AT"),<br />

A4= concat(Rt, "AMUS"), A5= concat(Rt, "ATIS"), A6= concat(Rt, "ANT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_subj(V, L) :- presRoot(V, Rt, tertia), !,<br />

A1= concat(Rt, "AM"), A2= concat(Rt, "AS"), A3= concat(Rt, "AT"),<br />

A4= concat(Rt, "AMUS"), A5= concat(Rt, "ATIS"), A6= concat(Rt, "ANT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_subj(V, L) :- presRoot(V, Rt, quarta), !,<br />

A1= concat(Rt, "IAM"), A2= concat(Rt, "IAS"), A3= concat(Rt, "IAT"),<br />

A4= concat(Rt, "IAMUS"), A5= concat(Rt, "IATIS"), A6= concat(Rt, "IANT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_subj(V, L) :- presRoot(V, Rt, mixta), !,<br />

A1= concat(Rt, "IAM"), A2= concat(Rt, "IAS"), A3= concat(Rt, "IAT"),<br />

A4= concat(Rt, "IAMUS"), A5= concat(Rt, "IATIS"), A6= concat(Rt, "IANT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

pres_subj(_V, []).<br />

imperf_ind(V, L) :- verb(V, "ind", "imperf", L), !.<br />

imperf_ind(V, L) :- presRoot(V, Rt, prima), !,<br />

A1= concat(Rt, "ABAM"), A2= concat(Rt, "ABAS"), A3= concat(Rt, "ABAT"),<br />

A4= concat(Rt, "ABAMUS"), A5= concat(Rt, "ABATIS"), A6= concat(Rt, "ABAN<br />

T"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

imperf_ind(V, L) :- presRoot(V, Rt, secunda), !,<br />

A1= concat(Rt, "BAM"), A2= concat(Rt, "BAS"), A3= concat(Rt, "BAT"),<br />

A4= concat(Rt, "BAMUS"), A5= concat(Rt, "BATIS"), A6= concat(Rt, "BANT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

imperf_ind(V, L) :- presRoot(V, Rt, tertia), !,<br />

A1= concat(Rt, "EBAM"), A2= concat(Rt, "EBAS"), A3= concat(Rt, "EBAT"),<br />

A4= concat(Rt, "EBAMUS"), A5= concat(Rt, "EBATIS"), A6= concat(Rt, "EBAN<br />

T"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

imperf_ind(V, L) :- presRoot(V, Rt, quarta), !,<br />

A1= concat(Rt, "IEBAM"), A2= concat(Rt, "IEBAS"), A3= concat(Rt, "IEBAT"),<br />

A4= concat(Rt, "IEBAMUS"), A5= concat(Rt, "IEBATIS"), A6= concat(Rt, "IEBA<br />

NT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

imperf_ind(V, L) :- presRoot(V, Rt, mixta), !,<br />

A1= concat(Rt, "IEBAM"), A2= concat(Rt, "IEBAS"), A3= concat(Rt, "IEBAT"),<br />

A4= concat(Rt, "IEBAMUS"), A5= concat(Rt, "IEBATIS"), A6= concat(Rt, "IEBA<br />

NT"),<br />

L= [A1, A2, A3, A4, A5, A6].


imperf_ind(_V, []).<br />

imperf_subj(V, L) :- verb(V, "subj", "imperf", L), !.<br />

imperf_subj(V, L) :- presRoot(V, Rt, prima), !,<br />

A1= concat(Rt, "AREM"), A2= concat(Rt, "ARES"), A3= concat(Rt, "ARET"),<br />

A4= concat(Rt, "AREMUS"), A5= concat(Rt, "ARETIS"), A6= concat(Rt, "AREN<br />

T"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

imperf_subj(V, L) :- presRoot(V, Rt, secunda), !,<br />

A1= concat(Rt, "REM"), A2= concat(Rt, "RES"), A3= concat(Rt, "RET"),<br />

A4= concat(Rt, "REMUS"), A5= concat(Rt, "RETIS"), A6= concat(Rt, "RENT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

imperf_subj(V, L) :- presRoot(V, Rt, tertia), !,<br />

A1= concat(Rt, "EREM"), A2= concat(Rt, "ERES"), A3= concat(Rt, "ERET"),<br />

A4= concat(Rt, "EREMUS"), A5= concat(Rt, "ERETIS"), A6= concat(Rt, "ERENT<br />

"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

imperf_subj(V, L) :- presRoot(V, Rt, quarta), !,<br />

A1= concat(Rt, "IREM"), A2= concat(Rt, "IRES"), A3= concat(Rt, "IRET"),<br />

A4= concat(Rt, "IREMUS"), A5= concat(Rt, "IRETIS"), A6= concat(Rt, "IRENT"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

imperf_subj(V, L) :- presRoot(V, Rt, mixta), !,<br />

A1= concat(Rt, "EREM"), A2= concat(Rt, "ERES"), A3= concat(Rt, "ERET"),<br />

A4= concat(Rt, "EREMUS"), A5= concat(Rt, "ERETIS"), A6= concat(Rt, "ERENT<br />

"),<br />

L= [A1, A2, A3, A4, A5, A6].<br />

imperf_subj(_V, []).<br />

dic_entry(V) :- verb(V, "ind", "pres", _).<br />

dic_entry(V) :- presRoot(V, _, _).<br />

verb("SUM", "ind", "pres", ["SUM", "ES", "EST", "SUMUS", "ESTIS", "SUNT"]).<br />

verb("SUM", "ind", "imperf", ["ERAM", "ERAS", "ERAT", "ERAMUS", "ERATIS", "ERANT"]).<br />

verb("VOLO", "ind", "pres", ["VOLO", "VIS", "VULT", "VOLUMUS", "VULTIS", "VOLUNT"]).<br />

verb("VOLO", "ind", "imperf", ["VOLEBAM", "VOLEBAS", "VOLEBAT", "VOLEBAMUS", "V<br />

OLEBATIS", "VOLEBANT"]).<br />

verb("SUM", "subj", "pres", ["SIM", "SIS", "SIT", "SIMUS", "SITIS", "SINT"]).<br />

verb("SUM", "subj", "imperf", ["ESSEM", "ESSES", "ESSET", "ESSEMUS", "ESSETIS", "ESSE<br />

NT"]).<br />

verb("VOLO", "subj", "pres", ["VELIM", "VELIS", "VELIT", "VELIMUS", "VELITIS", "VELIN<br />

T"]).<br />

verb("VOLO", "subj", "imperf", ["VELLEM", "VELLES", "VELLET", "VELLEMUS", "VELLET<br />

IS", "VELLENT"]).<br />

presRoot("AMO", "AM", prima).<br />

presRoot("VIDEO", "VIDE", secunda).<br />

read(File) :-<br />

retractFactDb(verb_base),<br />

file::consult(File, verb_base).<br />

allVerbs(L) :- L= [Q || dic_entry(Q)].<br />

next_verb_state() :- retract(verb_state(_,_)), !.<br />

next_verb_state().<br />

get_verb_state(X, Y) :- verb_state(X, Y), !.<br />

get_verb_state("none", "none").


end implement verbs<br />

%cases.cl<br />

class cases<br />

open core<br />

predicates<br />

classInfo : core::classInfo.<br />

% @short Class information predicate.<br />

% @detail This predicate represents information predicate of this class.<br />

% @end<br />

read:(string File) procedure (i).<br />

allWords:(string* L) procedure (o).<br />

declension:(string, string, string*) procedure (i, i, o).<br />

get_number_state:(string) procedure (o).<br />

next_number_state:() procedure.<br />

end class cases<br />

%cases.pro<br />

implement cases<br />

open core, string<br />

constants<br />

className = "cases/cases".<br />

classVersion = "".<br />

domains<br />

declens= prma; snda; trtum; trtium; trtii; qrta; qnta; nsnda; ntrtum; ntrtium.<br />

class facts - noun_base<br />

wrd:(string, string, string*) nondeterm.<br />

decl:(string, string, declens) nondeterm.<br />

num_state:(string).<br />

clauses<br />

classInfo(className, classVersion).<br />

num_state("singular").<br />

num_state("plural").<br />

wrd("ROSA", "singular", ["ROSA", "ROSAE", "ROSAE", "ROSA", "ROSAM"]).<br />

wrd("ROSA", "plural", ["ROSAE", "ROSARUM", "ROSIS", "ROSIS", "ROSAS"]).<br />

wrd("PUER", "singular", ["PUER", "PUERI", "PUERO", "PUERO", "PUERUM"]).<br />

wrd("PUER", "plural", ["PUERI", "PUERORUM", "PUERIS", "PUERIS", "PUEROS"<br />

]).<br />

decl("DOMINUS", "DOMIN", snda).<br />

declension(W, N, L) :- wrd(W, N, L), !.<br />

declension(W, "singular", L) :- decl(W, Root, prma), !,<br />

N= W, G= concat(Root, "AE"), D= concat(Root, "AE"),<br />

Abl= concat(Root, "A"), Acc= concat(Root, "AM"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "plural", L) :- decl(W, Root, prma), !,


N= concat(Root, "AE"), G= concat(Root, "ARUM"), D= concat(Root, "IS"),<br />

Abl= concat(Root, "IS"), Acc= concat(Root, "AS"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "singular", L) :- decl(W, Root, snda), !,<br />

N= W, G= concat(Root, "I"), D= concat(Root, "O"),<br />

Abl= concat(Root, "O"), Acc= concat(Root, "UM"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "plural", L) :- decl(W, Root, snda), !,<br />

N= concat(Root, "I"), G= concat(Root, "ORUM"), D= concat(Root, "IS"),<br />

Abl= concat(Root, "IS"), Acc= concat(Root, "OS"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "singular", L) :- decl(W, Root, nsnda), !,<br />

N= W, G= concat(Root, "I"), D= concat(Root, "O"),<br />

Abl= concat(Root, "O"), Acc= concat(Root, "UM"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "plural", L) :- decl(W, Root, nsnda), !,<br />

N= concat(Root, "A"), G= concat(Root, "ORUM"), D= concat(Root, "IS"),<br />

Abl= concat(Root, "IS"), Acc= concat(Root, "A"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "singular", L) :- decl(W, Root, trtium), !,<br />

N= W, G= concat(Root, "IS"), D= concat(Root, "I"),<br />

Abl= concat(Root, "E"), Acc= concat(Root, "EM"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "plural", L) :- decl(W, Root, trtium), !,<br />

N= concat(Root, "ES"), G= concat(Root, "IUM"), D= concat(Root, "IBUS"),<br />

Abl= concat(Root, "IBUS"), Acc= concat(Root, "ES"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "singular", L) :- decl(W, Root, trtum), !,<br />

N= W, G= concat(Root, "IS"), D= concat(Root, "I"),<br />

Abl= concat(Root, "E"), Acc= concat(Root, "EM"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "plural", L) :- decl(W, Root, trtum), !,<br />

N= concat(Root, "ES"), G= concat(Root, "UM"), D= concat(Root, "IBUS"),<br />

Abl= concat(Root, "IBUS"), Acc= concat(Root, "ES"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "singular", L) :- decl(W, Root, ntrtum), !,<br />

N= W, G= concat(Root, "IS"), D= concat(Root, "I"),<br />

Abl= concat(Root, "E"), Acc= W,<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "plural", L) :- decl(W, Root, ntrtum), !,<br />

N= concat(Root, "A"), G= concat(Root, "UM"), D= concat(Root, "IBUS"),<br />

Abl= concat(Root, "IBUS"), Acc= concat(Root, "A"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "singular", L) :- decl(W, Root, ntrtium), !,<br />

N= W, G= concat(Root, "IS"), D= concat(Root, "I"),<br />

Abl= concat(Root, "I"), Acc= W,<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "plural", L) :- decl(W, Root, ntrtium), !,<br />

N= concat(Root, "IA"), G= concat(Root, "IUM"), D= concat(Root, "IBUS"),<br />

Abl= concat(Root, "IBUS"), Acc= concat(Root, "IA"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "singular", L) :- decl(W, Root, trtii), !,<br />

N= W, G= concat(Root, "IS"), D= concat(Root, "I"),<br />

Abl= concat(Root, "I"), Acc= concat(Root, "EM"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(W, "plural", L) :- decl(W, Root, trtum), !,<br />

N= concat(Root, "ES"), G= concat(Root, "IUM"), D= concat(Root, "IBUS"),


Abl= concat(Root, "IBUS"), Acc= concat(Root, "ES"),<br />

L= [N, G, D, Abl, Acc].<br />

declension(_W, _, []).<br />

read(File) :-<br />

retractFactDb(noun_base),<br />

file::consult(File, noun_base).<br />

allWords(Wrds) :- Wrds= [Q || wrd(Q, "singular", _); decl(Q, _, _)].<br />

next_number_state() :- retract(num_state(_)), !.<br />

next_number_state().<br />

get_number_state(X) :- num_state(X), !.<br />

get_number_state("none").<br />

end implement cases<br />

%template.cl<br />

class template<br />

open core<br />

predicates<br />

classInfo : core::classInfo.<br />

% @short Class information predicate.<br />

% @detail This predicate represents information predicate of this class.<br />

% @end<br />

check_present:(string, string*, string) procedure (i, i, o).<br />

check_it:(string, string*, string) determ (i,i,o).<br />

end class template<br />

%template.pro<br />

implement template<br />

open core<br />

constants<br />

className = "template/template".<br />

classVersion = "".<br />

class facts<br />

points:integer := 0.<br />

class predicates<br />

compare:(string*, string*, string, string) procedure (i, i, i, o).<br />

checkAnswer:(string, string, string) procedure (i, i, o).<br />

clauses<br />

classInfo(className, classVersion).<br />

checkAnswer(X, Y, Ans) :- string::equalIgnoreCase(X, Y), !, Ans= string::concat(X, "="<br />

, Y, "\n").


checkAnswer(X, Y, Ans) :- Ans= string::concat(X, "", Y, "\n"), points := points+1, st<br />

dio::write("Points= ", points), stdio::nl.<br />

compare([""|R], L, Partial, Ans) :- !, compare(R, L, Partial, Ans).<br />

compare([X|Xs], [Y|Ys], Partial, Ans) :- !,<br />

checkAnswer(X, Y, T),<br />

PP= string::concat(Partial, T),<br />

compare(Xs, Ys, PP, Ans).<br />

compare([], [], Partial, Ans) :- !, Ans= Partial.<br />

compare(_, _, Partial, Ans) :- Ans= string::concat(Partial, "\nsomething is wrong").<br />

check_present(Txt, L, Ans) :- Ts= string::split(Txt, " \n"),<br />

compare(Ts, L, "", Ans).<br />

check_it(Txt, L, Ans) :- points := 0,<br />

Ts= string::split(Txt, " \n"),<br />

compare(Ts, L, "", Ans),<br />

points=0.<br />

end implement template<br />

我 们 创 建 一 个 needs 包 , 把 这 三 个 模 块 放 在 其 中 , 最 后 修 改 File/New, 增 加 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

W= query::new(S), W:show().<br />

还 要 把 需 要 调 用 的 几 个 声 音 文 件 , 放 到 exe 文 件 夹 里 , 如 图 16.2.7 所 示 。<br />

图 16.2.7 声 音 文 件 放 到 exe 文 件 夹 里<br />

运 行 一 下 啊 。 我 们 看 到 一 个 TAB 框 , 既 可 以 验 证 你 的 答 案 , 也 可 以 播 放 声 音 。<br />

第 16.3 节 treeView Control、listView Control<br />

这 一 节 , 通 过 例 子 我 们 来 接 触 treeView Control 的 使 用 。 我 们 设 计 一 个 浏 览 <strong>Prolog</strong> 源 文 件 的<br />

对 话 框 。 建 立 一 个 工 程 “proFileViewer”。 创 建 一 个 包 “viewerDialog”, 在 其 下 建 立 一 个


“browserDialog” 的 Dialog, 如 图 16.3.1 所 示 。<br />

图 16.3.1 创 建 browserDialog,rct(20,20,372,396) 设 置 外 形 参 数<br />

用 “File/Add” 逐 个 加 入 treeViewControl、EditorControl、MessageControl、listViewControl<br />

后 ,Build/Build 一 下 , 设 计 成 如 图 16.3.2 所 示 外 形 。<br />

图 16.3.2 设 计 对 话 框 外 形<br />

我 们 用 Code Expert 增 加 onShow 代 码 到 taskWindow.pro 里 , 如 下 :<br />

predicates<br />

onShow : window::showListener.<br />

clauses<br />

onShow(_, _CreationData):-<br />

BrowserDialog = browserDialog::new(This),<br />

BrowserDialog:show(),<br />

destroy().


运 行 一 下 , 界 面 如 图 16.3.3, 直 接 进 入 对 话 框 ; 而 当 我 们 关 闭 对 话 框 , 则 程 序 也 关 闭 。 这 个<br />

技 巧 , 是 我 们 去 掉 程 序 外 壳 的 一 个 办 法 。<br />

图 16.3.3 界 面 图<br />

然 后 我 们 把 各 个 控 件 定 义 成 如 下 名 字 ( 当 然 可 以 按 照 自 己 的 爱 好 ):<br />

facts<br />

ok_ctl : button.<br />

location_ctl : editControl.<br />

go_ctl : button.<br />

treeView_ctl : treeviewcontrol.<br />

listView_ctl : listviewcontrol.<br />

editor_ctl : editorcontrol.<br />

msg_ctl : messagecontrol.<br />

设 置 onShow, 在 显 示 对 话 框 时 的 代 码 :<br />

predicates<br />

onShow : window::showListener.<br />

clauses<br />

onShow(_Source, _CreationData) :-<br />

setIcon(application_icon),<br />

_ = onGo(go_ctl).<br />

而 onGo 按 钮 , 是 对 输 入 框 的 地 址 输 入 做 出 的 反 应 :<br />

predicates<br />

onGo : button::clickResponder.


clauses<br />

onGo(_Source) = button::defaultAction() :-<br />

treeView_ctl:deleteItem(wcc_Null),<br />

listView_ctl:deleteAllItems(),<br />

cursorSet(cursor_Wait),<br />

checkValidPath(),<br />

!,<br />

processDirectory(location_ctl:getText(), wcc_null),<br />

cursorSet(cursor_Arrow),<br />

treeView_ctl:expand(treeView_ctl:getChildItem(wcc_null)),<br />

treeView_ctl:select(treeView_ctl:getChildItem(wcc_null)).<br />

onGo(_) = button::defaultAction :-<br />

vpiCommonDialogs::error(<br />

string::format("Cannot show \"%s\" directory!", location_ctl:getText())).<br />

在 这 里 我 们 可 以 看 到 :<br />

treeView_ctl:deleteItem(wcc_Null),<br />

listView_ctl:deleteAllItems(),<br />

cursorSet(cursor_Wait),<br />

是 对 树 形 浏 览 器 、 列 表 浏 览 器 清 空 、 设 置 光 标 。 然 后 检 查 地 址 输 入 的 是 否 合 适 ? 用 到 内 部 谓<br />

词 checkValidPath() 其 代 码 如 下 :<br />

predicates<br />

checkValidPath : () determ.<br />

clauses<br />

checkValidPath() :-<br />

Text = location_ctl:getText(),<br />

trap( string::front(Text, string::length(Text) - 1, Text1, End),<br />

_TraceId,fail),<br />

End = "\\",<br />

location_ctl:setText(Text1),<br />

fail.<br />

checkValidPath() :-<br />

directory::existDirectory(location_ctl:getText()).<br />

然 后 是 processDirectory/2 处 理 对 当 前 目 录 的 显 示 等 :<br />

predicates<br />

processDirectory : (string DirectoryToInspect, treeViewControl::itemId ParentId).<br />

clauses<br />

processDirectory(DirectoryToInspect, ParentId):-<br />

ItemId = getNextId(),


treeView_ctl:insertItem(ItemId, ParentId, treeViewControl::sorted(),<br />

nameOrFullName(ParentId, DirectoryToInspect),<br />

[/*treeViewControl::expanded()*/], resId(idb_OpenFileBitmap), resId(idb_Ope<br />

nFileBitmap)),<br />

SubDirectory = directory::getSubDirectories_nd(DirectoryToInspect),<br />

SubDirectoryShort = fileName::getName(SubDirectory),<br />

SubDirectoryShort "",<br />

SubDirectoryShort ".",<br />

processDirectory(SubDirectory, ItemId),<br />

fail.<br />

processDirectory(_, _).<br />

predicates<br />

nameOrFullName : (treeViewControl::itemId, string) -> string procedure.<br />

clauses<br />

nameOrFullName(wcc_null, FullName) = FullName :-<br />

!.<br />

nameOrFullName(_, Directory) = Name :-<br />

filename::getPathAndName(Directory, _, Name).<br />

predicates<br />

addFiles : (string DirectoryToInspect).<br />

clauses<br />

addFiles(DirectoryToInspect):-<br />

File = directory::getFilesInDirectory_nd(DirectoryToInspect),<br />

file::getFileProperties(File, _, Size, _, _, GmtTimeValue),<br />

Time = time::newFromGMT(GmtTimeValue),<br />

Size = unsigned64(Size1,_),<br />

fileName::getPathAndName(File, _, Name),<br />

Id = getNextId(),<br />

FT = Time:formatTime(),<br />

listView_ctl:insertItem(item(Id, Name,<br />

idb_NewFileBitmap, %resid(idb_NewFileBitmap),<br />

[], [FT, toString(Size1)])),<br />

fail.<br />

addFiles(_).<br />

facts<br />

lastId : treeViewControl::itemId := 10.<br />

predicates<br />

getNextId : () -> treeViewControl::itemId.<br />

clauses<br />

getNextId() = lastId :-<br />

lastId := lastId + 1.


要 对 Dialogue 的 new() 进 行 修 改 :<br />

clauses<br />

new(Parent) :-<br />

dialog::new(Parent),<br />

generatedInitialize(),<br />

stdio::setOutputStream(msg_ctl:getOutputStream()),<br />

listView_ctl:insertColumnList(1, columnList),<br />

listView_ctl:setLvType(lvs_report),<br />

listView_ctl:setStyle([lvs_showselalways,lvs_editlabels,lvs_autoarrange,lvs_singl<br />

esel]),<br />

listView_ctl:addSelectEndListener(onListSelectEnd),<br />

listView_ctl:setSelectBeginResponder(onListSelectBegin),<br />

treeView_ctl:addSelectEndListener(onTreeSelect),<br />

location_ctl:setText(initialPath(directory::getCurrentDirectory())),<br />

editor_ctl:setFileName("file.pro"). % for coloring<br />

然 后 对 listControl 进 行 设 置 :<br />

constants<br />

columnList : listViewControl::column_list = [<br />

column("File",100,alignleft),<br />

column("Time",70,alignright),<br />

column("Size",70,alignright) ].<br />

prologExtensions : core::string_list = ["cl","i","pro","pack","ph","txt"].<br />

predicates<br />

onListSelectEnd : listViewControl::selectEndListener.<br />

clauses<br />

onListSelectEnd(List, ItemId, true) :-<br />

item(_,Name,_,_,_) = List:getItem(ItemId),<br />

File = string::concat(folder_FullPath(treeView_ctl:getSelected()), @"\", Name),<br />

file::existFile(File),<br />

editor_ctl:pasteStr(""),<br />

trap(String = file::readString(File, _), _TraceId,<br />

fail),<br />

!,<br />

editor_ctl:pasteStr(String).<br />

onListSelectEnd(_, _, _).<br />

predicates<br />

onListSelectBegin : listViewControl::selectBeginResponder.<br />

clauses<br />

onListSelectBegin(List, ItemId, true) = listViewControl::acceptSelect :-


item(_,Name,_,_,_) = List:getItem(ItemId),<br />

member(string::toLowerCase(filename::getExtension(Name)), prologExtensions),<br />

!.<br />

onListSelectBegin(_, _, true()) = listViewControl::cancelSelect() :-<br />

!.<br />

onListSelectBegin(_, _, _) = listViewControl::acceptSelect().<br />

图 16.3.4 设 置 ListViewControl 的 选 择<br />

再 对 treeViewControl 进 行 设 置 :<br />

predicates<br />

onTreeSelect : treeViewControl::selectEndListener.<br />

clauses<br />

onTreeSelect(_, _, ItemId) :-<br />

ItemId wcc_null,<br />

!,<br />

DirectoryToFill = folder_FullPath(ItemId),<br />

listView_ctl:deleteAllItems(),<br />

cursorSet(cursor_Wait),<br />

addFiles(DirectoryToFill),<br />

cursorSet(cursor_Arrow).<br />

onTreeSelect(_,_,_).<br />

predicates<br />

folder_FullPath : (treeViewControl::itemId) -> string procedure.<br />

clauses<br />

folder_FullPath(ItemId) = Text :-<br />

wcc_null = treeView_ctl:getParentItem(ItemId),<br />

!,<br />

treeView_ctl:getItem(ItemId, Text, _, _, _).<br />

folder_FullPath(ItemId) = string::concat(ParentPath, "\\", Text) :-<br />

ParentPath = folder_FullPath(treeView_ctl:getParentItem(ItemId)),<br />

treeView_ctl:getItem(ItemId, Text, _, _, _).<br />

class predicates


member : (string, string_list) determ.<br />

clauses<br />

member(S, [S|_]) :-<br />

!.<br />

member(S, [_|L]) :-<br />

member(S, L).<br />

图 16.3.5 设 置 treeViewControl 的 选 择 后 动 作<br />

然 后 , 把 open 语 句 修 改 为 :<br />

open core, vpiDomains, resourceIdentifiers, listViewControl, treeViewControl, stdio<br />

增 加 initialPath/1<br />

predicates<br />

initialPath : (string) -> string.<br />

clauses<br />

initialPath(S) = InitialPath :-<br />

Path = fileName::getPath(S),<br />

Len = string::length(Path),<br />

Len > 2,<br />

string::front(Path, Len-1, InitialPath, Slash),<br />

Slash = "\\",<br />

!.<br />

initialPath(S) = S.<br />

这 样 就 可 以 了 。 运 行 如 图 所 示 。


图 16.3.6 运 行 效 果 , 浏 览 文 件<br />

第 十 七 章 动 画<br />

我 们 还 要 复 习 使 用 GDIobjects 来 实 现 动 画 。 我 们 创 建 一 个 project, 描 述 如 下 :<br />

Project Name: rattlesnake<br />

UI Strategy: Object-oriented GUI (pfc/gui)<br />

创 建 一 个 包 “snake”。 在 此 包 内 创 建 一 个 Form, 名 字 “canvas”。 然 后 建 立 一 个 class, 名 字<br />

“click”, 注 意 不 选 中 “Create Objects”。 这 个 模 块 的 代 码 如 下 :<br />

%click.cl<br />

class click<br />

open core<br />

predicates<br />

bip:(window).<br />

kill:(window).<br />

end class click<br />

%click.pro<br />

implement click<br />

open core<br />

class facts<br />

tm:vpiDomains::timerId := null.<br />

clauses


ip(W) :- tm := W:timerSet(1000).<br />

kill(W) :- W:timerKill(tm).<br />

end implement click<br />

这 个 模 块 是 一 个 定 时 器 模 块 。 设 定 1000ms 触 发 一 次 ,bip/1 来 实 现 ;kill/1 来 清 除 定 时 器 。<br />

然 后 我 们 操 作 “ TaskWindow.win/CodeExpert/Menu/TaskMenu/id_file/id_file_new ” 设 置<br />

File/New 菜 单 项 :<br />

predicates<br />

onFileNew : window::menuItemListener.<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

F= canvas::new(S), F:show(), click::bip(F).<br />

第 17.1 节<br />

绘 图<br />

我 们 创 建 “dopaint”class 来 处 理 绘 图 。 文 件 dopaint.cl 如 下 :<br />

class dopaint<br />

open core<br />

predicates<br />

classInfo : core::classInfo.<br />

draw:(windowGDI).<br />

invalidrectangle:(vpiDomains::rct) procedure (o).<br />

end class dopaint<br />

而 文 件 “dopaint.pro” 如 下 :<br />

implement dopaint<br />

open core, vpiDomains<br />

constants<br />

className = "snake/snakestate".<br />

classVersion = "".<br />

class facts<br />

yesno:integer := 0.<br />

class predicates<br />

flipflop:(picture Picture,<br />

picture Mask) determ (o, o).<br />

clauses<br />

classInfo(className, classVersion).


flipflop(Pict, Mask) :- yesno= 0,<br />

yesno := 1,<br />

Pict= vpi::pictLoad("figs\\n0.bmp"),<br />

Mask= vpi::pictLoad("figs\\n0Mask.bmp"), !.<br />

flipflop(Pict, Mask) :- yesno= 1,<br />

yesno := 0,<br />

Pict= vpi::pictLoad("figs\\n1.bmp"),<br />

Mask= vpi::pictLoad("figs\\n1Mask.bmp").<br />

draw(W) :-<br />

P= vpi::pictLoad("figs\\frogs.bmp"),<br />

W:pictDraw(P, pnt(10, 10), rop_SrcCopy),<br />

flipflop(Snake, Mask), !,<br />

W:pictDraw(Mask, pnt(40, 50), rop_SrcAnd),<br />

W:pictDraw(Snake, pnt(40, 50), rop_SrcInvert).<br />

draw(_).<br />

invalidRectangle(rct(40, 50, 100, 100)).<br />

end implement dopaint<br />

我 么 还 要 准 备 几 个 图 片 文 件 放 在 “exe\figs” 目 录 之 下 , 如 图 17.1.1 所 示 。<br />

图 17.1.1 相 关 的 5 个 图 片<br />

第 17.2 节 定 时 器 的 使 用<br />

在 canvas 面 板 启 动 时 , 应 该 启 动 定 时 器 :<br />

clauses<br />

onFileNew(S, _MenuTag) :-<br />

F= canvas::new(S), F:show(), click::bip(F).


在 canvas 关 闭 时 , 也 应 该 销 毁 定 时 器 :<br />

predicates<br />

onDestroy : window::destroyListener.<br />

clauses<br />

onDestroy(W) :- click::kill(W).<br />

而 定 时 器 出 发 时 , 执 行 onTimer 任 务 :<br />

predicates<br />

onTimer : window::timerListener.<br />

clauses<br />

onTimer(_Source, _TimerID) :-<br />

dopaint::invalidRectangle(R),<br />

invalidate(R).<br />

绘 图 任 务 是 由 onPaint 来 执 行 的 , 它 调 用 dopaint 的 draw/1 谓 词 实 现 绘 图 :<br />

predicates<br />

onPaint : drawWindow::paintResponder.<br />

clauses<br />

onPaint(_Source, _Rectangle, GDIObject) :-<br />

dopaint::draw(GDIObject).<br />

这 些 事 件 都 要 在 canvas.frm 的 Events 对 话 框 设 置 , 如 图 :


图 17.2.1 设 置 和 定 时 器 工 作 有 关 的 事 件 侦 听 器<br />

第 17.3 节 动 画 是 怎 么 实 现 的 ?<br />

执 行 一 下 , 如 图 17.3.1 所 示 。<br />

图 17.3.1 每 隔 一 秒 钟 , 这 两 幅 图 画 交 替 出 现 , 仿 佛 蛇 在 扭 头 的 动 画<br />

谓 词 draw/1 在 绘 制 图 形 :<br />

flipflop(Pict, Mask) :- yesno= 0,


yesno := 1,<br />

Pict= vpi::pictLoad("figs\\n0.bmp"),<br />

Mask= vpi::pictLoad("figs\\n0Mask.bmp"), !.<br />

flipflop(Pict, Mask) :- yesno= 1,<br />

yesno := 0,<br />

Pict= vpi::pictLoad("figs\\n1.bmp"),<br />

Mask= vpi::pictLoad("figs\\n1Mask.bmp").<br />

draw(W) :-<br />

P= vpi::pictLoad("figs\\frogs.bmp"),<br />

W:pictDraw(P, pnt(10, 10), rop_SrcCopy),<br />

flipflop(Snake, Mask), !,<br />

W:pictDraw(Mask, pnt(40, 50), rop_SrcAnd),<br />

W:pictDraw(Snake, pnt(40, 50), rop_SrcInvert).<br />

draw(_).<br />

先 绘 青 蛙 背 景 , 然 后 判 断 该 画 哪 个 蛇 ,flipflop/2 是 根 据 yesno 标 志 , 来 确 定 应 该 用 那 两 张 图<br />

片 。Mask 在 背 景 上 挖 出 一 个 蛇 形 , 然 后 在 这 个 位 置 合 并 上 蛇 图 片 。 如 此 交 替 , 就 有 动 画 效<br />

果 了 。<br />

第 17.4 节 改 进 动 画 程 序<br />

我 们 设 置 一 个 鼠 标 事 件 :<br />

在 dopaint.pro 里 增 加 :<br />

class facts<br />

snake : pnt :=pnt(40,50).<br />

clauses<br />

setSnake(X):-snake:=X.<br />

修 改 :<br />

draw(W) :-<br />

P= vpi::pictLoad("figs\\frogs.bmp"),<br />

W:pictDraw(P, pnt(10, 10), rop_SrcCopy),<br />

flipflop(Snake, Mask), !,<br />

W:pictDraw(Mask, snake, rop_SrcAnd),<br />

W:pictDraw(Snake, snake, rop_SrcInvert).<br />

draw(_).<br />

invalidRectangle(rct(0, 0, 200, 200)).


在 dopaint.cl 里 增 加 setSnake/1:<br />

setSnake: (vpiDomains::pnt) procedure (i).<br />

在 canvas.frm 下 设 置 鼠 标 点 击 事 件 , 增 加 代 码 :<br />

predicates<br />

onMouseDown : drawWindow::mouseDownListener.<br />

clauses<br />

onMouseDown(_Source, Point, _ShiftControlAlt, _Button):-<br />

dopaint::setsnake(Point),<br />

invalidate().<br />

这 样 鼠 标 点 击 之 处 , 蛇 也 会 移 动 到 这 里 。 如 图 17.4.1 所 示 。<br />

图 17.4.1 点 击 后 蛇 位 置 移 动<br />

第 十 八 章 相 关 资 料 介 绍 、 索 引<br />

这 一 章 , 希 望 通 过 介 绍 一 下 相 关 的 资 料 , 希 望 能 对 学 习 者 , 有 进 一 步 的 帮 助 。 因 为 这 本 书 只<br />

是 起 步 , 而 更 进 一 步 的 学 习 , 是 一 个 不 断 积 累 的 过 程 。<br />

第 18.1 节 <strong>Prolog</strong> 书 籍<br />

我 这 里 对 <strong>Visual</strong> <strong>Prolog</strong> 5 的 读 者 或 者 爱 好 者 , 推 荐 这 样 几 本 书 , 但 恐 怕 也 很 难 买 到 了 。<br />

我 知 道 在 国 内 , 这 个 语 言 并 不 很 商 业 化 , 这 是 国 人 之 不 幸 也 。 本 人 此 小 书 , 不 过 稍 稍 弥 补 缺<br />

憾 而 已 。


《<strong>Visual</strong> <strong>Prolog</strong> 语 言 教 程 》<br />

作 者 : 雷 英 杰 , 张 雷 , 邢 清 华 , 孙 金 萍<br />

出 版 社 : 陕 西 科 学 技 术 出 版 社<br />

本 书 全 面 系 统 介 绍 <strong>Visual</strong> <strong>Prolog</strong> 语 言 及 其 编 程 。 全 书 共 分 四 个 部 分 , 第 一 部 分 简 短 介 绍<br />

<strong>Visual</strong> <strong>Prolog</strong> 可 视 化 开 发 环 境 ; 第 二 部 分 包 括 教 程 的 第 2 章 至 第 11 章 , 教 你 如 何 学 会 用 <strong>Visual</strong><br />

<strong>Prolog</strong> 编 程 ; 第 三 部 分 包 括 第 12 章 至 第 16 章 , 详 细 叙 述 <strong>Visual</strong> <strong>Prolog</strong> 的 预 定 义 特 性 ; 第 四<br />

部 分 包 括 第 17 章 至 第 18 章 , 完 整 而 系 统 地 叙 述 语 言 元 素 和 模 块 化 程 序 设 计 , 以 及 与 其 它 语<br />

言 的 接 口 。<br />

《Visaul <strong>Prolog</strong> 编 程 、 环 境 及 接 口 》<br />

作 者 : 雷 英 杰 , 邢 清 华 , 孙 金 萍 , 张 雷<br />

出 版 社 : 国 防 工 业 出 版 社<br />

出 版 时 间 :2004 年 1 月<br />

ISBN: 7118032824/TP848<br />

本 书 系 统 介 绍 <strong>Visual</strong><strong>Prolog</strong>(VP) 语 言 的 功 能 特 点 、 编<br />

程 方 法 、 开 发 环 境 及 编 程 接 口 。 全 书 分 为 三 篇 : 编 程 指<br />

南 、 开 发 环 境 和 编 程 接 口 。 第 一 篇 编 程 指 南 , 详 细 介 绍<br />

VP 编 程 方 法 , 叙 述 如 何 使 用 应 用 程 序 专 家 、 对 话 框 与 窗<br />

口 专 家 、 对 话 框 包 装 专 家 和 工 具 栏 专 家 等 代 码 专 家 , 以<br />

及 代 码 浏 览 器 、 资 源 标 识 符 浏 览 器 、 项 目 浏 览 器 、 图 形<br />

编 辑 器 和 菜 单 编 辑 器 等 工 具 进 行 智 能 化 应 用 软 件 的 工 程<br />

开 发 。 第 二 篇 开 发 环 境 , 详 细 介 绍 VP 的 开 发 环 境 VDE,<br />

包 括 项 目 管 理 代 码 浏 览 器 、 资 源 标 识 符 浏 览 器 等 实 用 程 序 和 应 用 程 序 专 家 、 资 源 编 辑 器 等 代<br />

码 专 家 及 帮 助 生 成 器 、VP 调 试 器 等 。 第 三 篇 编 程 接 口 , 详 细 介 绍 VP 的 编 程 接 口 VPI。 这<br />

个 VPI 是 专 为 <strong>Prolog</strong> 应 用 程 序 设 计 的 高 级 API, 基 本 上 不 受 平 台 限 制 , 其 内 容 包 括 处 理 诸<br />

如 窗 口 、 控 件 、 菜 单 及 事 件 等 GUI 元 素 的 所 有 VPI 谓 词 和 各 种 支 持 包 。<br />

《 人 工 智 能 (AI) 程 序 设 计 ( 面 向 对 象 语 言 )》<br />

作 者 : 雷 英 杰 , 邢 清 华 , 王 涛 等 编 著<br />

出 版 社 : 清 华 大 学 出 版 社<br />

出 版 时 间 : 2005-3-1<br />

本 书 主 要 介 绍 人 工 智 能 的 基 础 知 识 和 应 用 于 人 工 智 能 与 专 家 系 统 领 域 的 面 向 对 象 逻 辑<br />

程 序 设 计 语 言 <strong>Visual</strong> <strong>Prolog</strong> 等 内 容 。 第 1 部 分 主 要 介 绍 人 工 智 能 的 基 础 知 识 、 知 识 的 表 示 方<br />

法 以 及 AI 的 编 程 基 础 。 第 2 部 分 介 绍 <strong>Visual</strong><strong>Prolog</strong> 的 编 程 基 础 , 主 要 包 括 <strong>Visual</strong> <strong>Prolog</strong> 的<br />

类 与 对 象 机 制 、 程 序 结 构 、GUI 编 程 、 逻 辑 层 编 辑 、 数 据 层 编 程 、CGI 编 程 等 。 第 3 部 分 介<br />

绍 <strong>Visual</strong> <strong>Prolog</strong> 的 语 言 特 性 , 主 要 包 括 <strong>Visual</strong> <strong>Prolog</strong> 语 言 元 素 、<strong>Visual</strong> <strong>Prolog</strong> 数 据 元 素 、<strong>Visual</strong><br />

<strong>Prolog</strong> 程 序 元 素 以 及 <strong>Visual</strong> <strong>Prolog</strong> 与 其 他 编 程 语 言 接 口 等 。


第 18.2 节 本 书 的 例 子 程 序 的 获 得<br />

强 烈 建 议 , 读 者 自 己 按 照 书 中 叙 述 , 自 己 键 入 代 码 , 进 行 实 际 操 作 。 我 以 为 这 也 是 一 个<br />

很 好 的 学 习 过 程 。 但 是 如 果 读 者 希 望 节 省 时 间 , 可 以 发 邮 件 给 fan_gending@126.com , 注<br />

明 “ 索 求 例 子 程 序 源 代 码 ”, 我 们 会 通 过 邮 件 的 形 式 , 为 需 要 者 发 送 电 子 格 式 的 。 当 然 , 如<br />

果 您 下 载 <strong>Visual</strong> <strong>Prolog</strong> 7.1 不 顺 畅 , 我 们 也 可 提 供 帮 助 , 但 不 推 荐 这 样 , 因 为 这 个 属 于 <strong>PDC</strong><br />

的 发 布 。<br />

第 18.3 节 中 国 的 知 识 基 系 统 研 究<br />

中 国 有 大 量 的 计 算 机 大 学 毕 业 生 , 但 是 却 少 有 真 正 的 计 算 机 研 究 者 , 尤 其 是 软 件 的 理 想<br />

主 义 者 。 大 家 为 商 业 而 忙 碌 , 连 教 授 也 忙 于 捞 钱 了 。 所 以 , 在 中 国 , 知 识 基 系 统 的 研 究 还 基<br />

本 上 是 空 白 , 有 些 所 谓 的 成 就 , 也 不 过 是 山 中 无 老 虎 的 自 称 而 已 。 所 以 这 个 领 域 我 们 不 但 落<br />

后 国 外 几 十 年 , 而 且 是 大 大 地 可 惜 呀 。 我 们 有 这 么 多 人 , 我 们 最 应 该 善 于 做 知 识 基 系 统 , 我<br />

们 最 应 该 在 这 里 发 轫 超 越 式 发 展 , 但 是 很 不 幸 。 如 有 几 个 爱 好 者 能 够 作 为 星 星 之 火 , 也 许 在<br />

知 识 基 系 统 , 我 们 还 有 机 会 。 如 果 您 读 了 这 书 , 我 劝 您 挤 出 更 多 时 间 思 考 我 们 的 未 来 , 不 仅<br />

仅 把 这 作 为 嬉 闹 一 番 而 已 , 做 些 什 么 吧 。<br />

附 录 - 要 点 索 引<br />

自 序<br />

下 载 一 个 免 费 <strong>Visual</strong> <strong>Prolog</strong> 7 的 版 本<br />

<strong>Visual</strong> <strong>Prolog</strong> 爱 好 者 一 个 聚 会 的 交 流 的 平 台 。<br />

第 一 章 简 介<br />

创 建 一 个 新 的 程 序<br />

编 译 和 运 行 你 的 程 序<br />

Excute 菜 单 项<br />

把 程 序 的 显 示 标 题 改 成 我 的 名 字<br />

文 件 树<br />

程 序 的 文 件 和 目 录 列 表<br />

希 腊 人 发 明 了 逻 辑


<strong>Visual</strong> <strong>Prolog</strong> 遵 从 的 逻 辑 学 方 法<br />

第 二 章 Forms<br />

创 建 一 个 新 的 Form<br />

控 件 工 具 箱<br />

控 件 版 面 编 辑 安 置 工 具 箱<br />

任 务 菜 单 的 项 目 工 作 起 来<br />

用 Code Expert 添 加 程 序<br />

TaskWindow.win 文 件<br />

自 动 生 成 的 代 码<br />

亚 里 士 多 德 的 符 号 逻 辑<br />

三 段 论<br />

第 三 章 鼠 标 事 件<br />

增 加 鼠 标 按 下 事 件 侦 听 器<br />

query.frm 文 件<br />

进 入 对 话 框 Form 的 编 辑 状 态<br />

Form 编 辑 状 态 下 的 特 性 设 置 和 事 件 设 置 对 话 框<br />

EventsMouseDownLisener 侦 听 器<br />

重 画 功 能 onPaint<br />

说 明 文 字<br />

数 据 库 记 录 mousePoint<br />

保 存 鼠 标 点 击 时 的 坐 标 数 值<br />

类 型 pnt<br />

invalidate(R) 启 动 重 画 R 所 指 的 矩 形 区 域<br />

rct(X-8, Y-8, X+60, Y+8)<br />

处 理 一 个 事 件<br />

鼠 标 事 件 分 四 类<br />

布 尔 代 数<br />

论 证 的 形 式<br />

第 四 章 基 本 图 例<br />

工 程 factorial


VIP 编 程 环 境 IDE<br />

任 务 菜 单<br />

<strong>Prolog</strong> Setting 对 话 框<br />

目 录 树<br />

Code Expert 代 码 专 家<br />

通 过 树 形 结 构 来 选 择 具 体 编 码 的 项 目<br />

创 建 一 个 工 程 项 目<br />

程 序 包<br />

编 辑 控 件<br />

按 钮<br />

创 建 一 个 新 的 class<br />

设 置 控 件 属 性<br />

选 择 按 钮 事 件 的 反 应 器<br />

算 出 阶 乘 得 数<br />

谓 词 运 算<br />

第 五 章 Horn 语 句<br />

函 数<br />

函 子<br />

谓 词<br />

逻 辑 运 算 关 系<br />

求 解<br />

2 元 谓 词 ( 两 个 参 数 的 谓 词 )<br />

定 义 谓 词<br />

变 量<br />

draw 模 块 class<br />

答 案 不 惟 一<br />

一 个 采 用 多 解 谓 词 的 程 序<br />

利 用 多 解 特 性<br />

fail 的 强 制 回 溯 功 能<br />

Horn 句 子 分 为 两 种<br />

事 实<br />

规 则 语 句<br />

“:-” 符 号<br />

规 则 的 “ 头 ”<br />

规 则 的 “ 尾 ”<br />

规 则 的 “ 体 ”<br />

声 明<br />

谓 词 输 入 输 出 流 向 说 明<br />

事 实 (facts) 插 入 、 删 除<br />

open 语 句


determ<br />

procedure<br />

multi<br />

nondeterm<br />

class facts<br />

绘 图 谓 词<br />

drawLine/3<br />

drawEllipse/2<br />

GDI objects<br />

PaintResponder<br />

绘 图 语 句 的 引 用 有 很 大 不 同<br />

Horn 句 子 的 意 义<br />

第 六 章 控 制 台 应 用<br />

控 制 台 应 用 (Console Application)<br />

截 断<br />

stdio::read()<br />

stdio::write<br />

stdio::nl<br />

表<br />

表 头<br />

表 尾<br />

空 表<br />

匹 配<br />

计 算 元 素 之 和 :<br />

简 化 Reduction<br />

递 归 式<br />

两 个 表 的 点 积 (dotproduct)<br />

字 符 串 操 作<br />

<strong>Visual</strong> <strong>Prolog</strong> 里 处 理 数 据 结 构 问 题<br />

Fronttoken<br />

concatlist<br />

concat/2/3<br />

toterm<br />

hasdomain(real, IX)<br />

输 入 的 字 符 串 转 换 为 实 数<br />

“integer*”<br />

core::integer_list<br />

tostring<br />

format


格 式 化 输 出<br />

数 据 格 式 化 输 出<br />

adjustBehaviour<br />

adjustSide<br />

caseSensitivity<br />

把 字 符 串 内 多 余 的 空 格 去 掉<br />

谓 词 的 语 法<br />

Porolog 谓 词 描 述<br />

第 七 章 文 本 编 辑 器<br />

编 辑 器 控 件 editorControl<br />

建 立 文 本 编 辑 器 程 序<br />

File/Add 加 入 editorControl 控 件<br />

文 件 保 存<br />

保 存 文 件 对 话 框<br />

文 件 调 入<br />

编 辑 器 的 改 进<br />

四 个 角 都 和 对 话 框 的 形 状 关 联 起 来<br />

第 八 章 绘 图<br />

onPaint<br />

创 建 dopaint 模 块 , 不 选 “create objects”<br />

画 出 三 角 形<br />

brush/2<br />

PatStyle<br />

Color<br />

调 入 图 片<br />

画 出 不 带 背 景 的 鹰<br />

记 录 鼠 标 点 击 位 置<br />

鼠 标 点 击 之 处 , 鹰 也 动 来 动 去<br />

Custom Control<br />

不 会 超 出 我 们 画 的 边 界<br />

第 九 章 数 据 类 型


基 本 的 数 据 类 型<br />

integer ( 整 数 )<br />

real ( 实 数 )<br />

string ( 字 符 串 )<br />

symbol ( 符 号 )<br />

显 示 不 同 的 数 据 类 型<br />

集 合<br />

数 字 的 集 合<br />

展 示 一 个 自 动 形 成 的 表<br />

自 己 定 义 的 数 据 类 型<br />

格 式<br />

域<br />

domains 字 段<br />

定 义 表<br />

函 数 型 数 据 类 型<br />

计 算 某 一 天 是 星 期 几 的 程 序<br />

改 造 后 的 计 算 日 期 界 面<br />

第 十 章 如 何 用 <strong>Prolog</strong> 解 决 问 题<br />

搜 索 一 个 表 的 所 有 元 素<br />

寻 求 两 个 表 的 交 集 表<br />

两 个 表 的 合 并<br />

表 的 倒 序 排 列<br />

表 的 分 割<br />

表 的 快 速 排 序<br />

一 年 有 多 少 天 ?<br />

地 图 填 色 问 题<br />

四 色 定 理<br />

修 改 程 序 , 看 看 到 底 有 多 少 种 填 色 方 案<br />

翻 子 游 戏<br />

NIM 游 戏<br />

交 互 的 下 棋 过 程<br />

第 十 一 章 事 实<br />

用 facts 或 者 class facts 来 声 明 事 实<br />

数 据 库 记 录 的 生 成 和 保 存<br />

数 据 库 文 件 读 入 内 存


file::consult/2<br />

读 写 字 符 串 readString/writeString<br />

string::toUpperCase/1<br />

file::writeString、3<br />

stdio::readline()<br />

常 数<br />

resourceIdentifiers.res<br />

增 加 菜 单 项 目 侦 听 器<br />

Form 获 得 焦 点<br />

第 十 二 章 class 和 object<br />

存 取 社 会 保 障 号<br />

Object 的 事 实<br />

第 十 三 章 乌 龟 爬 和 语 言 处 理<br />

乌 龟 爬 行 程 序<br />

位 置 坐 标<br />

角 度 朝 向<br />

math::round/1<br />

cos/1<br />

sin/1<br />

assert/1<br />

画 出 正 三 角 形<br />

五 边 形<br />

五 角 星<br />

保 证 每 次 画 出 的 是 一 样 的 图 形<br />

在 不 同 的 位 置 , 画 出 不 同 的 图 形<br />

递 归<br />

复 杂 问 题 得 到 简 化<br />

利 用 递 归 方 法 画 复 杂 曲 线<br />

画 出 倾 斜 的 曲 线<br />

语 言 处 理


第 十 四 章 L- 系 统<br />

画 一 棵 树<br />

如 何 才 能 画 一 棵 树<br />

画 3 个 分 枝 最 简 单 的 图<br />

画 出 不 同 的 树<br />

改 变 树 的 形 状<br />

第 十 五 章 游 戏<br />

游 戏 描 述<br />

游 戏 设 计 前 的 分 析<br />

游 戏 编 程<br />

处 理 定 时 器<br />

关 闭 动 作 设 置<br />

显 示 动 作 设 置<br />

定 时 器 动 作 设 置<br />

重 画 动 作 设 置<br />

游 戏 的 说 明<br />

游 戏 的 改 进<br />

增 加 计 分 控 件<br />

带 计 分 牌 的 虫 子 爬 游 戏<br />

实 体 事 实 Object Facts<br />

第 十 六 章 几 个 有 趣 的 部 件<br />

TabControl 的 应 用<br />

创 建 控 件<br />

使 用 TabControl<br />

ListControl 的 应 用<br />

listEdit Control<br />

播 放 声 音<br />

playSound/3<br />

Windows API 程 序<br />

treeView Control<br />

listView Control<br />

open 语 句 修 改


第 十 七 章 动 画<br />

GDIobjects 来 实 现 动 画<br />

定 时 器 模 块<br />

设 定 1000ms 触 发 一 次<br />

绘 图<br />

准 备 几 个 图 片 文 件<br />

定 时 器 的 使 用<br />

canvas 面 板 启 动 时 , 启 动 定 时 器<br />

canvas 关 闭 时 , 也 应 该 销 毁 定 时 器<br />

定 时 器 触 发 时 , 执 行 onTimer 任 务<br />

动 画 是 怎 么 实 现 的 ?<br />

两 幅 图 画 交 替 出 现 , 仿 佛 蛇 在 扭 头 的 动 画<br />

改 进 动 画 程 序<br />

点 击 后 蛇 位 置 移 动<br />

第 十 八 章 相 关 资 料 介 绍 、 索 引<br />

《<strong>Visual</strong> <strong>Prolog</strong> 语 言 教 程 》<br />

《Visaul <strong>Prolog</strong> 编 程 、 环 境 及 接 口 》<br />

《 人 工 智 能 (AI) 程 序 设 计 ( 面 向 对 象 语 言 )》<br />

本 书 的 例 子 程 序 的 获 得<br />

中 国 的 知 识 基 系 统 研 究

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!