narust_158/language/features/image.rs
1//! 📄OpenNARS `nars.language.ImageXXt`
2//! * 🎯复刻OpenNARS中有关「外延像/内涵像」的通用函数
3//! * 📌NAL底层的「像」逻辑,对应`ImageExt`与`ImageInt`
4//! * ⚠️不包括与记忆区有关的`make`系列方法
5//!
6//! # 🆕差异点
7//! ! ⚠️因原先语法上实现的差异,此处对「像占位符位置」与「关系词项位置」的表述,和OpenNARS不一样
8//! * 📝NAL-4中,「像」作为一种「关系与参数中挖了空的词项」,
9//! * 将**第一位**固定为「关系词项」的位置
10//! * 📌范围:1~总词项数
11//! * ⚠️第一位为「关系词项」预留——若有占位符,则与「乘积」无异
12//! * 📌核心差异:自Narsese.rs以来,NARust更强调「占位符的词法位置」而非「关系词项之后的位置」
13//!
14//! ## 例
15//!
16//! 对`(*,A,B) --> P`
17//! * 在第1位
18//! * 📄OpenNARS: ⇔ `A --> (/,P,_,B)` ⇒ `(/,P,B)_0`
19//! * 📄NARust: ⇔ `A --> (/,P,_,B)` ⇒ `(/,P,B)_1`
20//! * 📌`1`而非`0`的依据:占位符在「像」中的位置为`1`
21//! * 在第2位
22//! * 📄OpenNARS: ⇔ `B --> (/,P,A,_)` ⇒ `(/,A,P)_1`
23//! * 📄NARust: ⇔ `B --> (/,P,A,_)` ⇒ `(/,P,A)_2`
24//! * 📌`2`而非`1`的依据:占位符在「像」中的位置为`2`
25//! * 在第0位(扩展)
26//! * 📄OpenNARS: 【不支持】(会自动转换到「第一位」去)
27//! * 📄NARust: ⇔ `P --> (/,_,A,B)` ⇒ `(/,A,B)_0`
28//! * 📌`0`的依据:占位符在「像」中的位置为`0`
29//! * ❓PyNARS却又支持`(/,_,A)`,但又把`<P --> (/,_,A,B)>.`推导成`<(*, A, B)-->_>.`
30//!
31//! # 方法列表
32//! 🕒最后更新:【2024-04-24 20:15:43】
33//!
34//! * `ImageExt` / `ImageInt`
35//! * `getRelationIndex`
36//! * `getRelation`
37//! * `getTheOtherComponent`
38//!
39//! # 📄OpenNARS
40//!
41//! ## 外延像
42//! An extension image.
43//!
44//! `B --> (/,P,A,_)` iff `(*,A,B) --> P`
45//!
46//! Internally, it is actually `(/,A,P)_1`, with an index.
47//!
48//! ## 内涵像
49//! An intension image.
50//!
51//! `(\,P,A,_) --> B` iff `P --> (*,A,B)`
52//!
53//! Internally, it is actually `(\,A,P)_1`, with an index.
54
55use crate::language::*;
56use nar_dev_utils::matches_or;
57
58impl<'a> CompoundTermRef<'a> {
59 // * ✅现在「判别函数」统一迁移至[`super::compound`]
60
61 /// 📄OpenNARS `getRelationIndex` 属性
62 /// * 🎯用于获取「像」的关系索引
63 /// * 🆕⚠️现在是获取「占位符位置」
64 /// * 📝原先OpenNARS是将「关系词项」放在占位符处的,现在是根据《NAL》原意,将「关系词项」统一放在「第一个词项」处
65 /// * 📌所以后续所有的「索引」都变成了「占位符位置」
66 /// * 💭【2024-05-11 14:40:15】后续可能会在这点上有隐患——随后要注意这种差别
67 ///
68 /// # Panics
69 ///
70 /// ! ⚠️仅限于「像」的`TermComponents::MultiIndexed`词项
71 /// * 若尝试获取「非『像』词项」的关系索引,则会panic
72 /// * 🚩【2024-06-12 22:53:09】本来就不应该对「非像词项」调用该函数——严格跟「像」类型绑定
73 ///
74 /// # 📄OpenNARS
75 ///
76 /// get the index of the relation in the component list
77 ///
78 /// @return the index of relation
79 #[doc(alias = "get_relation_index")]
80 pub fn get_placeholder_index(self) -> usize {
81 self.components
82 .iter()
83 .position(Term::is_placeholder)
84 .expect("尝试获取「非『像』词项」的关系索引")
85 }
86
87 /// 📄OpenNARS `getRelation` 属性
88 /// * 🎯用于获取「像」的「关系词项」
89 /// * ⚠️若尝试获取「非『像』词项」的关系词项,则会panic
90 /// * 🆕按NARust「索引=占位符索引」的来:总是在索引`0`处
91 ///
92 /// # 📄OpenNARS
93 ///
94 /// Get the relation term in the Image
95 ///
96 /// @return The term representing a relation
97 pub fn get_relation(self) -> &'a Term {
98 &self.components[0]
99 }
100
101 /// 📄OpenNARS `getTheOtherComponent` 属性
102 /// * 🎯用于获取「像」的「另一词项」
103 /// * ⚠️若尝试获取「非『像』词项」的词项,则会panic
104 ///
105 /// # 📄OpenNARS
106 ///
107 /// Get the other term in the Image
108 ///
109 /// @return The term related
110 pub fn get_the_other_component(self) -> Option<&'a Term> {
111 /* 📄OpenNARS源码:
112 if (components.size() != 2) {
113 return null;
114 }
115 return (relationIndex == 0) ? components.get(1) : components.get(0); */
116 matches_or! {
117 ?self.components,
118 // ! 🚩【2024-06-13 23:52:06】现在「占位符」算作一个词项了
119 // * 📄[R, _, A]
120 [_, term1, term2] => match term1.is_placeholder() {
121 true => term2,
122 false => term1,
123 }
124 }
125 }
126}
127
128/// 单元测试
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use crate::symbols::*;
133 use crate::test_compound as compound;
134 use crate::test_term as term;
135 use crate::{ok, util::AResult};
136 use nar_dev_utils::asserts;
137
138 #[test]
139 fn instanceof_image() -> AResult {
140 /// 📌工具方法:直接调用`make_image_xxt_vec`构造词项
141 fn make_image(
142 argument: impl IntoIterator<Item = &'static str>,
143 make_vec: impl Fn(Vec<Term>) -> Option<Term>,
144 ) -> AResult<Term> {
145 let argument = argument
146 .into_iter()
147 .map(|t| t.parse::<Term>().expect("内部词项解析失败"))
148 .collect::<Vec<_>>();
149 make_vec(argument).ok_or(anyhow::anyhow!("词项解析失败"))
150 }
151 fn make_ext(argument: impl IntoIterator<Item = &'static str>) -> AResult<Term> {
152 make_image(argument, Term::make_image_ext_vec)
153 }
154 fn make_int(argument: impl IntoIterator<Item = &'static str>) -> AResult<Term> {
155 make_image(argument, Term::make_image_int_vec)
156 }
157 asserts! {
158 // 像占位符在第一位的「像」会被解析为「乘积」
159 term!(r"(/, _, A, B)").identifier() => PRODUCT_OPERATOR,
160 term!(r"(\, _, A, B)").identifier() => PRODUCT_OPERATOR,
161 // 其余正常情况
162 make_ext(["S", "_", "A", "B"])?.instanceof_image()
163 make_int(["S", "_", "A", "B"])?.instanceof_image()
164 make_ext(["S", "A", "_", "B"])?.instanceof_image()
165 make_int(["S", "A", "_", "B"])?.instanceof_image()
166 make_ext(["S", "A", "B", "_"])?.instanceof_image()
167 make_int(["S", "A", "B", "_"])?.instanceof_image()
168 term!(r"(/, A, _, B)").instanceof_image()
169 term!(r"(\, A, _, B)").instanceof_image()
170 term!(r"(/, A, B, _)").instanceof_image()
171 term!(r"(\, A, B, _)").instanceof_image()
172 }
173 ok!()
174 }
175
176 #[test]
177 fn get_relation_index() -> AResult {
178 asserts! {
179 // compound!(r"(/, _, A, B)").get_relation_index() => 0 // 会被解析为「乘积」
180 // compound!(r"(\, _, A, B)").get_relation_index() => 0 // 会被解析为「乘积」
181 compound!(r"(/, A, _, B)").get_placeholder_index() => 1
182 compound!(r"(\, A, _, B)").get_placeholder_index() => 1
183 compound!(r"(/, A, B, _)").get_placeholder_index() => 2
184 compound!(r"(\, A, B, _)").get_placeholder_index() => 2
185 }
186 ok!()
187 }
188
189 #[test]
190 fn get_relation() -> AResult {
191 asserts! {
192 compound!(r"(/, R, _, B)").get_relation() => &term!("R")
193 compound!(r"(\, R, _, B)").get_relation() => &term!("R")
194 compound!(r"(/, R, A, _)").get_relation() => &term!("R")
195 compound!(r"(\, R, A, _)").get_relation() => &term!("R")
196 }
197 ok!()
198 }
199
200 #[test]
201 fn get_the_other_component() -> AResult {
202 asserts! {
203 compound!(r"(/, R, _, B)").get_the_other_component() => Some(&term!("B"))
204 compound!(r"(\, R, _, B)").get_the_other_component() => Some(&term!("B"))
205 compound!(r"(/, R, A, _)").get_the_other_component() => Some(&term!("A"))
206 compound!(r"(\, R, A, _)").get_the_other_component() => Some(&term!("A"))
207 }
208 ok!()
209 }
210}