週に一回は書きますよ 月に4つ記事を書けばノルマは満たされます。
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
Lispはキーワードや#'などがごちゃごちゃしていて頭に入りません。 言語仕様のすっきりしているschemeのほうが好きです。

今まで私はこう思っていました。 もっとも、schemeで何か面白いものが書けるわけでもなく、 他学科聴講の課題を作っただけですが。

しかし、先日急に考えが変わりました。

普通の使い方をするには関数をそのまま呼び出して、 特殊な使い方をするときにはキーワードを使うってのは変なことではないのでは? むしろ、常にbegin,endを指定しないといけないC++STLのほうが特殊かもしれない。 また、言語仕様がちょっと複雑でも、その結果プログラムが楽に書けるなら 別にいいんじゃないか?

と思ったら急にLispを知りたくなりました。 手始めにPaul GrahamのANSI Common Lispからはじめてみます。

スポンサーサイト
懲りずにHaskellでシューティングゲームを作っています。駒場祭目標くらいで。
当然シューティングなのでアニメーションが入ります。GLUTを使っているので、Graphics.UI.GLUT.Callbacks.Global.addTimerCallback 16 (C言語で言うとglutTimerFunc)を使って、秒間 1000/16≒60 フレーム描画されるようにしています。

ふと気がつくと、このフレームレートが48 frames/sec くらいに下がってました。キャラクタを増やしたので処理が重くなったんでしょうか。プロファイリングもしてみましたが、当然処理すべき部分が結構呼ばれているのがわかるだけで、どうしようもありません。

なんだかよくわからなかったので、アニメーション方法をidleCallback(C言語ではglutIdleFunc)に変えて、フレームレートを最大に変えてみました。すると190 frames/sec でるじゃないですか。もうよくわかりません。

しかも今起動したら60 frames/sec に戻っています。もうわけがわかりません。フレームレートに依存しないプログラムを書いたほうがよさそうです。
ところで、Kleisliって何なんでしょう。

検索調査中....

http://pub.cozmixng.org/~the-rwiki/rw-cgi.rb?cmd=view;name=Haskell
 より
>モナドってモノイドの間違い?
>>モノイドである場合があるが、モナドはモナド. ただし Haskell のモナドは圏論におけるモナドそのものではなく、 それと同値な Kleisli triple と呼ばれるもの.
うわぁ。圏論だ。モナドだ。知ってなきゃいけなかったかも。

ICFP programming contestに参加していました。チームINTERCAmLで、。結果は4103点。スコアボード凍結以降にBLACKを1000点にし、あとBALANCEとADVENTUREを少しときました。

詳しい解説は k.inabaさんnushioさんshinichiro.hさんのところを見てください。

まず一言、本当に楽しかったです。主催者はかみ。72時間は疲れましたが。 エミュレータが比較的早くできたので(チームメイトに感謝!)、 UMIX Systemが作れてログインできた時点から遊びまくってました。

知り合いの中ではかなり良いほうなので個人的には満足なんですが、 それだけでは上にいけないので反省をしてみます。 上に行くには四人そろえる必要があったという気もしますが。

第一に挙げるべきなのはプロファイリング重要ということですね。 エミュレータが遅かったので終盤でADVENTUREをとくのに かなりの障害になってしまいました。 はじめにOcamlで書いたエミュレータがもっと遅くて、 C++で書き直したらちょっと速くなって満足してしまいました。 メモリをたくさん使っていたので、このくらい遅くても仕方ないと考えていた節もあります。

それから、自動化する前に小さい問題を手でといてみよう、ってのがあります。 ADVENTUREはshinichiro.hさんの「なんかもっと悲惨な世界」を越えるまでに 時間がかかりすぎました。 チームメートがパーサ作ってうんうん考えて後で反省していましたが、 私も一日目の夜(どう見ても二日目の朝です)はそればっかり考えて、 結局出来たのはbetter redirectみたいな代物だったのでちょっと反省。 この辺は問題をじっと眺めればよかったんですが。 この反省はICPCにもつながりますね。 アルゴリズム思いついたらとりあえずSample Inputがちゃんと通るかどうかやってみろというやつです。

こういう注意点はやはり一回躓かないと覚えませんね:D。 この経験をを糧にしないと。

一週間に一度は書きますよ *たぶん* であったとしても、二週間あけるのはさすがに問題があるような気がしました。反省。

暑い夏です。wxHaskellのバイナリパッケージが、ghc6.4.0で作られていたため、ghc6.4.2を使っている私のノートには導入できませんでした。

Functional Reactive Programmingの論文では、関数の型に良く知らない表記が出てきました。

dpSwitch :: Functor col =>
  (forall sf . (a -> col sf -> col (b,sf)))
  -> col (SF b c)
  -> SF (a, col c) (Event d)
  -> (col (SF b c) -> d -> SF a (col c))
  -> SF a (col c)

