LoginSignup
5
5

More than 5 years have passed since last update.

1つの値を遅延評価してみよう

Last updated at Posted at 2017-12-11

「Rubyで遅延評価」と言えば、Enumerator::Lazyをよく見かけますが、それ以外の手段を使うことになった話をしてみます。

遅延評価、とは

Rubyを含め多くの言語では、a = foo(b)のようにすれば、foo(b)の処理が行われて、その結果がaに代入される、という挙動になります。これを先行評価といいます。

一方で、Haskellでは、(そもそも変数は再代入不可、関数も結果は引数のみで一意に決まる、という特徴があるからこその話という面も大きいですが)実際にaの値が必要となるまでfoo(b)の処理は行われません。これを遅延評価といいます。

遅延評価が便利な面

遅延評価にしてメリットがあることといえば、

  • 無限リストや、ものすごく長いリストも簡便に扱える(Enumerator::Lazyもこれです)
  • 引数に渡すために計算はしたけど、関数内で使わなかった…という余計な計算をカットできる(竹内関数では劇的に効果があることで有名です)
  • 宣言と評価のタイミングをずらすことができる

今回使ったライブラリ

Rubyの場合、標準ライブラリについているDelegatorのように、ダックタイピングやmethod_missingを駆使すれば、「他のオブジェクトにメソッドを丸投げする」ようなオブジェクトは比較的簡単に作れます。

今回は、bhuga/promising-futureを使ってみましたが、ソースコードはシンプルなものでした。

使うことになった場面

Railsで検索条件をハッシュ定数に入れて、あとでぶん回す、なんてことをやっていたのですが、条件値の1つとしてscopeを入れていたため、データベースのない環境下では定数定義の段階でDB接続を探してしまって死ぬ、という事態となってしまいました。

しかも、定数を使った構造は変えづらいという事情もあったので、遅延評価して「DBがなくても、実際に使わなければ問題なく通る」という形にしました。さっきのライブラリでKernel.#promiseが生えているので、こんな形となりました。

class SomeCalculator
  CALCULATIONS = {
    foo: promise { SomeModel.some_cond_scope }
  }
end

まあ、本来は定義ごと違う形にすべきなのかもしれませんが、こんな遅延評価の使い方もある、という話でした。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5