narust_158/language/base/structs.rs
1//! 「词项」的结构体
2//! * 🚩【2024-06-12 21:11:15】新迁入作为「定义」mod
3
4/// 作为「结构」的词项
5/// * 🚩更多通过「复合」而非「抽象特征-具体实现」复用代码
6/// * 📍【2024-04-20 21:13:20】目前只需实现OpenNARS 1.5.8的东西
7///
8/// ! ⚠️【2024-04-20 21:47:08】暂不实现「变量 < 原子 < 复合」的逻辑
9/// * 🎯OpenNARS中有关「词项顺序」的概念,目的是保证「无序不重复集合」的唯一性
10/// * 🚩然而此实现的需求用「派生[`Ord`]」虽然造成逻辑不同,但可以满足需求
11/// * 📌核心逻辑:实现需求就行,没必要(也很难)全盘照搬
12/// * ⚠️[`Hash`]特征不能在手动实现的[`PartialEq`]中实现,否则会破坏「散列一致性」
13///
14/// * 📝OpenNARS在「记忆区构造词项」时,就会进行各种预处理
15/// * 📄`<(-, {A, B}, {A}) --> x>` 会产生 `<{B} --> x>`(外延「差」规则)
16/// * 📝OpenNARS中的词项基本只能通过`make`系列方法(从外部)构造
17/// * 💭这似乎意味着它是一种「记忆区专用」的封闭数据类型
18///
19/// * 📌【2024-06-16 11:42:25】目前应手动实现[`Ord`]
20/// * ⚠️在「重排唯一化」的需求场景下需要「变量之间均相等」
21/// * 🚩目前要对「重排唯一化」做单独实现:[`Ord`]需要与[`PartialEq`]对齐
22/// * 💭大多情况下不会用到「比大小」逻辑,场景如「排序」
23/// * ⚠️不能破坏「比对为等」和「直接判等」的一致性:原先不比对`is_constant`字段,就已经破坏了这点
24///
25/// # 📄OpenNARS
26///
27/// Term is the basic component of Narsese, and the object of processing in NARS.
28/// A Term may have an associated Concept containing relations with other Terms.
29/// It is not linked in the Term, because a Concept may be forgot while the Term exists. Multiple objects may represent the same Term.
30///
31/// ## 作为特征的「实现」
32///
33/// ### Cloneable => [`Clone`]
34///
35/// Make a new Term with the same name.
36///
37/// ### equals => [`Eq`]
38///
39/// Equal terms have identical name, though not necessarily the same reference.
40///
41/// ### hashCode => [`Hash`]
42///
43/// Produce a hash code for the term
44///
45/// ### compareTo => [`Ord`]
46///
47/// Orders among terms: variable < atomic < compound
48///
49/// ### toString => [`Display`]
50///
51/// The same as getName by default, used in display only.
52///
53/// @return The name of the term as a String
54#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
55pub struct Term {
56 /// 标识符
57 /// * 🎯决定词项的「类型」
58 /// * 🚩使用不同词项类型独有的「标识符」
59 /// * 📄原子词项⇒原子词项前缀
60 /// * 📄复合词项⇒复合词项连接词
61 /// * 📄陈述⇒陈述系词
62 /// * ❌【2024-04-21 00:57:39】不能使用「静态字串」固定
63 /// * ⚠️需要针对「用户输入」作一定妥协
64 /// * 此刻通过「词法折叠」等途径获得的「词项」就不一定是「静态引用」了
65 /// * 📌即便标识符的类型尽可能「固定」(就那么几种)
66 pub(super) identifier: String,
67 /// 组分
68 /// * 🎯表示「词项名称」「词项包含词项」的功能
69 /// * 🚩通过单一的「复合组分」实现「组合」功能
70 pub(super) components: TermComponents,
71 // 自由属性「是否为常量」
72 // * 🎯用于决定其在记忆区、NAL-6推理中的行为
73 // * ❓为何要设置成「结构属性」:会在系统构造「语句」时改变
74 // * 📝源自OpenNARS:构造语句时所直接涉及的词项均为「常量词项」,必须进入记忆区
75 // * 📄OpenNARS `isConstant` 属性
76 // * 📜默认为`true`
77 // * 📌此属性影响到「语义判等」的行为
78 // * ✅【2024-06-19 02:07:04】现已无用:在OpenNARS改版中验证了「运行时动态判断,不影响单步推理结果」
79 // pub(in crate::language) is_constant: bool,
80}
81
82/// 复合词项组分
83/// * ⚠️
84#[derive(Debug, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
85pub enum TermComponents {
86 /// 不包含任何组分
87 /// * 📄占位符
88 Empty,
89
90 /// 仅包含一个字符串作为「名称」
91 /// * 📄词语
92 Word(String),
93
94 /// 仅包含一个非负整数作为「变量」
95 /// * 📄变量
96 ///
97 /// ## 有关「变量命名」的笔记
98 ///
99 /// * 📝在NAL中,变量词项的语义仅在单个词项之内有效
100 /// * 📄这两个词项是**语义等价**的:`<A --> $x>` 和 `<B --> $y>`
101 /// * 📄这两个词项同样是**语义等价**的:`(&&,<#1 --> lock>,<#2 --> key>)` 和 `(&&,<#1 --> key>,<#2 --> lock>)`
102 /// * ✨具体原理:单个词项之内,变量名的改变不会影响其逻辑语义,只要名字不发生冲突(重命名后不和任一变量名相同)
103 /// * 📌这意味着:若需确定「带变量词项」的唯一性,就需要一套「自动重命名」机制来保证变量「语义相等」
104 /// * 📄此即:重命名后,可以直接通过「数据判等」实现「语义判等」逻辑
105 Variable(usize),
106
107 /// 一般复合词项
108 /// * 📌一元、二元、多元词项
109 /// * 🚩【2024-06-12 21:18:23】现在统一「一元复合词项」「二元复合词项」和「多元复合词项」
110 /// * 📌统一使用「构造后定长数组」实现,无需复杂match匹配(少两个分支)
111 /// * 📌统一「可交换」与「不可交换」两类词项(无需考虑复合词项数目)
112 /// * 📌使用堆分配的「构造后定长数组」规避「循环包含」问题
113 ///
114 /// 词项类型举例:
115 /// * 📄乘积
116 /// * 📄否定
117 /// * 📄继承、蕴含
118 /// * 🚩通过「构造时自动去重并排序」实现「集合无序性」
119 /// * 📄外延差、内涵差
120 /// * 📄外延集、内涵集
121 /// * 📄外延交、内涵交
122 /// * 📄合取、析取
123 /// * 📄相似、等价
124 ///
125 /// ## 【2024-06-12 22:12:25】有关「像」的结构实现 笔记
126 /// * 📝实际上,「有索引」和「使用占位符」是两个相同的方案
127 /// * 🎯这两个方案的核心目标皆为「在『外延像/内涵像』中取到『像占位符』的位置」
128 /// * 📄差别仅在手段不同
129 /// * 📌「有索引」的方法:多加一个字段,特别标识「像占位符位置」以便快速索引
130 /// * 📌「使用占位符」的方法:允许「占位符」独立作为词项,在「获取像占位符位置」时依靠「索引像占位符」获取位置
131 /// * 💭两个方案各有优劣
132 /// * ⚠️「有索引」的方法:以设计换性能——需要多添加字段,在不应「另创新类」的情况下徒增许多模式匹配需要
133 /// * ⚠️「使用占位符」的方法:以性能换设计——在每次「检索像占位符位置」均需要`O(内容词项数)`的时间复杂度
134 /// * 🚩【2024-06-12 22:11:47】综合多方考量,最终确定(并唯一选择)第二种「使用占位符」的方案
135 Compound(Box<[Term]>),
136 // /// 多重组分(有序)+索引
137 // /// * ❓【2024-04-20 21:57:35】日后需要通过「像」使用时,会造成「像-MultiIndexed」绑定
138 // /// * ⚡那时候若使用「断言」是否会导致不稳定
139 // /// * ❓若不使用「断言」而是音量失败,是否会增加排查难度
140 // /// * ❓若不使用「断言」而是发出警告,那是否会导致性能问题
141 // /// * 🚩可行的解决方案:`match (self.identifier, self.components) { ('/', MultiIndexed(i, v))}`
142 // ///
143 // /// 词项类型举例:
144 // /// * 📄外延像、内涵像
145 // MultiIndexed(usize, Box<[Term]>),
146}
147
148/// 单元测试
149/// * 🎯对结构体本身进行测试(仅结构字段、枚举变种)
150/// * 🎯提供通用的测试用函数
151#[cfg(test)]
152pub(crate) mod test_term {
153 use super::*;
154 use nar_dev_utils::ResultBoost;
155
156 /// 用于批量生成「解析后的词项」
157 /// * 🚩使用`?`直接在解析处上抛错误
158 #[macro_export]
159 macro_rules! test_term {
160 // 词项数组
161 ([$($s:expr $(,)?)*]) => {
162 [ $( term!($s) ),* ]
163 };
164 // 词项引用数组(一次性)
165 ([$($s:expr $(,)?)*] &) => {
166 [ $( &term!($s) ),* ]
167 };
168 // 单个词项(字符串),问号上抛
169 ($s:literal) => {
170 $s.parse::<Term>()?
171 };
172 // 单个词项(字符串),问号上抛
173 (str $s:expr) => {
174 $s.parse::<Term>()?
175 };
176 // 单个词项,但unwrap
177 (unwrap $s:expr) => {
178 $s.parse::<Term>().unwrap()
179 };
180 // 单个词项,问号上抛
181 ($($t:tt)*) => {
182 $crate::test_term!(str stringify!($($t)*))
183 };
184 }
185
186 /// 快捷构造[`Option<Term>`](Option)
187 #[macro_export]
188 macro_rules! option_term {
189 () => {
190 None
191 };
192 (None) => {
193 None
194 };
195 ($t:literal) => {
196 parse_option_term($t)
197 };
198 }
199
200 /// 用于封装作为`result`的方法
201 /// * 🚩在解析失败时,打印错误信息并返回`None`
202 /// * 📌一般这时会有「预期比对」能触发失败
203 pub fn parse_option_term(t: &str) -> Option<Term> {
204 t.parse::<Term>()
205 .ok_or_run(|e| eprintln!("!!! 词项 {t:?} 解析失败:{e}"))
206 }
207
208 /// 快捷格式化[`Option<Term>`](Option)
209 pub fn format_option_term(ot: &Option<Term>) -> String {
210 match ot {
211 Some(t) => format!("Some(\"{t}\")"),
212 None => "None".to_string(),
213 }
214 }
215}