関数閉包で遊ぶ

今回は小ネタとして関数閉包(closure)で遊んでみる。 起点となるコードはこれにしよう。

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn main() {
    let x = 1;
    let y = 2;
    println!("{} + {} = {}", x, y, add(x, y)); //Output: 1 + 2 = 3
}

add() 関数は見ての通り整数の足し算。 この関数を関数閉包で表してみたい。

関数閉包で記述する

関数閉包の記述では fn 構文は使えない。 こんな感じに書く。

fn main() {
    let add = |x, y| {x + y};
    let x = 1;
    let y = 2;
    println!("{} + {} = {}", x, y, add(x, y)); //Output: 1 + 2 = 3
}

|...| で囲まれている部分で引数を定義し {...} で囲まれている部分が関数本体になる。 ちなみに,上のような記述であれば波括弧は省略できる。 ていうか rustfmt コマンドで整形すると問答無用で消される(笑)

let add = |x, y| x + y;

引数や返り値の型は推論可能であれば省略できるが,型注釈で明示することもできる。

let add = |x: i32, y: i32| -> i32 { x + y };

この関数閉包をもう少し弄ってみよう。

スマートポインタ

関数閉包はスマートポインタを表すトレイト Box<T> で囲むことができる。

fn main() {
    let add = Box::new(|x, y| x + y);
    let x = 1;
    let y = 2;
    println!("{} + {} = {}", x, y, add(x, y)); //Output: 1 + 2 = 3
}

敢えて add に型注釈を付けるとこんな感じになる。

let add: Box<dyn Fn(i32, i32) -> i32> = Box::new(|x, y| x + y);

ちなみに Fn は関数閉包を表すトレイトである。

これだけでは何も面白くないが,実は「高階関数(higher-order function)」で威力を発揮する。 高階関数の定番といえばアレだよね。 そう「カリー化(currying)」である1

カリー化と所有権

では早速 add() 関数をカリー化してみよう。 こんな感じ。

fn add(x: i32) -> Box<dyn Fn(i32) -> i32> {
    Box::new(move |y| x + y)
}

fn main() {
    let x = 1;
    let y = 2;
    println!("{} + {} = {}", x, y, add(x)(y)); //Output: 1 + 2 = 3
    let increment = add(x); //partial application
    println!("add({}) -> increment({}) = {}", x, y, increment(y)); //Output: add(1) -> increment(2) = 3
}

move キーワードを使って関数閉包内の変数 x の所有権を明示的に移動してる点に注意。 move を付けないと借用とみなされるが add() 関数を抜けるとスコープから外れるためコンパイルエラーになる。

ちなみに関数等のスコープ内であれば,もっと簡単に記述できる。 こんな感じ。

fn main() {
    let add = |x| move |y| x + y;
    let x = 1;
    let y = 2;
    println!("{} + {} = {}", x, y, add(x)(y)); //Output: 1 + 2 = 3
    let increment = add(x); //partial application
    println!("add({}) -> increment({}) = {}", x, y, increment(y)); //Output: add(1) -> increment(2) = 3
}

よーし,うむうむ,よーし。

参考図書

photo
プログラミング言語Rust 公式ガイド
Steve Klabnik (著), Carol Nichols (著), 尾崎 亮太 (翻訳)
KADOKAWA 2019-06-28 (Release 2019-06-28)
単行本
4048930702 (ASIN), 9784048930703 (EAN), 4048930702 (ISBN)
評価     

公式ドキュメントの日本語版。索引がちゃんとしているので,紙の本を買っておいて手元に置いておくのが吉。

reviewed by Spiegel on 2020-02-24 (powered by PA-APIv5)

photo
プログラミングRust
Jim Blandy (著), Jason Orendorff (著), 中田 秀基 (翻訳)
オライリージャパン 2018-08-10
単行本(ソフトカバー)
4873118557 (ASIN), 9784873118550 (EAN), 4873118557 (ISBN)
評価     

Eブック版あり。公式ドキュメントよりも系統的に書かれているので痒いところに手が届く感じ。ただし量が多いので,一度斜め読みしたらあとは傍らに置いて必要に応じてつまみ食いしていくのがいいだろう。

reviewed by Spiegel on 2020-03-08 (powered by PA-APIv5)


  1. カリー化については拙文「カリー化に関する覚え書き」を参考にどうぞ。 ↩︎