问题描述
1994 年,早在 OWL 的规范出现之前,Thomas R. Gruber 和 Gregory R. Olsen 就发布了 An Ontology for Engineering Mathematics。由于缺少任何特定于本体的语言,它是在 lisp 中实现的。 lisp 代码仍然可用(参见 example),但不能被现代 OWL 本体使用(也称为导入)。
使该本体适用于当今(基于 OWL)的工具链的最简单方法是什么?
解决方法
有两种可能的方法来解决这个问题:你想采用哪种方法部分取决于编写这个本体的系统是否仍然可以访问。这些方法对于拯救其他用 Lisp 编写的旧包很常见,所以我认为它们值得描述。
这个答案没有详细说明如何做到这一点,因为这个过程必然有些迭代和实验性。
软件是如何开发的(在 Lisp 中)
经常使人们感到困惑的一件事是,该系统根本没有真正在 Lisp 中实现。
解决计算机问题的一个很好的方法是首先实现一种易于讨论问题领域的语言。这是一种极其常见的方法:大量的大型系统涉及一种或多种专用语言。例如,驱动现代系统的所有小型配置文件格式实际上都是小型专用语言。任何曾经在大型软件系统上工作过的人都会遇到大量这样的事情。
而且它们中的大多数只会很糟糕,因为它们是由不知道自己在做什么的人编写的:他们不明白自己在设计一种语言,而是认为自己在做其他事情,通常涉及替换字符串与其他基于模式的字符串。
Lisp 是少数对此持开放态度的语言家族之一:在 Lisp 中解决问题的方式非常明确,就是通过编写一种可以更好地表示问题的语言。该语言(通常)在表面上看起来有点像 Lisp:它会使用带括号的前缀符号等等,但它也会包含在底层 Lisp 中根本不存在的结构。 (事实上,现代 Lisps 自身就是这样的语言:它们是在自身的更简单版本中实现的语言。)
因此,EngMath 系统是用一种在 Lisp 之上实现的语言编写的程序:它根本不是真正用 Lisp 编写的。该语言是 KIF 或 Ontolingua:我不明白这两者之间的关系。
第一步:有一个Common Lisp环境
这一切的第一步是拥有一个高质量的 Common Lisp 环境,并熟悉它的使用。我不会在这里推荐一个,因为我对此非常陌生,但有几个是免费提供的。
如果底层本体语言仍然可用
因此,如果底层语言的实现仍然有用,则拯救系统的第一种方法是使用的方法。我无法确定是否是这种情况:Stanford KSL 网站上的许多链接现在似乎无处可去,因此它可能仍然存在,也可能不存在。正如我上面所说,我认为底层语言是 KIF 或 Ontolingua(可能是 Ontolingua 是 KIF 的实现?不确定)。
方法如下。
- 获取要在 Lisp 中构建的底层本体语言。这可能很难,也可能不难:Common Lisp 一直非常稳定,但一些旧程序在语言的临时版本中编写得非常草率,并且取决于特定的实现特性。
- 让您的本体以这种重构语言加载。
- 用重构的语言测试它。
- 对本体对应的对象的实现有足够的了解,才能进行下一步。这将涉及阅读和试验底层本体语言的源代码。
- 编写一个程序(结合原生 Lisp 和底层本体语言),该程序可以遍历与本体对应的对象,并以您想要的某种格式(可能是 OWL?)打印它们
- 利润。
这将是大量的工作。请注意,如果您只想使用本体,则可以从上面的步骤 (3) 分支:一旦系统运行,您就可以使用本体。
如果底层本体语言仍然不可用
如果底层语言变得有效或真的不可用,您可以使用这种方法。这也可能是一种更好的方法,即使它仍然可用,如果您所有想做的只是转换。
- 了解底层语言中数据的表面表示是什么。如果语言是 KIF,则至少有 some papers 幸存下来,这可能会对您有所帮助。
- 说服 Lisp 阅读本体源代码。这可能并不像看起来那么难:看起来您发布链接的本体部分并没有使用任何特别奇怪的读取级别语法。有关这方面的更多信息,请参见下文。
- 编写一个假的 shim,允许 Lisp 构造与本体对应的假对象。这也应该相当简单:见下文。
- 一旦您拥有这些伪造的对象,请处理它们以发出合适的 OWL 输出。这将涉及对旧的本体语言有足够的了解,以便能够将用它编写的内容转化为 OWL。
- 利润。
这种方法仍然很痛苦,但可能不如第一种痛苦:如果原始系统丢失,这也是您所能做的。我曾用用古老(非常古老)的代数系统编写的程序完成过类似的工作。
第二种方法的一些例子
所有这些都使用 file you posted 作为数据(下面是 /tmp/engmath-sample.lisp
)。
首先检查它是否可以用这样的函数读取
(defun read-file-naively (f)
;; Just read all the forms in a file and return a list of them
(with-open-file (in f)
(loop for form = (read in nil in)
until (eql form in)
collect form)))
这样,(read-file-naively "/tmp/engmath-sample.lisp")
将返回一个相当长的列表。关键是它不会呕吐,所以这个文件至少可以被 Lisp 读取。
这里有一些代码可以稍微伪造一个本体:这足以成功加载示例文件:
;;;;
;;;
(defpackage :fake-ontolingua
(:use :cl)
(:export
#:clear-theories
#:find-theory
#:theory-name #:theory-supers #:theory-issues
#:theory-documentation #:theory-relations
#:frame-ontology
#:define-theory
#:in-theory
#:current-theory
#:current-relations
#:find-relation
#:clear-relations
#:relation-name #:relation-theory #:relation-arglist
#:relation-documentation #:relation-body
#:define-relation))
(defpackage :fake-ontolingua-user
(:nicknames :ontolingua-user)
(:use :cl :fake-ontolingua)) ;should it use CL?
(in-package :fake-ontolingua)
(defvar *theories* (make-hash-table))
(defclass theory ()
((name :initarg :name
:reader theory-name)
(supers :initform '()
:reader theory-supers)
(issues :initform '()
:initarg :issues
:reader theory-issues)
(documentation :initform nil
:initarg :documentation
:reader theory-documentation)
(relations :initform '()
:accessor the-theory-relations)))
(defun find-theory (name &optional (errorp t))
(let ((it (gethash name *theories*)))
(or it
(if errorp
(error "no theory ~S" name)
nil))))
(defun (setf find-theory) (theory name)
(setf (gethash name *theories*) theory))
(defgeneric ensure-theory (theory-or-name)
(:method ((name symbol))
(find-theory name))
(:method ((theory theory))
theory)
(:method (mutant)
(error "what?")))
(defmethod initialize-instance :after ((theory theory)
&key (supers '(frame-ontology)))
(setf (slot-value theory 'supers)
(mapcar #'ensure-theory supers)))
(defvar *current-theory* nil)
(defun in-theory (theory-name)
(setf *current-theory* (find-theory theory-name)))
(defun current-theory ()
*current-theory*)
(defun clear-theories ()
;; Reset everything
(clrhash *theories*)
(setf (gethash 'frame-ontology *theories*)
(make-instance 'theory :name 'frame-ontology :supers '()))
(in-theory 'frame-ontology)
(values))
;;; Bootstrap theories
(clear-theories)
(defun make-theory (name kws)
(when (find-theory name nil)
(restart-case
(error "redefinition not supported")
(continue ()
:report "clear all existing theories"
(clear-theories)
(make-theory name kws))))
(setf (find-theory name) (apply #'make-instance 'theory :name name kws)))
(defmacro define-theory (name supers &body doc/body)
(let ((effective-body (if (stringp (first doc/body))
(cons ':documentation doc/body)
doc/body)))
`(progn
(make-theory ',name (list* ':supers ',supers ',effective-body))
',name)))
(defun clear-relations (&optional (theory *current-theory*))
(setf (the-theory-relations theory) nil)
(values))
(defgeneric theory-relations (theory)
;; Get them in the right order. (Should use collecting to make
;; relation list...)
(:method ((theory theory))
(reverse (the-theory-relations theory))))
(defun current-relations (&optional (theory (current-theory)))
(theory-relations theory))
(defun find-relation (name &optional (theory (current-theory))
(errorp t))
(let ((it (assoc name (the-theory-relations theory))))
(cond
(it (cdr it))
(errorp (error "no relation ~A" name))
(t nil))))
(defclass relation ()
((name :initarg :name
:reader relation-name)
(theory :initarg :theory
:reader relation-theory)
(arglist :initarg :arglist
:reader relation-arglist)
(documentation :initarg :documentation
:initform nil
:reader relation-documentation)
(body :initarg :body
:reader relation-body)))
(defun make-relation (name kws)
(let* ((theory *current-theory*)
(relations (the-theory-relations theory))
(existing (assoc name relations))
(relation (apply #'make-instance 'relation
:name name
:theory theory
kws)))
(cond
(existing
(warn "redefining ~A in ~A" name (theory-name theory))
(setf (cdr existing) relation))
(t
(setf (the-theory-relations theory)
(acons name relation (the-theory-relations theory)))))
relation))
(defmacro define-relation (name arglist &body doc/body)
(let ((effective-body
(if (stringp (first doc/body))
(list ':documentation (first doc/body)
':body (rest doc/body))
(list ':body doc/body))))
`(progn
(make-relation ',name (list* ':arglist ',arglist ',name)))
加载并编译完成后,就可以加载示例了:
> (load (compile-file "fake-ontolingua"))
[noise from compiler removed]
> (in-package :ontolingua-user)
#<The FAKE-ONTOLINGUA-USER package,0/16 internal,0/16 external>
> (load "engmath-sample.lisp")
; Loading text file /private/tmp/engmath-sample.lisp
#P"/private/tmp/engmath-sample.lisp"
> (find-relation 'linear-space)
#<fake-ontolingua::relation 40200B6323>
ONTOLINGUA-USER 6 > (relation-body (find-relation 'linear-space))
(:iff-def
[lots of stuff])
请注意,此代码足以读取示例文件并从中构建结构。该结构不是本体,特别是对于什么是关系、关系体是什么或任何类似的东西,事物一无所知。要真正做任何有用的事情,您需要充分了解 KIF / Ontolingua 以了解事物的含义,然后然后编写一个程序,该程序可以遍历所有关系并吐出合适的 OWL。