このforallって何でしょうか。意味はわかるんですが。ただのsfではだめみたいに見えますが何ででしょうか。うむむ無知すぎる。

仕方ないので書籍部に行ってふつけると他一冊を立ち読み。

書いてませんでした。ショック。

気が向いたので、Arrow調査 回答編のコードにインデントをつけてみます。 がりがりっと
import Control.Arrow
import Control.Monad
import Control.Monad.State

type LogState = State (Int,String)
type LogArrow  = Kleisli LogState

withLog :: (Show a,Show b) => String -> LogArrow a b -> LogArrow a b
withLog name a = Kleisli act where
                -- この型宣言だとエラーが出ます。どう書くべきなのでしょうか。
                -- act :: (Show a,Show b) => a -> LogState b
                act x = do
                        (indent,mes) <- get
                        let tab = replicate indent '\t'
                        put (indent+1, mes++tab++name++" "++show x++"\n")
                        res <- runKleisli a x
                        (indent2,mes2) <- get
                        let tab2 = replicate indent2 '\t'
                        put (indent, mes2++tab2++"return "++show res++"\n")
                        return res

orGate,andGate :: LogArrow (Bool,Bool) Bool
andGate = withLog "AND" $ arr $ uncurry (&&)
orGate  = withLog "OR"  $ arr $ uncurry (||)

inverter :: LogArrow Bool Bool
inverter = withLog "NOT" $ arr not

halfAdder :: LogArrow (Bool,Bool) (Bool,Bool)
halfAdder = withLog "HalfAdder" $
                        proc (a,b) -> do
                d <- orGate -< (a,b)
                c <- andGate -< (a,b)
                e <- inverter -< c
                s <- andGate -< (d,e)
                returnA -< (s,c)

fullAdder :: LogArrow (Bool,Bool,Bool) (Bool,Bool)
fullAdder = withLog "FullAdder" $
                proc (a,b,cIn) -> do
                        (s2,c2) <- halfAdder -< (b,cIn)
                        (s1,c1) <- halfAdder -< (a,s2)
                        cOut <- orGate -< (c1,c2)
                        returnA -< (s1,cOut)

main :: IO ()
main = do
        let (_,str) = execState (runKleisli fullAdder (True,True,True)) (0,"")
        putStr str

とりあえずfullAdderまで。StateTとかを使うのは面倒だったので、Stateにインデントの深さと出力すべき文字列とを保持するようにしました。IOはmainだけに封印。

ところでコメントにあるとおり、withLogの途中の関数の関数宣言がうまく出来ません。 解法募集中。

まあそれは当面問題はないのでコンパイルします。

arrowst.o(.text+0x56):fake: undefined reference to `ControlziMonadziState_zdfMon
adStates_closure'
arrowst.o(.text+0xc6):fake: undefined reference to `ControlziMonadziState_zdfMon
adStates_closure'
arrowst.o(.text+0x3d9):fake: undefined reference to `ControlziMonadziState_zdfMo
nadStates_closure'
arrowst.o(.text+0x929):fake: undefined reference to `ControlziMonadziState_zdfMo
nadStates_closure'
arrowst.o(.text+0x339a):fake: undefined reference to `ControlziMonadziState_exec
State_closure'
arrowst.o(.text+0x34a3):fake: undefined reference to `__stginit_ControlziMonadzi
State_'
arrowst.o(.rodata+0x0):fake: undefined reference to `ControlziMonadziState_zdfMo
nadStates_closure'
arrowst.o(.rodata+0x4):fake: undefined reference to `ControlziMonadziState_zdfMo
nadStates_closure'
arrowst.o(.rodata+0x10):fake: undefined reference to `ControlziMonadziState_zdfM
onadStates_closure'
arrowst.o(.rodata+0x158):fake: undefined reference to `ControlziMonadziState_exe
cState_closure'
collect2: ld returned 1 exit status
なんなんですか。なんで急にリンクエラー起こすんですか。<TM>

検索したら同じところで引っかかった人を見つけました。http://d.hatena.ne.jp/rahAloe/20060617/p1

どうも必要なパッケージ(MonadTrans関連?)がリンクされないようです。 リンク先にあるとおり、--makeオプションをつければ大丈夫でした。

実行結果

FullAdder (True,True,True)
        HalfAdder (True,True)
                OR (True,True)
                        return True
                AND (True,True)
                        return True
                NOT True
                        return False
                AND (True,False)
                        return False
                return (False,True)
        HalfAdder (True,False)
                OR (True,False)
                        return True
                AND (True,False)
                        return False
                NOT False
                        return True
                AND (True,True)
                        return True
                return (True,False)
        OR (False,True)
                return True
        return (True,True)

