haskellでフィボナッチ そしてPerlで書いてみた

普通のhaskellプログラミングを読み終えて、なんか次に読むものないかなと探していたら、webarchiveに面白そうなページがあることを知ったので、ダウンロードして読んでます。

3回目の講義資料に、タプルを使って高速にフィボナッチ数を計算する話があったので、なんでそうなるのかを理解するために手で書き下してみた。

普通に書くとこんな感じ(fib)。

fact :: Int -> Int
fact n
  | n == 0  = 1
  | n > 0   = fact (n-1) * n

書き下すと、計算量があっという間に増えていくことがわかる。

fib 5
=> fib 3 + fib 4
=> (fib 1 + fib 2) + (fib 2 + fib 3)
=> (fib1 + (fib 0 + fib 1)) + ((fib 0 + fib 1) + (fib 1 + fib 2))
=> (fib1 + (fib 0 + fib 1)) + ((fib 0 + fib 1) + (fib 1 + (fib 0 + fib 1)))

タプルを使った例(fastfib)

フィボナッチ数列を効率良く計算する方法を考えよう.前に見たとおり,フィボナッチ数列の各項は,その前の二つの項の和である.逆に言えば,二つの連続した項の値が与えられれば,次の項を計算するのは簡単なのだ.

fibStep :: (Int, Int) -> (Int, Int)
fibStep (u, v) = (v, u+v)

fibPair :: Int -> (Int, Int)
fibPair n
  | n == 0    = (0, 1)
  | otherwise = fibStep (fibPair (n-1))

fastFib :: Int -> Int
fastFib = fst . fibPair

コードを眺めただけではなんで速くなるのか実感がわかなかったので、書き下してみる。

fastFib 5
=> fst (fibPair 5)
=> fst (fibStep (fibPair 4))
=> fst (fibStep fibStep (fibPair 3))
=> fst (fibStep fibStep fibStep (fibPair 2))
=> fst (fibStep fibStep fibStep fibStep (fibPair 1))
=> fst (fibStep fibStep fibStep fibStep fibStep (fibPair 0))
=> fst (fibStep fibStep fibStep fibStep fibStep (0, 1))
=> fst (fibStep fibStep fibStep fibStep (1, 1))
=> fst (fibStep fibStep fibStep (1, 2))
=> fst (fibStep fibStep (2, 3))
=> fst (fibStep (3, 5))
=> fst (5, 7)
=> 5

をを、タプルにすることで、計算量が減ってます、自分で展開していくとなるほどとよくわかる。

さて、最近Higher-order Perlを読んでいる僕としては、これをperlで書いてみたくなるわけである。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/perl

use strict;
use warnings;

sub fibStep {
    my $n = shift;
    return [$n->[1], $n->[0] + $n->[1]];
}

sub fibPair {
    my ($n, $fs, $fp) = @_;
    return [0,1] if ($n == 0);
    return $fs->($fp->($n-1,$fs,$fp))
}

sub fastFib {
    my $n = shift;
    my $fs = \&fibStep;
    my $fp = \&fibPair;
    my $fib_num = $fp->($n,$fs,$fp);
    return $fib_num->[0];
}

my $number = shift;
print fastFib($number)

サブルーチンのリファレンスを引数にとらないといけないのがhaskellと異なるとこかな。あとは書いてて思ったのは関数の型を書くとプログラムの流れが理解しやすいナァと。

実行すると一瞬で計算が終わる(速い!)

実際に処理の流れを追うために-dオプションを付けて実行してsを連打すると、main::fibPairがどんどん実行されて$nが0になったところで今度はmain::fibStepが次々実行されていく様がわかる。

Higher-order Perlの5章にもフィボナッチ数列のことが書いてあるのだが、まだそこまで読み進めていない。(今3章突入したところ)

ProductName Higher-Order Perl: Transforming Programs with Programs
Mark Jason Dominus
Morgan Kaufmann / 5692円 ( 2005-03-28 )


ProductName ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門
青木 峰郎
ソフトバンククリエイティブ / ?円 ( 2006-06-01 )


haskellを勉強しながら、Higher-order Perlを読むと、色々新たな発見があって楽しい。

Sudoku

実家に帰ったら、婆さんが数独にはまってた。ボケ防止にいいらしい。ちょうどいいので、flashの数独をgoogleで適当に探してきてお気に入りに登録しておいてやったら喜ばれたヨ。まぁ、使うかどうかわからんけど。

