如何递归且惰性简化类Lisp表达式?附Rust类型错误排查
问题解决:类型错误与惰性求值实现
一、类型错误修复
错误原因
add_sexp函数声明了泛型参数T,但这个T由调用该函数的外部代码决定,而你在函数内部调用add_internal时传入的是Vec<Index>::IntoIter具体类型,导致simplifier的参数类型(泛型T)和实际传入的迭代器类型不匹配。Rust的函数指针是具体类型绑定的,泛型T在这里被固化成调用者指定的类型,而非你期望的任意迭代器。
修复方案
调整函数签名,让simplifier接受任意实现Iterator<Item=Index>的类型,而非绑定到特定泛型参数。可通过trait object或调整泛型层级实现:
方案1:使用Trait Object(更灵活)
修改add_sexp和add_internal的simplifier参数类型,将函数指针改为Box<dyn FnMut>包裹的 trait object,同时把迭代器包装成Box<dyn Iterator>:
// 修改add_sexp的签名,去掉泛型T,调整simplifier类型 pub fn add_sexp( &mut self, sexp: Sexp, simplifier: Option<Box<dyn FnMut(Tags, Box<dyn Iterator<Item=Index>>, &mut Asg) -> Either<Node, Index>>>, ) -> Index { match sexp { Sexp::String(symb) => self.add_node(Node::String(symb)), Sexp::List(args) => { match &args[0] { Sexp::List(_) => panic!("a tag cannot be a list"), Sexp::String(t) => { let tag = Tags::from_str(t.as_str()).unwrap_or_else(|_| panic!("Unknown tag")); // 构建延迟迭代器:每次next才处理对应的Sexp let args_iter = args[1..].iter().map(|a| self.add_sexp(a.clone(), simplifier.as_mut().map(|f| f.as_mut()))); self.add_internal(tag, Box::new(args_iter), simplifier) } Sexp::Empty => panic!("empty tag") } } Sexp::Empty => panic!("empty input") } } // 修改add_internal的签名 pub fn add_internal( &mut self, tag: Tags, generator: Box<dyn Iterator<Item=Index>>, mut simplifier: Option<Box<dyn FnMut(Tags, Box<dyn Iterator<Item=Index>>, &mut Asg) -> Either<Node, Index>>>, ) -> Index { match simplifier.take() { None => self.add_node(Node::Internal { tag, args: generator.collect() }), Some(mut s) => match s(tag, generator, self) { Left(n) => self.add_node(n), Right(i) => i } } }
方案2:调整泛型层级(更高效,无堆分配)
若想避免trait object的堆分配,可让add_sexp不声明泛型,由add_internal的泛型根据调用时的迭代器类型推导:
// 去掉add_sexp的泛型T pub fn add_sexp( &mut self, sexp: Sexp, simplifier: Option<impl FnMut(Tags, impl Iterator<Item=Index>, &mut Asg) -> Either<Node, Index>>, ) -> Index { match sexp { Sexp::String(symb) => self.add_node(Node::String(symb)), Sexp::List(args) => { match &args[0] { Sexp::List(_) => panic!("a tag cannot be a list"), Sexp::String(t) => { let tag = Tags::from_str(t.as_str()).unwrap_or_else(|_| panic!("Unknown tag")); // 延迟处理子表达式的迭代器 let args_iter = args[1..].iter().map(|a| self.add_sexp(a.clone(), simplifier.as_mut())); self.add_internal(tag, args_iter, simplifier) } Sexp::Empty => panic!("empty tag") } } Sexp::Empty => panic!("empty input") } } // add_internal保留泛型T,由调用者推导 pub fn add_internal<T>( &mut self, tag: Tags, generator: T, mut simplifier: Option<impl FnMut(Tags, T, &mut Asg) -> Either<Node, Index>>, ) -> Index where T: Iterator<Item=Index> { match simplifier.take() { None => self.add_node(Node::Internal { tag, args: generator.collect() }), Some(mut s) => match s(tag, generator, self) { Left(n) => self.add_node(n), Right(i) => i } } }
注意:该方案要求simplifier的参数迭代器类型和传入的generator类型完全匹配,适合无需动态切换迭代器类型的场景。
二、惰性求值实现
核心思路
要实现惰性求值,关键是避免提前处理所有子表达式,将子表达式的处理逻辑包装成延迟迭代器——只有当简化器需要获取下一个子节点时,才调用add_sexp处理对应的Sexp。这样像and这类逻辑表达式,可在遇到第一个false后停止迭代,不再处理后续子表达式。
具体实现
- 构建延迟迭代器:在
add_sexp处理列表节点时,不要先collect成Vec,直接返回map迭代器,每次next()才执行add_sexp处理子Sexp。 - 简化器逻辑适配:让简化器接收迭代器,在遍历过程中决定是否停止。例如
and的简化器可写为:
fn and_simplifier(tag: Tags, mut iter: impl Iterator<Item=Index>, asg: &mut Asg) -> Either<Node, Index> { for idx in iter { // 检查当前节点是否为false(假设Node::String("false")对应逻辑假) if let Node::String(s) = asg.get_node(idx) { if s == "false" { // 直接返回false,停止迭代后续子表达式 return Left(Node::String("false".to_string())); } } // 可在此记录已处理节点,若全为true则返回true } // 所有子表达式都为true,返回true Left(Node::String("true".to_string())) }
- 集成到代码:将上述简化器通过
Box包装后传入add_sexp,处理(and A B false C D)时,迭代器在处理到false对应的节点后停止,不再处理C和D。
关键注意点
- 迭代器必须是惰性的:避免使用
collect等消耗整个迭代器的方法,确保子表达式只在需要时被处理。 - 简化器需控制迭代流程:通过
for循环或手动调用iter.next()逐个处理子节点,满足终止条件时立即返回,中断迭代。
内容的提问来源于stack exchange,提问作者Pierre Carbonnelle
相关产品推荐
相关产品推荐

