如何在EDN文件中编写Clara Rules并访问修改会话事实?
在Clojure中用外部EDN编写Clara Rules的完整结构及会话事实操作
一、外部EDN编写Clara Rules的完整结构
Clara Rules支持将规则定义在外部EDN文件中,每个规则是一个包含核心字段的Map,完整结构说明如下:
:name:规则唯一标识(必填):lhs:规则左部(条件),由一组模式组成(必填)
每个模式包含::type:匹配的事实类全限定名(必填):binding:可选,将匹配到的事实绑定到变量(如:binding :?user),供RHS引用:constraints:约束条件列表,支持Clojure表达式,可引用绑定变量
:rhs:规则右部(动作),是Clojure表达式列表(必填),可使用clara.rules/insert!、clara.rules/retract!等API:params:可选,规则参数集合,用于接收外部传入的值:salience:可选,规则优先级,数值越高执行顺序越靠前
示例完整EDN规则:
{ :name "update-user-criteria" :params #{:?default-id} :lhs [ {:type rules.rules.User, :binding :?user, :constraints [(= (:user-id ?user) "userfact1")]} {:type rules.rules.UserCriteria, :binding :?criteria} ] :rhs [ (let [new-id (:user-id ?user) updated-ids (conj (:ids ?criteria) new-id)] (clara.rules/retract! ?criteria) (clara.rules/insert! (rules.rules.UserCriteria. updated-ids))) ] }
二、在EDN规则中绑定并访问会话现有事实
你之前无法绑定事实的核心原因是未在LHS模式中指定:binding字段,正确的绑定方法:
- 在LHS的目标事实模式中添加
:binding :?变量名,将匹配到的事实绑定到变量 - 在RHS中直接通过该变量引用事实,读取其属性
修正后的rules.edn示例:
{ :name "criteria-1", :lhs [ {:type rules.rules.User, :constraints []} ; 补充你的实际约束条件 {:type rules.rules.UserCriteria, :binding :?criteria} ] :rhs [ (let [id (:user-id ?user) updated-criteria (conj (:ids ?criteria) id)] (clara.rules/retract! ?criteria) (clara.rules/insert! (rules.rules.UserCriteria. updated-criteria))) ] }
注意:EDN中的类名必须是运行时可加载的全限定名,比如你的User对应rules.rules.User,避免拼写错误(如你之前写的rules.rules.Users是错误的)。
三、修改会话中现有事实的正确方式
Clara Rules中的事实是不可变的,无法直接修改,标准流程是:
- 在LHS中匹配并绑定要修改的旧事实
- 在RHS中用
clara.rules/retract!移除旧事实 - 基于旧事实的属性创建新实例,用
clara.rules/insert!插入会话
四、从会话中获取事实的最优方法
你当前使用的(first (first (:fact->explanation session-data)))方式不够直观,推荐两种更简洁的方法:
方法1:使用Clara查询
定义查询(可写在EDN或代码中),通过clara.rules.api/query获取指定事实:
; 定义查询(可放入EDN规则文件) (def get-user-criteria '{:name "get-user-criteria" :params [] :lhs [{:type rules.rules.UserCriteria, :binding :?c}] :rhs [(clara.rules/return ?c)]}) ; 执行查询获取事实 (let [session (-> (mk-session (conj (fetch-rules-from-file) get-user-criteria)) (insert (->User "userfact1" "Alice") (->UserCriteria [100])) (fire-rules)) criteria (first (clara.rules.api/query session :get-user-criteria))] criteria)
方法2:使用clara.rules.api/get-facts函数
直接获取会话中指定类型的所有事实:
(let [session (-> (mk-session (fetch-rules-from-file)) (insert (->User "userfact1" "Alice") (->UserCriteria [100])) (fire-rules)) criteria-list (clara.rules.api/get-facts session rules.rules.UserCriteria)] (first criteria-list)) ; 获取第一个UserCriteria实例
补充:代码修正点
- 简化规则读取函数:如果
rules.edn是单个规则Map,用vector包装;如果是规则列表,直接读取即可:
; 单规则文件 (defn fetch-rules-from-file [] (vector (edn/read-string (slurp "rules.edn")))) ; 多规则列表文件 (defn fetch-rules-from-file [] (edn/read-string (slurp "rules.edn")))
- 确保
rules.edn中的:type字段与代码中定义的defrecord全限定名完全一致。
内容的提问来源于stack exchange,提问作者xyj
相关产品推荐
相关产品推荐

