Logical Programming
Lisa现在支持逻辑式编程了,具体是通过一个内置的可插拔DSL来实现的。也就是说,Lisa的逻辑式编程模块是可插拔的,可以和宿主语言交互。
原子
为了实现逻辑式编程语言中的原子
的概念,本次更新中,Lisa引入了原子。原子以冒号:
开头,紧接着一个合法的标识符,如果原子中有空白字符,可以接一个字符串,例如: :a
, :"atom with spaces"
, 或者 :"b"
。
:a
和 :"a"
被视为相同的原子。
Logical Module
若想要使用逻辑模块,你需要先引入这个模块,使用 (import-env! logical)
来引入该模块。
正如定义变量需要环境模型一样,逻辑的事实会被定义在LogicalContext
里面,因此,你可以处理多个不同的上下文。而且上下文可以被储存在Lisa变量中,一旦该上下文被储存,该上下文就是不可变的,因此,可以保证引用透明性。
你可以用(current-context)
来获得当前上下文, (pop-context!)
来移除当前上下文, (logical/push-context context)
来设定context
为当前上下文。
你可以使用 (logical/new-context)
来创建一个上下文并将其设为当前的上下文。
当一个上下文被设定为当前上下文的时候,你就可以使用上下文中定义的宏,例如fact
, define-rule
以及 query
。
当你想要了解一个语言的时候,你需要着重了解这个语言的基本要素,抽象方法以及组合方法1。从这个方面上讲,Lisa的logical module作为一个DSL,其基本要素都和Lisa有一些差别。
Facts
除了Lisa支持的基本要素外,事实
是Lisa/Logical的基本表达式。
事实可以使用 fact
宏来定义。比如:
(fact (path a b))
(fact (path b c))
(fact (path d e))
(fact (path e f))
prolog
有所了解,你就会发现prolog区分变量和原子的方法是大小写和下划线。这点和Erlang
相似,然而我个人不是十分喜欢这一点,因此Lisa是和Elixir
类似的,用原子类型来代表原子,用符号(Symbols)来代替变量。事实上,你可以以任何非符号基本变量,比如字符串和数字来代表事实。
注意到当我们定义事实当时候,我们往往不怎么用到变量,因此在fact
宏中,符号会被自动转换为同名的原子。如果你硬是要用符号,你可以使用'sym
,即一个符号的引用(quoted symbol)来指代。以上规则只在定义事实的时候有效。
下划线 _
匹配任何表达式,并且忽略该值。
(fact (path a b)) ; <=>
(fact (path :a :b))
Rules
规则是逻辑式编程的用来抽象的方法。
规则可以用 define-rule
宏来定义。其语法和定义一个函数很像。
(define-rule (link a c)
(or (path a c)
(and (path a b)
(link b c))))
规则中的符号被视为变量,原子被视为原子。
规则的规则体是一个查询
。特别地,当一个规则的规则体是空的时候,该规则默认匹配到了就永远为真。
Queries
查询是逻辑式编程的组合方法。
一个查询可以是事实和规则的组合,组合方式是使用一些特殊的组合子。
使用query
宏来执行一段查询。
(query (link from to)) ; => ({'from :a 'to :b} {'from :b 'to :c} {'from :d 'to :e} {'from :e 'to :f} {'from :a 'to :c} {'from :d 'to :f})
(query (link :a to)) ; => ({'to :b} {'to :c})
查询的结果是一个记录的列表,如果该查询匹配不到任何规则,则生成一个空列表。
如果你仅仅想要知道一个事实只不是存在,你可以使用is-true?
宏。
注意,他们都是宏,意味着后面的表达式将不进行任何转换直接被转入。
Combinator
一些特殊的组合子提供了强大的抽象能力。
and
接受任意查询,如果所有的查询都为真,则结果查询为真。or
接受任意查询,如果有其中一个查询为真,则结果查询为真。but
接受一个查询,如果该查询为真,则结果查询为假。?
接受一个Lisa表达式,该表达式必须返回真或者假,如果为真,则总查询为真。execute-lisa
接受一个Lisa表达式,执行该表达式,结果查询是一个永真查询。=
可以对传入的两个表达式做归一化。<-
接受一个变量和一个表达式,对表达式求值之后对变量和表达式做归一化。
(fact (salary a 114))
(fact (salary b 514))
(query (and (salary x y) (? (> y 200)))) ; => ({'x :b 'y 514})
(query (and (salary x y) (execute-lisa (println! y)))) ; Prints 114 514 and the result should be ({'x :a 'y 114} {'x :b 'y 514})
(query (= (x x x) ((1 b c) (a 2 c) (a b 3)))); => ({'a 1 'b 2 'c 3 'x (1 2 3)})
(define-rule (minus-one x y)
(<- y (- x 1)))
(is-true? (minus-one 3 2)) ; => true
逻辑模块提供了一个子语言,能和Lisa交互。但是目前看啦逻辑式编程仍然无法找到合适的用处,甚至教科书上能见到函数式编程,却很难见到逻辑式编程的身影。
本篇文章只是一个介绍,具体实现可以看下一篇文章。 r{{ changeExtraDisplaySettings({blurMainImage: true}) }}
-
Structure and Interpretation of Computer Programs Chapter 1 The Elements of Programming. ↩