ちょうど、Highr Order Perl読み始めたところだし、家に戻ってきて、数独ソルバでも書いてみるかと思ってたら、タイミングよくUno, dos, tres, quatro!にエントリが。

ProductName Higher-Order Perl: Transforming Programs with Programs
Mark Jason Dominus
Morgan Kaufmann / 6402円 ( 2005-03-28 )


バックトラック法ってのを使えばいいのねと自分で書いてみたのはマス目を一つ一つ移動してくコードだったんだけど、再帰深すぎるヨっていうエラーを吐いて終了。なんかダメダメだなぁ。他の人のを読んでみるとまだ埋まってないマス目を再帰で埋めていくべきだったことに気付いた。ちょっとこれに引っ張られたかなと反省。

まだ埋まってないマス目の配列と、過程として数字を置いたマス目の配列の二つを用意して、バックトラック法で解くというわけですな。

とか、わかった上で、三行数独ソルバを読むと、説明もついてるしよくわかった、素敵。

家のマシンで三行数独ソルバを実行してみるとこんな感じ。

 $ time perl sudoku.pl < sample
 825169734
 463857921
 917234658
 684712593
 759386142
 231495867
 548621379
 376948215
 192573486 #本当はココまで一行で出力

 real    0m1.439s
 user    0m1.436s
 sys     0m0.004s

プログラム・プロムナードにhaskellの数独ソルバが載ってたのでそれも実行してみた。ちゃんと理解しないといけないと思いつつ、今回はコピペ。ghciで計算させた。

 *Main> sudoku sample
 [8 2 5 1 6 9 7 3 4
 4 6 3 8 5 7 9 2 1
 9 1 7 2 3 4 6 5 8

 6 8 4 7 1 2 5 9 3
 7 5 9 3 8 6 1 4 2
 2 3 1 4 9 5 8 6 7
 5 4 8 6 2 1 3 7 9
 3 7 6 9 4 8 2 1 5
 1 9 2 5 7 3 4 8 6
 ]
 (0.28 secs, 26584692 bytes)

haskellもperlクイズみたいなのを誰かやってくれたら嬉しいナァ。とか。

yum-fastestmirror

yumで速いリポジトリを自動的に選択するには - @IT

yum-fastestmirrorは、ミラーリストから速いリポジトリを自動的に選択してくれる。Fedora Extrasにパッケージが用意されているので、yumコマンドでインストールできる。

入れてみたが、そもそも家の回線が遅いのであまり実感できないのかも。または、いつも使っている理研のミラーが速いからプラグイン入れても速く感じないのかな。

X windowをテキストモードに変える

新しいマシンにFedoracore入れたらXが表示されない、、、のに、システムがX再起動をかけまくって無限ループに陥ってどうしようもないので、[Ctrl-Alt-F2]でコンソールにして作業してたら。

なんすかそれ?と

最近のLinuxはグラフィカルなログイン画面がデフォルトなので、真っ黒画面にlogin:だけがでるのが普通だって知らないのね。(昔はx11startとか打って、fvwm95とか使ってたのに)

ちなみに、[Ctrl-Alt-F7]でコンソールから X に復 帰

英語の勉強をしないといけないなと思った

ここのところ、立て続けに自分の考えを英語で表現できないもどかしさを感じて凹むという事態が続いたので、このままではいかんと思い、どうにかしようと決心した。

今までは、gooの英和を使っていたのだけど、ひけない単語が結構あったのと、レスポンスが若干気になるので、英辞郎使えないかと色々調べた。

お金をかけずブックマークレットとか拡張機能に頼る

ブックマークレットは便利だが、いちいちブックマークレットを実行させるためにカーソルを移動するのがちょっと手間かな。右クリックでメニューバーに表示されんかなと思って探したら発見。

右クリック辞書メニュー

これで結構快適に辞書を扱える。ただ、Biomedical,SBDDな単語とかはさすがにヒット率が低いので、単語とか例文とか自分で追加、修正したいなぁと。となると、辞書データを買って自分好みに育て上げるほうがエエので、辞書データも購入してみた。

英辞郎の辞書データを買って活用することを考える

pdicは使い始めて2,3日だが凄く快適で、もっと早くに使っておけばとか思った。常駐させておけば、コピー(Ctrl-c)した途端に辞書を探して、ヒットすればポップアップして表示するので使いやすい。Meadowだと、ハイライトするだけでポップアップ表示するのでもっと楽。