おお美しい。われながら良く出来たような気がします。

ところでこのコード、arrowというよりmonadの練習ですね。

解答編です。Arrow Circuit 1 by oxyさんの回路に、計算中に回路名を表示する機能をつけてみました。
type IOArrow = Kleisli IO

--aの実行前に nameを表示するArrow
addName :: Show b => String -> IOArrow b c -> IOArrow b c
addName name a = Kleisli act where
    act x = do
        putStrLn $ name++" "++show x
        runKleisli a x

--上記の関数版.
namedGate :: Show b => String -> (b -> c) -> IOArrow b c
namedGate str f = addName str $ arr f

namedOr = addName "OR" $ arr orGate
namedAnd = addName "AND" $ arr andGate
namedInv = addName "NOT" $ arr inverter

halfAdder :: IOArrow (Bool,Bool) (Bool,Bool)
halfAdder = addName "HalfAdder" $
            proc (a,b) -> do
              d <- namedOr -< (a,b)
              c <- namedAnd -< (a,b)
              e <- namedInv -< c
              s <- namedAnd -< (d,e)
              returnA -< (s,c)

fullAdder :: IOArrow (Bool,Bool,Bool) (Bool,Bool)
fullAdder = addName "FullAdder" $
            proc (a,b,cIn) -> do
                (s2,c2) <- halfAdder -< (b,cIn)
                (s1,c1) <- halfAdder -< (a,s2)
                cOut <- namedOr -< (c1,c2)
                returnA -< (s1,cOut)

main :: IO ()
main = do
        ans <- runKleisli fullAdder (True,True,True)
        putStrLn $ "Answer = " ++ show ans
出力結果です。
FullAdder (True,True,True)
HalfAdder (True,True)
OR (True,True)
AND (True,True)
NOT True
AND (True,False)
HalfAdder (True,False)
OR (True,False)
AND (True,False)
NOT False
AND (True,True)
OR (False,True)
Answer = (True,True)

やったできました。

次はこの出力にインデントをつけましょう。

ゲームを作るためにステートマシンを振り回しつつ、ふと気になったことがあります。 参照透過な言語で入出力を行うのにすぐ思いつくのはWorld型との相互作用を書くこと ですが、Haskellはモナドを用いることでもっときれいに入出力が書けます。 たしかこのような説明がどこかにあったような。 ステートマシンの更新もWorldの書き換えと似たようなものなので、これも実は モナドでかけるのではないでしょうか。

当面のプログラムはステートマシンで書くとして、そういうものも探しておいて 損は無い、むしろHaskellの書き方を学ぶ上で広く役立つかもしれない、と思いました。 そのような情報はおそらくhaskell.orgに行けば多くあるのかもしれません。 とりあえず私は手近なところ、tanakhさんの日記のコメントで言及されていた "The Yampa Arcade" を読んでみました。

いきなりArrowですよArrow。 正直判らないので、Arrow Circuit 1 by oxyさんを読んで勉強しました。 とりあえず(->)やKleisli arrowsの定義を読んで、計算途中に文字列を書き出すArrowを作ったんですが、失敗しました。結果しか出てきません。

--前略 orGateやandGateは上記の引用もとと同様のものを用いる
--計算ついでに文字を出力するArrow
type IOArrow = Kleisli IO
fun2IOA :: Show b => String -> (b->c) -> IOArrow b c
fun2IOA name fn = proc b -> do
	c <- (arr fn) -< b
	_ <- (arr $ putStrLn . (name++) . show) -< b
	returnA -< c

nameIOA :: Show b => String -> IOArrow b c -> IOArrow b c
nameIOA name ar = proc b -> do
	_ <- (arr $ putStrLn . (name++) . show) -< b
	c <- ar -< b
	returnA -< c

namedOr = fun2IOA "OR" orGate
namedAnd = fun2IOA "AND" andGate
namedInverter = fun2IOA "NOT" inverter

namedHalfAdder :: --省略

main :: IO ()
main = runKleisli namedAnd (True,True) >>= print
こう書いたら最後の結果のTrueしか出て来ないんです奥さん。 Kleisli arrowの定義に>>が入って無いからでしょうか。 IO monadは>>が、「前の結果いかんにかかわらず、前のものを実行してから後のものを実行する」という動作をしてくれたので、この中に副作用を押し込めたと理解しています。 とすると、それが定義に入っていないKleisli (IO a b)は、アクションのところでモナド結合を明示的に書かないとだめそうですね。

追記:7/18

上記のコードは、arrをKleisliに書き換えれば解答編と同じように出来ます。当時はarrの型を勘違いしていました。

親父に教えられました。
http://kiwi-us.com/~knp3/judge/cs.shtml
>あなたの一般常識の総合正解率は80.0 %です
>たいへん優秀な成績です。就職試験などで課せられる一般常識テストでは良い結果を得られると思います。

