Rust で,行と列を転置する
やりたいこと
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
という 2 次元ベクタがあったときに,これを転置して
[[1, 4, 7],
[2, 5, 8],
[3, 6, 9]]
にする transpose 関数を作りたいと思います. Rust はイテレータが扱いやすいので,転置したもののイテレータが得られるようにします.使い方としては,次のようになります.
fn main() {
let matrix = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
let result: Vec<_> = matrix.iter().transpose().collect();
assert_eq!(
result,
vec![vec![&1, &4, &7], vec![&2, &5, &8], vec![&3, &6, &9]]
);
}
transpose 関数はベクタへのイテレータを返すようにします.たとえば今回なら,返り値は Iterator<Item = Vec<&i32>> を impl しており,それを .collect() した result の型は Vec<Vec<&i32>> となっています.
発想
返り値を構造体 Transposed として, impl Iterator for Transposed することにします.
Transposed は matrix[0].iter(), matrix[1].iter(), matrix[2].iter(), ……をベクタとして持っておき, .next() が呼び出されるたびに各イテレータを一つずつ進めます.
よって Transposed のもつメンバはイテレータを要素にもつベクタ 1 つです.これの名前を iters とします.
境界
各 matrix[i] についてイテレータが呼び出せればよいです. &Vec<T> は IntoIterator<Item = &T> を impl しているので,これを利用します.
よって, &matrix[0] &matrix[1] &matrix[2] ……が得られれば,各々に IntoIterator::into_iter を適用することでベクタ iters を作ることができます.
また, &Vec<Vec<T>> は IntoIterator<Item = &Vec<T>> を impl しているので, &matrix に対し IntoIterator::into_iter を呼び出せば &matrix[0] &matrix[1] &matrix[2] ……が得られます.
よって, Iter: IntoIterator<Item = &Elem> , T: IntoIterator<Item = Iter> として T に対して transpose 関数を呼び出せるように設計します.
実装
トレイト
x.transpose() の形式で呼び出したいので, trait Transpose を定義します.
trait Transpose<'a, Elem, Iter, T>
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
T: IntoIterator<Item = Iter>,
{
fn transpose(self) /* -> Transposed 構造体 */;
}
impl<'a, Elem, Iter, T> Transpose<'a, Elem, Iter, T> for T
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
T: IntoIterator<Item = Iter>,
{
fn transpose(self) /* -> Transposed 構造体 */ {
todo!();
}
}
IntoIterator::into_iter 関数の返り値は IntoIterator::IntoIter です. Transposed 構造体は, IntoIterator::IntoIter を要素にもつベクタ iters をメンバとしてもつので, Transposed は型パラメータとして Elem と Iter: IntoIterator<Item = &Elem> をとります.
struct Transposed<'a, Elem, Iter>
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
{
iters: Vec<Iter::IntoIter>,
}
よって,先ほど書かなかった transpose 関数の返り値の型は, Transposed<'a, Elem, Iter> となります.
そして,この Transposed<'a, Elem, Iter> に Iterator<Item = Vec<&'a Elem>> を impl します.
impl<'a, Elem, Iter> Iterator for Transposed<'a, Elem, Iter>
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
{
type Item = Vec<&'a Elem>;
fn next(&mut self) -> Option<Self::Item> {
todo!();
}
}
関数の中身
実装する関数は, T 上の Transpose::transpose と, Transposed 上の Iterator::next の 2 つです.
transpose 関数は, T に対し into_iter() を呼び出し, IntoIterator::into_iter を map した上で Vec<Iter::IntoIter> に collect します.返り値は,これを iters メンバにもつ Transposed です.よって次のようになります.
impl<'a, Elem, Iter, T> Transpose<'a, Elem, Iter, T> for T
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
T: IntoIterator<Item = Iter>,
{
fn transpose(self) -> Transposed<'a, Elem, Iter> {
Transposed {
iters: self.into_iter().map(IntoIterator::into_iter).collect(),
}
}
}
next 関数は,まず iters の各要素に対し Iterator::next を map します.すると, Iterator::next が Option<&Elem> を返すので,これを Option<Vec<&Elem>> に collect します( V: FromIterator<A> なる V と A について, FromIterator<Option<A>> for Option<V> が impl されています.今回は A を &Elem , V を Vec<&Elem> とした形です).よって次のようになります.
impl<'a, Elem, Iter> Iterator for Transposed<'a, Elem, Iter>
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
{
type Item = Vec<&'a Elem>;
fn next(&mut self) -> Option<Self::Item> {
self.iters.iter_mut().map(Iterator::next).collect()
}
}
全体
全体では,次のようになります.
fn main() {
let matrix = vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]];
let result: Vec<_> = matrix.iter().transpose().collect();
assert_eq!(
result,
vec![vec![&1, &4, &7], vec![&2, &5, &8], vec![&3, &6, &9]]
);
}
trait Transpose<'a, Elem, Iter, T>
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
T: IntoIterator<Item = Iter>,
{
fn transpose(self) -> Transposed<'a, Elem, Iter>;
}
impl<'a, Elem, Iter, T> Transpose<'a, Elem, Iter, T> for T
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
T: IntoIterator<Item = Iter>,
{
fn transpose(self) -> Transposed<'a, Elem, Iter> {
Transposed {
iters: self.into_iter().map(IntoIterator::into_iter).collect(),
}
}
}
struct Transposed<'a, Elem, Iter>
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
{
iters: Vec<Iter::IntoIter>,
}
impl<'a, Elem, Iter> Iterator for Transposed<'a, Elem, Iter>
where
Elem: 'a,
Iter: IntoIterator<Item = &'a Elem>,
{
type Item = Vec<&'a Elem>;
fn next(&mut self) -> Option<Self::Item> {
self.iters.iter_mut().map(Iterator::next).collect()
}
}
また,これを使うと ABC182 E - Akari が次のように解けます: AC コード.
以上,行と列の転置でした.この記事が誰かの役に立てば幸いです.
Discussion