viewerで探してみつからない単語は編集画面になるので、そのままgoogleなんかで調べて登録すれば辞書データも充実するし、、、

当面はこれを使っていこうかと。

納涼生ビール電車

地元テレビ(よくわからないけどCATVなのかな?これは)見てたら、ビール電車があるらしい。おっ、とか思ったのでフジブログにもうチョイ面白い記事でもないかなと探りを入れたら、それらしいものはなかった。たいした企画ではないのかな。それよりも、

もっと素晴らしいものを見つけてしまった!

すげー、、、

面子とか見るとすげー行きたかったんだけど、、、ジェフベックとか、ヌーノとか中学ぐらいではまりましたよ。ちなみに、Ben Foldsは7,8年くらいにいったフジロックではまった。

あー、朝霧はいきたいなぁ。チケット取れるかどうかわからんけど。

毎度のつけ麺

毎度でつけ麺はじめたそうなので、遅まきながら出陣。子供連れなので、早めの空いてる時間にでもと平日の昼の部の始まりにあわせて。

ちょっと、早く着きすぎた。

そんなときは信号渡ってすぐの、三島大社で涼みながら待つことに。

三島大社 三島大社

うーん他にも2,3人うろうろしておる。もしや?!と思ったらその通り、開店待ちのおじさんたちでした。

開店即入店だったので、混まずに入れた。というわけで、ベビーカーもあまり邪魔にならずに、僕とU隊長の間に置かせてもらえた。毎度は子連れにヤサシゲなので素敵です。しかも前回は3ヶ月以上前に行ったのに覚えられてたのにはちょっとびっくり。

潮と醤油のつけ麺を。僕は潮。毎度の細い麺でつけ麺はどうだろうか?と思ってたが、やはりそこは麺職人、配合とか変えてつけ麺用の麺を用意してあるとのこと。

メニュー

で、つけ麺だが、若干麺が太い、でもちもちしてて旨い。麺の旨さが引き立つ潮で正解だった。

つけ麺 潮

麺が長いせいもあるんだと思うんだが、ちょっと麺がほぐれにくくて苦労したかなってくらい。水切りの加減かな。少しスープ割りもしてもらって余ったつけ汁を美味しくいただいた。

支那そば家 毎度!

支那そば家 毎度!

鮎を食べた

帰省ついでに鮎でも食うかと、やなまで出かけていった。本当は胡桃亭でもいって蕎麦食べたり、もちのきとかいうラーメン屋行きたかったんだけど、ユッキー様が小さいので無理だ。

さて、こっちのほうも合併がすすんでいるみたいで、さくら市とかなんとか町とか知らない市名が色々できてた。あそこらへんはそんなことやっても税制とか有利になるんだろうか?単なる見栄かな?

鮎御膳

鮎刺身 鮎塩焼き

やっぱ、鮎はうまい。

高瀬観光やな

Perlクイズを読み終えた

一ヶ月くらい前から暇をみてはPerlクイズを解いていたのだが、ここ2,3日は集中してすすめることができた。というわけで、本日終了。

最後のほうはパズルばかりでかなり手ごわいクイズが並んだが、一通りやり遂げた。

ProductName 結城浩のPerlクイズ
結城 浩
ソフトバンククリエイティブ / ?円 ( 2002-08 )


特に面白かったのはここらへん

初盤ではハッシュの操作とか正規表現なんかの問題が多かったが、特にソート関係は勉強になった。

あとは、「あらかじめ行数がわかっていなくてもランダムに行を表示する方法は特にイテレータ使ってる場合にランダムに一個選びたい場合にイイ。

chemichoっていうプログラムを書いていたときに、perlmolの部分構造マッチのメソッドがイテレータで、ヒット数を返すメソッドがなかったため、マッチした部分の中からランダムで一箇所選ぶためにわざわざ一回スキャンしてヒット数を調べてから、int(ヒット数*rand)番目を再度イテレータ使って取得するという二回同じイテレータをまわすプログラムだったという。

これって無駄だなと思っていたので、上のやり方はエレガントじゃ!と感動した。

perldoc perlvar

perlのコードを読んでると、たまに知らない変数が出てくることがあるが、$?とか$#みたいな記号のみで構成されているモノはgoogleでヒットさせにくくてイライラすることが多い

そんなときは

perldoc perlvar

で調べればいいということを知った。忘れるとアレなのでメモ。