uniquis
@uniquis

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

[rust]イテレータわからん

ひとまず動くコードは作れたけど、ちょっと納得行かない……
問題は言語処理100本ノック #05から

コード

現在のコード
// 05. n-gram
// 与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.

fn ngram<T: std::marker::Copy, I: Iterator<Item = T>>(iter: I, n: usize) -> Vec<Vec<T>> {
    let r:Vec<T> = iter.collect();
    let sz = r.iter().count();
    let mut res= Vec::new();
    for i in 0..sz-n+1{
        res.push(r.iter().skip(i).take(n).copied().collect());
    }
    res
}

fn main() {
    let s = "I am an NLPer";
    let r1 = ngram(s.split_whitespace(), 2);
    let r2 = ngram(s.chars(), 2);

    println!("{:?}", r1);
    println!("{:?}", r2);
}
出力
[["I", "am"], ["am", "an"], ["an", "NLPer"]]
[['I', ' '], [' ', 'a'], ['a', 'm'], ['m', ' '], [' ', 'a'], ['a', 'n'], ['n', ' '], [' ', 'N'], ['N', 'L'], ['L', 'P'], ['P', 'e'], ['e', 'r']]

課題

イテレータを引数に渡しているのに、中ですぐcollect()してから改めてループ処理してるところ。
iter.map(|x| [x, x.next()]).collect()みたいなことがしたいわけですが
1. x:<T>でありイテレータではないため、next()できない
2. 可能だったとして、next()でイテレータが消費されてしまう

という点が問題になっています。
動いてるのでOKではあるんですが、よりきれいに書ける方法を御存知の方にぜひ教えてほしいです。

0

1Answer

slice::windows という関数がありまして、これでスライスに対して似たようなこと(隣接するn個をまとめる)ができます(結局スライスに対しての操作なのでcollectはしないといけないですが……)
https://doc.rust-lang.org/std/primitive.slice.html#method.windows

T: Copy ならitertoolsの tuple_windows も使えるかなと思いますが、要素数が最大4なうえに型レベルで個数指定しないといけないのできびしいかもです。
https://docs.rs/itertools/0.9.0/itertools/trait.Itertools.html#method.tuple_windows

2Like

Comments

  1. @uniquis

    Questioner

    おお、やっぱりありますね!
    イテレータから直接できないのは注意点ですね…
    ありがとうございました。

Your answer might help someone💌