Rustで「組み合わせ」をつくるのに使ってみたいメソッド

はじめに

形状処理チーム柳野です。 突然ですが、「任意の組み合わせのパターンで○する」のような処理は、形状処理に限らず多くの分野で必要になると思います。 例えば板金の形状認識の分野では、「面と面の組み合わせ」を条件と照らし合わせて、そのモデルが板部分(Wall)、曲げ部分(Bend)なのかを判定したりしています。

そこで今回は、形状処理チームではリモデルの開発言語として用いているRustで「組み合わせ」の処理を記述するのに役立ちそうなメソッドを学んだので紹介したいと思います。

板金の形状認識については以下ご覧ください。

techblog.dt-dynamics.com

形状処理分野でのRust導入に至る経緯は以下をご覧ください。

techblog.dt-dynamics.com

itertoolsのメソッドを使って、「組み合わせ」を作ろう

組み合わせを作りたいもの一覧は配列としてひとかたまりになっていることが多いと思いますので、今回は「配列の要素から組み合わせを作る」ことにフォーカスします。 例えば既存のメソッドとしてitertoolsクレートにはtuple_combinations()メソッドやiproduct!マクロがあります。

それぞれを比較すると以下のようになります。

tuple_combinations() iproduct!
使用例 1つのリストから組み合わせを選ぶ場合 複数のリストの要素を組み合わせる場合
生成できる組み合わせ 組み合わせ(nCr) 複数リストのすべての組み合わせ(直積,デカルト積)

tuple_combinations()は「同一の性質をもつもの同士の組み合わせを作る場合」に、iproduct!は「異なる性質をもつもの同士の組み合わせを作る場合」に役立ちそうです。

ユースケース

ユースケースとして以下を確認する処理を考えてみます。

  • 「異なる偶数同士の和は偶数」
  • 「偶数と奇数の和は奇数」

パラメータとして偶数をまとめた配列と奇数をまとめた配列、偶数かどうかを確認する関数を用意します。

let even_numbers = vec![2, 4, 6, 8, 10];
let odd_numbers = vec![1, 3, 5, 7, 9];
let is_even = |n: i32| n % 2 == 0;

そのうえで素直にforループを使うと以下のように書けます。

// 偶数同士の和は偶数
for &a in &even_numbers {
    for &b in &even_numbers {
        if a == b {
            continue;
        }
        let sum = a + b;
        assert!(is_even(sum), "Failed: {} + {} = {} is not even", a, b, sum);
    }
}

// 偶数と奇数の和は奇数
for &a in &even_numbers {
    for &b in &odd_numbers {
        let sum = a + b;
        assert!(!is_even(sum), "Failed: {} + {} = {} is not odd", a, b, sum);
    }
}

読めなくはないですが、forループが2つあって少し煩雑な印象を持ちます。 組み合わせの要素数が増えるとさらにループが増えてしまいます。 ではitertoolstuple_combinations()メソッドやiproduct!マクロを使ってみるとどうでしょうか。

use itertools::iproduct;
use itertools::Itertools;

// 偶数同士の和は偶数
for (a, b) in even_numbers.iter().tuple_combinations() {
    let sum = a + b;
    assert!(is_even(sum), "Failed: {} + {} = {} is not even", a, b, sum);
}

// 偶数と奇数の和は奇数
for (a, b) in iproduct!(even_numbers, odd_numbers) {
    let sum = a + b;
    assert!(!is_even(sum), "Failed: {} + {} = {} is not odd", a, b, sum);
}

各々の処理でループの数が減り、コードがすっきりしました。 また、メソッドを使うことで「組み合わせを作りたい」という意図が明確になり、可読性も向上したと思います。

まとめ

今回はRustで「組み合わせ」をつくるときに使ってみたいメソッドについて紹介しました。 itertoolsクレートのtuple_combinations()メソッドやiproduct!マクロを使うことで、すっきりとした記述でありながら「組み合わせを作りたい」という意図を明確に表現できるようになりそうです。

終わりに

形状処理チームでは、Rustを用いた形状処理の開発を進めています。 チーム全体の紹介はこちらをご覧ください。

techblog.dt-dynamics.com

興味をもっていただいた方は、カジュアルから面談でも結構ですのでぜひご連絡ください。