ScalaでProject Euler(3)

Problem 1

Pythonなら

from itertools import *

N = 1000
print sum(ifilter(lambda n: n % 3 == 0 or n % 5 == 0, xrange(1, N)))

Haskellなら

n = 1000
main = print (sum (filter (\m -> mod m 3 == 0 || mod m 5 == 0) [1..n-1]))

こんな関数型で書くことを目標にしましょう。

Range

まず、Pythonのxrangeみたいなのはこう書きます。

println (1 to 10)
Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Rangeオブジェクトといって、xrangeと同じように最初と最後の値だけ持つみたいです。あと、刻み幅を指定するにはこうします。

println (1 to 10 by 3)
Range(1, 4, 7, 10)

sum

和を取るには、

println ((1 to 10).sum)
55

こうです。メソッドで引数を取らない場合は括弧は省略できます。
それより、sumはRangeオブジェクトのメソッドなんですね。いままで見てきた言語では、sumは前に書いていたから違和感があります。例えば、Pythonではsumは組込み関数で、要素に+演算子が定義されているiterableならなんでもいいんだっけかな?HaskellはPreludeの関数、C++はstd::accumulateでテンプレートの力でなんとかしています。そういうわけで違和感があるのですが、万事この調子なので頭を切り替えて語順を逆にしなければなりません。

filter

sumの次はfilterです。書く順は逆ですが。

def even(n :Int) = n % 2 == 0
println ((1 to 10).filter(even).sum)
30

無名関数

無名関数を使いたいときはこうします。

println ((1 to 10).filter((n) => n % 2 == 0).sum)

こういう使い方をするときは型推論が働くので型が省略できるようです。
これでもうPythonHaskellのように書けますね。

val N = 1000
println ((1 to (N - 1)).filter((n) => n % 3 == 0 || n % 5 == 0).sum)