LoginSignup
1
0

More than 5 years have passed since last update.

Rubyで累積的計算を遅延評価する

Posted at

はじめに 

 まず簡単な例を挙げてみます。
 例えば、1から順番に整数の二乗した値を足し算していき、累積的に加算された値が10000を超えない最大の数を求める場合です。

 Haskellで書くとこんな感じです。無限リストが使えます。
 last $ takeWhile (<=10000) $ scanl (\b a-> b+a*a) 0 [1..]

Rubyでは

Rubyのinjectやeach_with_objectが近いのですが、遅延評価してくれないので無限配列を使えません。近い機能をもつメソッドを作ってみました。

class Enumerator::Lazy
    def scan(init)
        prev=init
        Enumerator::Lazy.new(self){|yielder, *values|
            yielder << (prev=yield prev, *values)
        }
    end
end

puts (1..Float::INFINITY).lazy.scan(0){|r,a| r+a*a}.take_while{|a| a<=10000}.to_a.last

Haskellのscanlでは、初期値がリストの先頭に入りますが、上のruby版scanは初期値が入らない違いがあります。

他の使いかたも

初期値に空の配列を与えて累積的に配列に要素を追加していくことで、いろいろな計算で遅延評価できるようになります。

uniqの遅延評価だと
p [1,2,3,4,1,3].lazy.scan([]){|r,a| r.include?(a) ? r : r+[a]}.to_a.last

少し危険な香りがしますが、破壊的代入で
p [1,2,3,4,1,3].lazy.scan([]){|r,a| r.include?(a) ? r : r<<a}.to_a.last
もありかなあ、もっとも遅延評価できるuniqが標準で入っていますのでわざわざ作る必要ないのですが

そこまでに計算した累積結果を元に計算できますので、いろいろな応用がありそうです。

1
0
3

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
1
0