>ジャンル 正解率
>政治 90.0%
>経済 100.0%
>法律 80.0%
>歴史 60.0%
>国語 70.0%

法律は勉強してないから八割でもしょうがないか、とか思っていたらその後がgdgd。「パリって占領されたの?」とかいう人がいる学科だからって鈍りすぎです。
関数のCurry化は別になくても良いような気がします。Curry化によって特別な表記や関数なしにbind1stを記述できますが、与える引数の数やら引数の範囲やらを明示出来れば「関数の引数が足りないよ」とコンパイルエラーが出て便利です。もっとも、そのような言語の設計が可能かどうかは知りません。勉強しないと。

Curry化されると部分適用が簡単に書けるといいますが、簡単に書けるのは第一引数だけです。第二第三引数について部分適用をするにはflipやら新たに無名関数を書くやらしないといけません。第一引数の部分適用だけが簡単にかけるというのは、宣伝するほどすばらしい事ではないと思います。

O'camlでは、ミスとCurry化とが混ざると大きな被害となります。引数がひとつ足りなかったのに警告もでず、走るはずの関数が走らなかったというようなことがおきます。このあたり、関数起動と部分適用とを明示的に区別する構文があれば被害は少なくなると思うのですが。

ちなみにHaskellでは、コンパイルエラーに関する問題は少ないです。関数を呼んだときに引数が足りないと、カリー化されていてもどこかで型エラーが出る可能性が高いです。それでも、エラーメッセージが親切に出たほうがさまざまな人を取り込みやすいと思います。

一方、Curry化があると確実に良くなることとしては、関数の型が簡単になることが挙げられます。Curry化が無いと、引数の数の異なる関数同士をどうやって多相的に扱えばよいのでしょうか。私にはよくわかりません。bind1stやflipのような関数を書くのに苦労しそうです。C++のboostのように、引数の数に応じてたくさんの記述をしないといけないのでしょうか。


関数型言語に慣れてしまうとこういう意見は自分の中から消えてしまいそうなので、忘れないうちに書いてみました。
ところで、C++でベクタのマップを使ってがりがりファイル上の数値データを処理するプログラムを作ると、
#include <iostream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <iterator>
#include <vector>
#include <map>
#include <functional>
#include <complex>
#include <cmath>
のようになりませんか。一定の規則で命名された多数のファイルを扱う場合にはさらにsstreamを入れて、おおっとそもそもstringが入って無いぞとかやっていると、あっという間に#include文だけで15行程度になってしまいます。さらに、ここでiteratorは入れたかしらとか考え出すとさらにひどいことに。

この#includeリストを整理するには、あいうえお順に並べるほか無いでしょうか。iostreamが上に無いのはちょっと落ち着かないんですが。
入出力がどうとか数学的にどうとかはよくわかりません。 僕にわかることはただ、リストモナドが非常に萌えるということだけです。
import Control.Monad
answers = do
    a <- [1..9]
    b <- [0..9]
    c <- [0..9]
    let number = a*100+b*10+c
    if number == (a+b+c)*11 then return number else mzero
これで、「各桁の和の11倍と等しい三桁の整数」がわかります。簡単。手続き型風に書いて勝手にループ、勝手に全探査します。 実はリストの内包表記が出来れば同じことが出来ます。ただ、たとえば内包表記の使えるpythonは無名関数が不自由なのでHaskellのほうがお勧めです。

今度スクリプトをperlからhaskellに替えようかしら。ディレクトリの操作/走査が出来れば乗り換えるかもしれません。System.DirectoryとSystem.IOを見ておきましょう。
と思いましたがスクリプトを移植するにはあとヒアドキュメントとsprintfがほしいですね。わがままです。

追記:Rubyのある風景にもっと纏まった情報がありました。上のif文もguardで書いたほうがわかりやすいですね。
Haskellでゲーム1および1.5の続きです。過去ログはカテゴリ:Haskellで。

ゲーム中に現れる多種多様なオブジェクトの更新作業を、一箇所に書かないですむ方法はないか、というのが現在の問題提起です。要求仕様は以下のとおりです。
  • オブジェクトすべてを一様に扱うにはそれらが同じ型でなければいけない。型クラスでなくて完全に同じ型でないとコンテナにつめない。
  • 別々のオブジェクトごとに別々の更新関数を使いたい。そしてその関数は別のファイルで定義されていても問題なく使えるようにしたい。
これをどう扱うのか。私はlethervertさんの http://d.hatena.ne.jp/lethevert/20060618/p2 このへんの表記を思い出しました。オブジェクト指向はクロージャのようです。私のナナメ読みが適切かは知りませんが、クロージャを使えば書けるのではないかと思いました。
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。