魔術師をめざして

魔術師を目指して、相場・数学・プログラム言語を研究しています。

Python って Scheme なの?

プロジェクト・オイラー問題 1 の解答が Scheme で書かれているのを眺めていたら、ふと、これって Python でもまったくおなじように書けるんじゃないか!? と気がついた。

f:id:fxrobot:20150129171552j:plain

その前にまず、プロジェクト・オイラーとはなにか。以下のサイトを見てもらえばわかるだろう。プログラミングのお題。ぼくも取り組んでいるよ。

本家サイト       : https://projecteuler.net/
日本語訳サイト: http://odz.sakura.ne.jp/projecteuler/

プロジェクト・オイラー問題 1。

『10未満の自然数のうち, 3 もしくは 5 の倍数になっているものは 3, 5, 6, 9 の4つがあり, これらの合計は 23 になる. 同じようにして, 1000 未満の 3 か 5 の倍数になっている数字の合計を求めよ.』


で、まずは Scheme だとどう解けるのか。

; in scheme
(display
    (reduce + 0
        (filter
            (lambda (x)
                (or (= (remainder x 3) 0) (= (remainder x 5) 0)))
                (iota 1000))))
(newline)

いかがだろう。
これを Python で書いてみると以下のようにる。

from functools import reduce
print(
    reduce(
        lambda a, b: a+b,
            filter(
                lambda x: x%3==0 or x%5==0,
                    range(1000))))
print()

SchemePython の 2つの構造はまったくおなじ。display → print、reduce → reduce、filter → filter、lambda → lambda、iota → range と対応している。最初の import が必要なのは Guido(Python の発明者)が reduce は醜いと functools のなかに追いやったためとどこかに書いてあったような。

初心者の人向けに少し説明しよう。まずは、Python の lambda に注目。lambda ってなにか? Python では関数は def で作ってから使うが、lambda を使えば、使いたいその場で名前を定めることなく使うことができる。たとえば、def nantoka(x, y): return x*y+2y という関数は lambda x, y: x*y+2y などと書いて関数名は無名のまま使える。

さて、Pythonコードの下から説明してみよう。まずは、お馴染み range で、0 から 999 までの数列を作る。「filter(lambda x: x%3==0 or x%5==0,」によって 0 から 999 の数列にまさにフィルターを掛け、これで 3 または 5 で割り切れる数だけの数列になる。

つぎに「reduce(lambda a, b: a+b,」によってその数列の数すべてを足し併せる。そして結果を print 。Scheme とおなじだね。上の Python のコードはそのまま動いて正しい答えを出力してくれるが、もっと簡単に print(sum(x for x in range(1000) if x%3==0 or x%5==0)) こう書いてもおなじように動く。

ちなみに Ruby だとこんな感じ。

sum=0
1000.times do |i|
    if i%3==0 or i%5==0
        sum+=i
    end
end
puts sum

えっ、こっちがいい? ならば Python だって。

sum=0
for i in range(1000):
    if i%3==0 or i%5==0:
        sum+=i
print(sum)

さらに、ぼくならいつもこう書くんだ。賛否はあると思うが、とくに単純な1行 if ならこう書いたほうが読みやすいと思う。

sum=0
for i in range(1000):
    if i%3==0 or i%5==0: sum+=i
print(sum)

こうやって見てみると、これが素朴で一番わかりやすい。


で、ことはついでに、プロジェクト・オイラー問題 2。

フィボナッチ数列の項は前の2つの項の和である. 最初の2項を 1, 2 とすれば, 最初の10項は以下の通りである.
                    1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
数列の項の値が400万以下の, 偶数値の項の総和を求めよ.』

ぼくの解答はこう。

f:id:fxrobot:20150207105927j:plain

折り曲がっているだろうけど、これ 1行。 で、ただしこれ、 機械伯爵さん(http://blog.livedoor.jp/kikwai/)から基本的なアイデアをいただいた。

ちょっと、裏技的過ぎて読みにくいかも知れない。