如何将尾递归重写为循环?
How to rewrite tail recursion as a cycle?
我写了一个简单的前缀树变体:
struct Trie {
data: Vec<Option<Trie>>
}
impl Trie {
pub fn new() -> Trie {
let mut data = Vec::with_capacity(26);
for _ in 0..26 {
data.push(None);
}
Trie { data: data }
}
pub fn add(&mut self, word: &str) {
if word.len() != 0 {
let idx = Trie::char_idx(word.char_at(0));
let word_suffix = word.slice_from(1);
if self.data[idx].is_none() {
self.data[idx] = Some(Trie::new());
}
self.data[idx].as_mut().unwrap().add(word_suffix)
}
}
pub fn contain(&self, word: &str) -> bool {
if word.len() == 0 {
true
} else {
match self.data[Trie::char_idx(word.char_at(0))] {
Some(ref next) => next.contain(word.slice_from(1)),
None => false
}
}
}
fn char_idx(chr: char) -> usize {
(chr as u32 - 97) as usize
}
}
除了 add
函数的递归性质外,它工作正常。现在,rust 没有尾调用优化,所以我需要将其重写为一个循环。
这项任务应该是微不足道的,但如果没有借用检查员对我大喊大叫,我无法弄清楚如何做到这一点。
这是我天真的方法:
pub fn add(&mut self, word: &str) {
let mut current = self;
for chr in word.chars() {
let idx = Trie::char_idx(chr);
if current.data[idx].is_none() {
current.data[idx] = Some(Trie::new());
}
current = current.data[idx].as_mut().unwrap();
}
}
我可以做什么?
我记得读过其他问同样概念性问题的问题,但我现在找不到。也许善良的灵魂会link他们在这里。
也就是说,编译:
pub fn add(&mut self, word: &str) {
let mut current = self;
for chr in word.chars() {
current = {
let tmp = current;
let idx = Trie::char_idx(chr);
if tmp.data[idx].is_none() {
tmp.data[idx] = Some(Trie::new());
}
tmp.data[idx].as_mut().unwrap()
}
}
}
我的总体想法是解决当前 borrow scopes are lexical 的问题。在 for 循环内部,我创建了一个新的词法作用域,然后将 current
重新借用为 tmp
。我们在 tmp
上进行工作,最终返回一个引用,然后我们将其放回 current
.
fold
似乎可以解决问题:
pub fn add(&mut self, word: &str) {
word.chars().fold(
self,
|current, chr| {
let idx = Trie::char_idx(chr);
if current.data[idx].is_none() {
current.data[idx] = Some(Trie::new());
}
current.data[idx].as_mut().unwrap()
});
}
我写了一个简单的前缀树变体:
struct Trie {
data: Vec<Option<Trie>>
}
impl Trie {
pub fn new() -> Trie {
let mut data = Vec::with_capacity(26);
for _ in 0..26 {
data.push(None);
}
Trie { data: data }
}
pub fn add(&mut self, word: &str) {
if word.len() != 0 {
let idx = Trie::char_idx(word.char_at(0));
let word_suffix = word.slice_from(1);
if self.data[idx].is_none() {
self.data[idx] = Some(Trie::new());
}
self.data[idx].as_mut().unwrap().add(word_suffix)
}
}
pub fn contain(&self, word: &str) -> bool {
if word.len() == 0 {
true
} else {
match self.data[Trie::char_idx(word.char_at(0))] {
Some(ref next) => next.contain(word.slice_from(1)),
None => false
}
}
}
fn char_idx(chr: char) -> usize {
(chr as u32 - 97) as usize
}
}
除了 add
函数的递归性质外,它工作正常。现在,rust 没有尾调用优化,所以我需要将其重写为一个循环。
这项任务应该是微不足道的,但如果没有借用检查员对我大喊大叫,我无法弄清楚如何做到这一点。
这是我天真的方法:
pub fn add(&mut self, word: &str) {
let mut current = self;
for chr in word.chars() {
let idx = Trie::char_idx(chr);
if current.data[idx].is_none() {
current.data[idx] = Some(Trie::new());
}
current = current.data[idx].as_mut().unwrap();
}
}
我可以做什么?
我记得读过其他问同样概念性问题的问题,但我现在找不到。也许善良的灵魂会link他们在这里。
也就是说,编译:
pub fn add(&mut self, word: &str) {
let mut current = self;
for chr in word.chars() {
current = {
let tmp = current;
let idx = Trie::char_idx(chr);
if tmp.data[idx].is_none() {
tmp.data[idx] = Some(Trie::new());
}
tmp.data[idx].as_mut().unwrap()
}
}
}
我的总体想法是解决当前 borrow scopes are lexical 的问题。在 for 循环内部,我创建了一个新的词法作用域,然后将 current
重新借用为 tmp
。我们在 tmp
上进行工作,最终返回一个引用,然后我们将其放回 current
.
fold
似乎可以解决问题:
pub fn add(&mut self, word: &str) {
word.chars().fold(
self,
|current, chr| {
let idx = Trie::char_idx(chr);
if current.data[idx].is_none() {
current.data[idx] = Some(Trie::new());
}
current.data[idx].as_mut().unwrap()
});
}