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}