narust_158/language/features/variable.rs
1//! 📄OpenNARS `nars.language.Variable`
2//! * 📌与NAL-6有关的「变量」逻辑
3//! * 📄`isConstant`
4//! * 🚩【2024-06-14 17:31:44】只包含最基本的「变量存在性判定」「是否为常量」等基本逻辑
5//! * ⚠️涉及「变量统一」「变量重命名」等逻辑,放置在专用的「变量推理」代码中
6//!
7//! # 方法列表
8//! 🕒最后更新:【2024-06-19 02:05:25】
9//!
10//! * `isConstant`
11//! * `getType` => `getVariableType`
12//! * `containVarI`
13//! * `containVarD`
14//! * `containVarQ`
15//! * `containVar`
16//!
17//! # 📄OpenNARS
18//!
19//! A variable term, which does not correspond to a concept
20
21use crate::language::*;
22use crate::symbols::*;
23use nar_dev_utils::matches_or;
24
25impl Term {
26 /// 用于判断是否为「变量词项」
27 /// * 📄OpenNARS `instanceof Variable` 逻辑
28 /// * 🎯判断「[是否内含变量](Self::contain_var)」
29 pub fn instanceof_variable(&self) -> bool {
30 matches!(
31 self.identifier(),
32 VAR_INDEPENDENT | VAR_DEPENDENT | VAR_QUERY
33 )
34 }
35
36 /// 🆕用于判断「是否为独立变量」
37 pub fn instanceof_variable_i(&self) -> bool {
38 self.identifier() == VAR_INDEPENDENT
39 }
40
41 /// 🆕用于判断「是否为非独变量」
42 pub fn instanceof_variable_d(&self) -> bool {
43 self.identifier() == VAR_DEPENDENT
44 }
45
46 /// 🆕用于判断「是否为查询变量」
47 pub fn instanceof_variable_q(&self) -> bool {
48 self.identifier() == VAR_QUERY
49 }
50
51 /// 尝试匹配出「变量」,并返回其中的编号(若有)
52 pub fn as_variable(&self) -> Option<usize> {
53 matches_or!(
54 ?self.components(),
55 TermComponents::Variable(n) => *n
56 )
57 }
58
59 /// 📄OpenNARS `Term.isConstant` 属性
60 /// * 🚩检查其是否为「常量」:自身是否「不含变量」
61 /// * 🎯决定其是否能**成为**一个「概念」(被作为「概念」存入记忆区)
62 /// * ❓OpenNARS中在「构造语句」时又会将`isConstant`属性置为`true`,这是为何
63 /// * 📝被`Sentence(..)`调用的`CompoundTerm.renameVariables()`会直接将词项「视作常量」
64 /// * 💭这似乎是被认为「即便全是变量,只要是【被作为语句输入过】的,就会被认作是『常量』」
65 /// * 📝然后这个「是否常量」会在「记忆区」中被认作「是否能从中获取概念」的依据:`if (!term.isConstant()) { return null; }`
66 /// * 🚩【2024-04-21 23:46:12】现在变为「只读属性」:接受OpenNARS中有关「设置语句时/替换变量后 变为『常量』」的设定
67 /// * 💫【2024-04-22 00:03:10】后续仍然有一堆复杂逻辑要考虑
68 ///
69 /// * ✅【2024-06-19 02:06:12】跟随最新改版更新,删去字段并铺开实现此功能
70 /// * ♻️【2024-06-26 02:07:27】重构修正:禁止「占位符」作为「常量词项」
71 /// * ♻️【2024-07-31 21:41:49】修正:不再将查询变量计入「常量词项」
72 ///
73 /// # 📄OpenNARS
74 ///
75 /// Check whether the current Term can name a Concept.
76 ///
77 /// - A Term is constant by default
78 /// - A variable is not constant
79 /// - (for `CompoundTerm`) check if the term contains free variable
80 #[inline(always)]
81 pub fn is_constant(&self) -> bool {
82 !self.instanceof_variable() && !self.is_placeholder() && !self.contains_sole_variable()
83 }
84
85 /// 🆕检查自身是否包含有「孤立非查询变量」
86 /// * 📄复刻自OpenNARS改版逻辑
87 fn contains_sole_variable(&self) -> bool {
88 use std::collections::HashMap;
89
90 /// * 🚩计算「非查询变量数目集」
91 fn variable_count_map(this: &Term) -> HashMap<usize, usize> {
92 let mut var_count_map = HashMap::new();
93 this.for_each_atom(&mut |atom| {
94 if let Some(n) = atom.as_variable() {
95 // * 🚩非查询变量
96 if !atom.instanceof_variable_q() {
97 let new_value = match var_count_map.get(&n) {
98 Some(count) => count + 1,
99 None => 1,
100 };
101 var_count_map.insert(n, new_value);
102 }
103 }
104 });
105 var_count_map
106 }
107
108 // * 🚩计算并过滤
109 let var_count_map = variable_count_map(self);
110 var_count_map.values().any(|&count| count < 2)
111 }
112
113 /// 📄OpenNARS `Variable.containVar` 方法
114 /// * 🚩检查其是否「包含变量」
115 /// * 自身为「变量词项」或者其包含「变量词项」
116 /// * 🎯用于决定复合词项是否为「常量」
117 /// * 📝OpenNARS中对于复合词项的`isConstant`属性采用「惰性获取」的机制
118 /// * `isConstant`作为`!Variable.containVar(name)`进行初始化
119 /// * 🆕实现方法:不同于OpenNARS「直接从字符串中搜索子串」的方式,基于递归方法设计
120 ///
121 /// # 📄OpenNARS
122 ///
123 /// Check whether a string represent a name of a term that contains a variable
124 #[inline]
125 pub fn contain_var(&self) -> bool {
126 self.instanceof_variable() || self.components().contain_var()
127 }
128
129 /// 📄OpenNARS `Variable.containVarI` 方法
130 /// * 🎯判断「是否包含指定类型的变量」
131 /// * 🚩通过「判断是否包含指定标识符的词项」完成判断
132 pub fn contain_var_i(&self) -> bool {
133 self.contain_type(VAR_INDEPENDENT)
134 }
135
136 /// 📄OpenNARS `Variable.containVarD` 方法
137 /// * 🎯判断「是否包含指定类型的变量」
138 /// * 🚩通过「判断是否包含指定标识符的词项」完成判断
139 pub fn contain_var_d(&self) -> bool {
140 self.contain_type(VAR_DEPENDENT)
141 }
142
143 /// 📄OpenNARS `Variable.containVarQ` 方法
144 /// * 🎯判断「是否包含指定类型的变量」
145 /// * 🚩通过「判断是否包含指定标识符的词项」完成判断
146 pub fn contain_var_q(&self) -> bool {
147 self.contain_type(VAR_QUERY)
148 }
149
150 /// 📄OpenNARS `Variable.getType` 方法
151 /// * 🎯在OpenNARS中仅用于「判断变量类型相等」
152 /// * 🚩归并到「判断词项标识符相等」
153 ///
154 /// # 📄OpenNARS
155 ///
156 /// Get the type of the variable
157 #[inline(always)]
158 pub fn get_variable_type(&self) -> &str {
159 self.identifier()
160 }
161
162 /// 🆕获取多个词项中编号最大的变量词项id
163 pub fn maximum_variable_id_multi<'s>(terms: impl IntoIterator<Item = &'s Term>) -> usize {
164 terms
165 .into_iter()
166 .map(Term::maximum_variable_id) // 统计各个词项的最大变量id
167 .max() // 取最大值
168 .unwrap_or(0) // 以0为补充(即便空集)
169 }
170}
171
172/// 🆕获取编号最大的变量词项id
173/// * 🎯兼容「词项」与「词项数组」
174pub trait MaximumVariableId {
175 fn maximum_variable_id(&self) -> usize;
176}
177
178/// 词项本身
179impl MaximumVariableId for Term {
180 /// 🆕获取一个词项中编号最大的变量词项id
181 fn maximum_variable_id(&self) -> usize {
182 use TermComponents::*;
183 match self.components() {
184 // 变量⇒自身id
185 Variable(id) => *id,
186 // 内含词项⇒递归深入
187 Compound(terms) => Term::maximum_variable_id_multi(terms.iter()),
188 // 其它⇒0 | 后续开放补充
189 Empty | Word(..) => 0,
190 }
191 }
192}
193
194/// 词项引用
195impl MaximumVariableId for &Term {
196 fn maximum_variable_id(&self) -> usize {
197 Term::maximum_variable_id(*self)
198 }
199}
200
201/// 兼容词项数组
202impl<const N: usize> MaximumVariableId for [Term; N] {
203 fn maximum_variable_id(&self) -> usize {
204 Term::maximum_variable_id_multi(self)
205 }
206}
207
208/// 兼容引用数组
209impl<const N: usize> MaximumVariableId for [&Term; N] {
210 fn maximum_variable_id(&self) -> usize {
211 // * 🚩使用`cloned`将`&&Term`转换为`&Term`
212 Term::maximum_variable_id_multi(self.iter().cloned())
213 }
214}
215
216/// 兼容数组切片
217impl MaximumVariableId for [Term] {
218 fn maximum_variable_id(&self) -> usize {
219 Term::maximum_variable_id_multi(self)
220 }
221}
222
223/// 兼容引用数组切片
224impl MaximumVariableId for [&Term] {
225 fn maximum_variable_id(&self) -> usize {
226 // * 🚩使用`cloned`将`&&Term`转换为`&Term`
227 Term::maximum_variable_id_multi(self.iter().cloned())
228 }
229}
230
231impl TermComponents {
232 /// 判断「是否包含变量(词项)」
233 /// * 🎯支持「词项」中的方法,递归判断「是否含有变量」
234 /// * 🚩【2024-04-21 20:35:23】目前直接基于迭代器
235 /// * 📌牺牲一定性能,加快开发速度
236 pub fn contain_var(&self) -> bool {
237 self.iter().any(Term::contain_var)
238 }
239}
240
241/// 单元测试
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use crate::test_term as term;
246 use crate::{ok, util::AResult};
247 use nar_dev_utils::{asserts, macro_once};
248
249 /// 测试/包含变量
250 /// * ✨同时包含对「是否常量」的测试
251 #[test]
252 fn contain_var() -> AResult {
253 macro_once! {
254 macro test($($term:expr => $expected:expr)*) {
255 asserts! {$(
256 term!($term).contain_var() => $expected
257 )*}
258 }
259 "<A --> var_word>"=> false
260 "<A --> $var_word>"=> true
261 "<A --> #var_word>"=> true
262 "<A --> ?var_word>"=> true
263 }
264 ok!()
265 }
266
267 #[test]
268 fn is_constant() -> AResult {
269 macro_once! {
270 macro test($($term:expr => $expected:expr)*) {
271 asserts! {$(
272 term!($term).is_constant() => $expected
273 )*}
274 }
275 "<A --> var_word>" => true
276 "<A --> $var_word>" => false
277 "<A --> #var_word>" => false
278 // * 📌【2024-06-19 02:27:06】现在改版中成功的项:
279 // 查询变量
280 "<A --> ?var_word>" => true
281 "<?this --> ?that>" => true
282 // 封闭词项
283 "<<A --> $1> ==> <B --> $1>>" => true
284 "<<$2 --> $1> ==> <#1 --> #2>>" => true
285 "(*, $1, $1)" => true
286 }
287 ok!()
288 }
289}