<   2012年 07月 ( 25 )   > この月の画像一覧

System.Win32 と Graphics.Win32

Windows 版の Haskell Platform には System.Win32 と Graphics.Win32 があり、Win32 なプログラミングができる道具が揃っているらしい。

Programming Windows in Haskell というプログの記事に基本プログラムが紹介されていた。コピペをして、ghci でコンパイル失敗を繰り返しながら、全角スペースの草取りをしていったら、Hello World と書いたウィンドウを開くことができた。閉じるボタンをクリックしたらきちんとプログラムが終了した。Windows プログラマであれば、Haskell Platform で Win32 のプログラムをばりばり書ける状況が既にあるのだなという気がした。

Prelude> :l test.hs
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
Loading package bytestring-0.9.2.1 ... linking ... done.
Loading package Win32-2.2.2.0 ... linking ... done.

Win32 プログラムをほとんどしたことがないので、管理人にはレベル高すぎだったが、Haskell Platform の可能性を感じさせる記事だった。

Linux な人には、Graphics.UI.Gtk がある。Ubuntu の場合 sudo cabal innstall gtk でなかなかインストールできないが、エラーメッセージを頼りに、必要なパッケージを synaptic や apt-get で補充していったら使えるようになった。将来 Haskell Platform に採用されればそういう苦労もなくなるだろう。

サンプルプログラムと ghci での実行例を次に示す。リンクされているパッケージがないと、cabal でインストールできない。

ファイル名: test.hs

import Graphics.UI.Gtk

main :: IO ()
main = do
  initGUI
  window <- windowNew
  set window [windowDefaultWidth := 200, windowDefaultHeight := 200]
  onDestroy window mainQuit
  widgetShowAll window
  mainGUI

実行例:
Prelude> :l test.hs
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
Loading package array-0.4.0.0 ... linking ... done.
Loading package bytestring-0.9.2.1 ... linking ... done.
Loading package deepseq-1.3.0.0 ... linking ... done.
Loading package containers-0.4.2.1 ... linking ... done.
Loading package glib-0.12.2 ... linking ... done.
Loading package transformers-0.2.2.0 ... linking ... done.
Loading package mtl-2.0.1.0 ... linking ... done.
Loading package gio-0.12.2 ... linking ... done.
Loading package cairo-0.12.2 ... linking ... done.
Loading package filepath-1.3.0.0 ... linking ... done.
Loading package old-locale-1.0.0.4 ... linking ... done.
Loading package old-time-1.1.0.0 ... linking ... done.
Loading package unix-2.5.1.0 ... linking ... done.
Loading package directory-1.1.0.2 ... linking ... done.
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package process-1.1.0.1 ... linking ... done.
Loading package pango-0.12.2 ... linking ... done.
Loading package gtk-0.12.2 ... linking ... done.

なお、ubuntu には haskell-platform パッケージがあるのでそれをインストールすれば、いろいろなモジュールが一気にインストールできる。

これだけツールが整ってくると、Haskell がブレイクするのも間近なのではないかと思えてくる。
[PR]
by tnomura9 | 2012-07-30 17:11 | Haskell | Comments(0)

Haskell で OpenGL

Windows の Haskell Platform には Graphics.UI.GLUT があるので、Haskell でグラフィックスができるらしい。Graphics.UI.GLUT を使うためには、GLUT for Win32 から、glut-3.7.6-bin.zip (117 KB) をダウンロードしてきて解凍した後 glut32.dll を Windows\System32 フォルダーにコピーしておく必要がある。

次のソースを test.hs に作成して、

import Graphics.Rendering.OpenGL
import Graphics.UI.GLUT

main :: IO ()
main = do
  (progname, _) <- getArgsAndInitialize
  createWindow "Hello World"
  displayCallback $= display
  mainLoop

display :: IO ()
display = do
  clear [ ColorBuffer ]; flush

次のように ghci から動かしたら、ウィンドウが表示できた。

Prelude> :l test.hs
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
Loading package OpenGL-2.2.3.1 ... linking ... done.
Loading package array-0.4.0.0 ... linking ... done.
Loading package deepseq-1.3.0.0 ... linking ... done.
Loading package containers-0.4.2.1 ... linking ... done.
Loading package GLUT-2.1.2.1 ... linking ... done.

しかし、ウィンドウを消しても ghci へ制御が戻らなかった。Ubuntu で試したら、ghci を終了して端末のプロンプトに戻った。端末から runhaskell test.hs で実行したらちゃんと端末に制御が戻った。グラフィックスのプログラムは余り作ったことがないので、Haskell でグラフィックスができることを確認できたので満足。

HasekllWiki の OpenGLTutorial には基本的なプログラミングの記事がある。
[PR]
by tnomura9 | 2012-07-30 00:52 | Haskell | Comments(0)

Haskell の正規表現

文字列の中からパターンを見つける正規表現は、Python にも、Perlにも、Ruby にも、Java にも採用されている。したがって、正規表現について知っていれば、どの言語を使っていても同じような文字列のパターンマッチをプログラムできる。正規表現は一種の言語内言語と考えることができる。

最新版の Haskell Platform 2012.2.0.0 では、Windows 版も正規表現のモジュールが標準で装備されている。正規表現のモジュールには何種類かあるようだが、Text.Regex.Posix は C 言語の正規表現ライブラリのラッパだ。文字列はアスキーコード (ByteString) しか使えない。

Text.Regex.Posix の =~ は多形性なので、戻値の型指定を変えることで様々なデータを得ることができる。

Prelude> :m Text.Regex.Posix
Prelude Text.Regex.Posix> "hello" =~ "hello, world" :: Bool
Loading package array-0.4.0.0 ... linking ... done.
Loading package bytestring-0.9.2.1 ... linking ... done.
Loading package deepseq-1.3.0.0 ... linking ... done.
Loading package containers-0.4.2.1 ... linking ... done.
Loading package transformers-0.3.0.0 ... linking ... done.
Loading package mtl-2.1.1 ... linking ... done.
Loading package regex-base-0.93.2 ... linking ... done.
Loading package regex-posix-0.95.1 ... linking ... done.
False
Prelude Text.Regex.Posix> "hello, world" =~ "world" :: Bool
True
Prelude Text.Regex.Posix> "hello, world" =~ "[wo]" :: Int
3
Prelude Text.Regex.Posix> "hello, world" =~ "[wo]" :: String
"o"
Prelude Text.Regex.Posix> "hello, world" =~ "[wo]" :: [[String]]
[["o"],["w"],["o"]]
Prelude Text.Regex.Posix> "hello, world" =~ "[wo]." :: [[String]]
[["o,"],["wo"]]

ByteString とは C 言語で使うアスキーコードの配列だ。Haskell の String は Char のリストだが、大量の文字列を扱うのには速度的にも、メモリ消費の上からも不利だ。リスト操作を、C 言語の文字列(アスキーコードの配列)に対しても行えるようにしたモジュールが ByteString だ。ByteString の文字は Char ではなく、Word8 という8ビットのアスキーコードだ。

ByteString は Haskell 本来のデータ型ではなく C 言語などのデータとのインターフェースの働きをするもののようだ。

ByteString を扱う関数を搭載したモジュールは Data.ByteString で、Char と Word8 の変換を行う関数を記述したモジュールは、Data.ByteString.Internal だ。Data.ByteString の関数は Prelude の関数の名前と被るので、qualified import が必要だ。

Prelude> :m Data.ByteString.Internal
Prelude Data.ByteString.Internal> import qualified Data.ByteString as B
Prelude Data.ByteString.Internal B> :t c2w
c2w :: Char -> GHC.Word.Word8
Prelude Data.ByteString.Internal B> let bytesample = B.pack $ map c2w "hello"
Loading package bytestring-0.9.2.1 ... linking ... done.
Prelude Data.ByteString.Internal B> bytesample
"hello"
Prelude Data.ByteString.Internal B> :t bytesample
bytesample :: ByteString
Prelude Data.ByteString.Internal B> B.putStrLn bytesample
hello
Prelude Data.ByteString.Internal B> B.take 3 bytesample
"hel"

さしあたって、アスキー文字列の正規表現をばりばり使う用途を持っていないので、これくらいにしておく。
[PR]
by tnomura9 | 2012-07-28 06:12 | Haskell | Comments(0)

みてみて☆こっちっち

ひま

【ひま】みてみて☆こっちっちを踊ってみた

ももいろクローバーZ

ももいろクローバーZ「みてみて☆こっちっち」【ふりつけビデオ】

変身???

ももクロZが大人に!?「ペプシ ブラック」CM

いかん、いかん、完全にロリコン菌に感染してしまっている。
[PR]
by tnomura9 | 2012-07-26 02:59 | ボーカロイド | Comments(0)

ロミオとシンデレラ

ロミオとシンデレラ踊ってみた。を集めてみた。

お手本。何度でも見たくなる。
【みうめ&気まぐれプリンス】Romio & Cinderella 踊ってみた HD

フェミニンなロミシン。こずえのカリスマダンスとパン2の優美な踊りが楽しい。
【ぱん2×こずえ】ロミオとシンデレラ【踊ってみた】

白い腕がゴージャス。衣装とセットがすごい。床が滑るので苦戦しているが、本当はふたりとも踊りはすごく上手い。
【なつねこ×みかちぬ】Romeo & Cinderella ロミオとシンデレラ 踊ってみたHD

若さの爆発。ダイナミックな踊り。若いって素晴らしい。
【あすきょう】ロミオとシンデレラ【踊ってみたかった】

のらくらのかわいさに目が釘付けになる。
【のらくら×ノックソ】Romeo&Cinderella ロミオとシンデレラ【踊ってみた】HD

パーフェクトなダンス。
【orion】Romeo & Cinderella ロミオとシンデレラ 踊ってみた 【のら】
[PR]
by tnomura9 | 2012-07-23 23:09 | ボーカロイド | Comments(0)

ちょっと疲れたときに見る動画

【友だち10人で】モーニング娘。さんの『One・Two・Three』【踊ってみた】
【こずえとマリス】I ♥を踊ってみた
【ひま】Gravity=Realityを踊ってみた【200万再生記念】
【@小豆】『プラチナ』-shin'in future Mix- 【リベンジ動画】HD
【いくらミンカ】メランコリックを踊ってみた【魚民】
【みうめ】Romeo & Cinderella ロミオとシンデレラ【気まぐれプリンス】HD
男11人で モーニング娘。 - 恋愛ハンター を踊ってみた.HD
【りりりんご】Strobo Nights ストロボナイツ踊ってみた【りりりと林檎酢】
【みうめ×れいちぇる】リモコン踊ってみた
【踊ってみた】Techno Break テクノブレイク 【R.A.B・あきら様・江戸川UB】
【DANCEROID】Baby Maniacs【踊ってみた】
【ゆりにゃ】The World Warrior (rap ver.)【踊ってみた】
【AMU×LUNA】踊ってみた リモコン 【リンレンコス
【MAP】Mr. Wonderboy 歌って踊ってみた 【オリジナル振り付け】HD
【きょお☆】*ハロー、プラネット。【踊ってみた
【歯茎ピペット】Mermaid マーメイド 踊ってみた【オリジナル振付】HD
[PR]
by tnomura9 | 2012-07-23 13:05 | ボーカロイド | Comments(0)

IOモナドのループを純粋関数で作る方法

IOモナドのループを書くのはややこしいことが多い。たとえば、端末から一行読み込んでそのまま表示するというような簡単なプログラムもIOモナド内でループさせると次のようになる。(空行を入力するとループから脱出できる。)

ファイル名: test.hs
main = do
  cs <- getLine
  if cs == ""
    then
      return ()
    else do
      putStrLn cs
      main

実行例:
*Main> main
hello
hello
world
world

しかし、IOモナドで書くループを純粋関数のほうで引き受ける方法もある。

ファイル名: test2.hs
loop input = unlines $ takeWhile (/= "") $ lines input

main = do
  input <- getContents
  putStr $ loop input

こちらのほうがシンプルだし、データの加工も純粋関数の世界でできるので楽ちんだ。例えば入力した行の数値を整数にしてその総和をとりたい時は次のようなプログラムになる。

ファイル名: test3.hs

loop input = sum $ map (read :: String -> Int) $ takeWhile (/= "") $ lines input

main = do
  input <- getContents
  print $ loop input

Lazy IO の getContents を利用することで随分楽ができる。

注: getContents を利用したプログラムを ghci 上で実行すると1回目は上手くいくが、2回目からエラーが出る。

*Main> main
*** Exception: : hGetContents: illegal operation (handle is closed)

どうも getContents が処理の終了時に stdin をクローズしてしまうようだ。なかなかうまくいかないものだ。こういうときは一旦 ghci から端末へ出て runhaskell で動作させると問題ないようだ。

C:\Users\******>runhaskell test3.hs
1
2
3

6
[PR]
by tnomura9 | 2012-07-22 18:54 | Haskell | Comments(0)

ZOKUDAM

家の食卓に転がっていた、自分で買って放っておいたのか、子供が買ったのか覚えていない『ZOKUDAM』森博嗣著をふと手にとったら、一気に読み終えてしまった。

夕暮れの遊園地に現れた、サングラスと黒の皮の上下のヒロイン、ヒロインとちょっと間が抜けているがナイスガイの年下のイケメン相棒との出会い、二人の前に現れた巨大な秘密基地あたりまではお約束だった。

しかし、そのあとは、分厚いロボット操作のマニュアルを二人で読み続けるシーンが延々と続くばかりで話が一向に進まない。巨大ロボットの姿もいつまでたっても現れない。変な話だが、それが妙にロボット開発のリアリティを感じさせた。

あまりに日常的で、拍子抜けするが、その日常生活の中に、分厚いマニュアルの脅威や、かわいいペットや、企業スパイや、簡単に壊れる部品や、思ってもいなかったハプニングや、拍子抜けするその原因や、生理的に受け付けない上司や、ゆれる三十路の女性心理や、原因不明というのに何百字も使う技術者の報告書や、英語を自動翻訳した使用説明書や、日本語なのに意味不明のマニュアルや、ちょっと動かすだけで発生する不具合や、白熱する担当者会議や、ガンプラや、美少女フィギュアや、ロボワンや、熟女とゴスロリ高校生の火花を散らす舌戦や、現場とは異なる経営側の思惑などなど、好物がたくさん準備されていた。

Amazon の書評では評判は今ひとつだったが、自分的には5つ星だった。万人受けする内容ではないし、映像化も難しいと思うが、勇気あるプロデューサーがアニメ化や実写化をしてくれないだろうか。サラリーマン・ネオやアキバレンジャーが受けるくらいだから、脚本化次第ではヒットするかもしれない。
[PR]
by tnomura9 | 2012-07-22 02:55 | 話のネタ | Comments(0)

Eliza (6) applyRules

apllyRules のコードを再掲する。

applyRules q = head matchingRules
    where matchingRules = do (pats,resps) <- ruleData
                             pat <- pats
                             Just sub <- [matchSomewhere pat (" " ++ q)]
                             return $ map (doSub $ changePerspective sub) resps
          doSub sub ('_' : resp) = sub ++ resp
          doSub sub (c : resp)   = c : doSub sub resp
          doSub sub []             = []

局所定義の doSub が分かりやすそうなので先に読んでみる。doSub は再帰関数だ。1行目と3行目は初期条件 the base case で2行目が再帰的定義だ。1行目のパターンでは doSub の第2引数の文字列がアンダースコアの時に、アンダースコアを第1引き数に置き換える。2行目はアンダースコアーを一文字ずつ探索していく。3行目は第2引数が空リストになった時の処理をしている。要する第2引数の文字列中のアンダースコアを第1引き数の文字列で置き換える関数だ。

ghci で試してみた。

Prelude> let doSub sub ('_' : resp) = sub ++ resp; doSub sub (c : resp) = c : doSub sub resp; doSub sub [] = []
Prelude> doSub "SUB" "foo _ bar"
"foo SUB bar"

'_' が "SUB" で置き換えられている。こういう文字列処理は頻繁に出会うので doSub は汎用性がある。

doSub の機能が分かったので、matchingRules を解読してみる。matchingRules は do 記法で記述されているが、IO モナドのプログラムではなく、リストモナドのプログラムだ。したがって <- はリストから要素をひとつづつ取り出すという意味になる。リストなどで do 記法を使ったプログラムを次に示す。

Prelude> do x <- [1,2,3]; return (x * 2)
[2,4,6]

これは、map (*2) [1,2,3] と同じ意味になる。リストの全ての要素について処理を施すときにリストモナドを使う利点は、map 関数と同じことが複雑な処理にも使えるということだ。

<- の意味が分かったので mathcingRules を読んでみると、

(pats,resps) <- ruleData

でリストruleData からパターンのリスト pat と そのパターンに対する応答のリスト resps を取り出す。また、

pat <- pats

で、パターンのリストの要素をひとつ pat に取り出す。つぎに、

Just sub <- [matchSomewhere pat (" " ++ q)]

でコンソールから入力されたユーザの入力文字列からパターンに合致した部分 sub をとりだし、

return $ map (doSub $ changePerspective sub) resps

で、パターンに合致した部分の人称を変更した上でそれを resps のアンダースコア '_' 部分と置き換える。という動作を ruleData の要素について実行している。つまり、applyRules はユーザが入力した文字列のパターンを分析し、そのパターンに応じて人称を入れ替えたオウム返しの返答(のリスト)を作成しているということになる。

もう峠は超えたのであとは一気に行ってみる。

次の関数は conversation だ、乱数値の無限リストと端末からの入力を受け取り、ランダムに入力に対する応答を選び出す。注目しないといけないのは、convo が再帰的定義になっていることだ。それも初期条件 the base case がないので無限にループが続く。これでは、端末からコントロールDで入力を止められないのでいいのだろうかと思うが手抜きしたのだろう。

しかし、重要なのは端末からの入力を処理するようなループを、IOモナドではなく純粋関数の世界でも工夫すればプログラムすることができるということだ。

conversation rands input = unlines (convo (lines input) rands)
    where convo (line : rest) (r : randrest) =
              let answers = applyRules line in
               choice r answers : convo rest randrest
          choice r xs = xs!!(r `mod` (length xs))

最後はメインループだ。

main :: IO ()
main = do r <- getStdGen
          interact (conversation (randoms r))

とくに解読するようなものもないが、randoms は乱数の無限リストを発生させる関数だ。ghc で試すと次のようになる。

Prelude> :m System.Random
Prelude System.Random> do r <- getStdGen; return $ take 10 (randoms r)
[-1707593484,-818636185,1280678006,-604207182,40167066,1192696589,2066073470,-329758024,-1546086506,-816996862]

短いプログラムだったが、パターンマッチをスクラッチから作る方法、文字列の置換、リストモナドの使い方、IOモナドのループを純粋関数で組む方法、乱数の使い方など盛りだくさんのテクニックが織り込まれていた。

これを見ても Haskell がとっつきやすさの割に奥が見えないプログラム言語なのが分かる。テクニックを知っていれば非常に短い記述で多くの事が最小限のエラーで実行できるが、知らないと、他の人のコードを読みこなすことすらできない。それは、Haskell に限ったことではないのだろうが、Haskell を勉強しているととくにそれを感じる。

また、上のようなプログラムを読むと、欧米では既に Haskell がかなり普及した言語なのではないかという危機感を感じる。並列コンピューティングの時代に突入しつつある今 Haskell をマスターすることは個性の強いプログラム言語を趣味で使うという以上の意味があるのではないだろうか。
[PR]
by tnomura9 | 2012-07-21 19:58 | Haskell | Comments(0)

Eliza (5) applyRules

eliza.hs の4番目の関数は applyRules だ。コードは次のようになっている。

applyRules q = head matchingRules
    where matchingRules = do (pats,resps) <- ruleData
                             pat <- pats
                             Just sub <- [matchSomewhere pat (" " ++ q)]
                             return $ map (doSub $ changePerspective sub) resps
          doSub sub ('_' : resp) = sub ++ resp
          doSub sub (c : resp)   = c : doSub sub resp
          doSub sub []             = []

コードを読んでも何をやっているのかよくわからないので、eliza.hs を ghci で読み込んで apllyRules を使ってみる。コードの先頭の部分を眺めると、eliza.hs の データ部分の ruleData というリストを利用しているようなので、ruleData を調べてみる。

*Main> head ruleData
(["can you *"],["Perhaps you would like to be able to _...","You want me to _?", "You want me to be able to _?"])

リスト ruleData の要素は (パターンのリスト、パターンに対する返答のリスト)というペアになっているようだ。それで、do ブロックの先頭部分の (pats,resps) <- ruleData の意味が何となくつかめる。どうやら、applyRules は引き数の q に適合するパターンからその返答を取り出すといった働きをしているのではないかと予想してみた。それを確かめるには、ruleData の先頭のペアの fst 部分のパターンに適合する文字列を引き数 q として ruleData に与えてみればいい。

実際に "can you explain that?" に applyRules を適用してみた。

*Main> applyRules "can you explain that"
["Perhaps you would like to be able to explain that...","You want me to explain that?","You want me to be able to explain that?"]

クライアントの問に答えるべきカウンセラの応答のリストが、きちんと explain that の部分の置き換えもなされて返ってきた。もう一度 ruleData を調べてみると、返答の部分の _ が explain that に置き換えられたのが分かる。

*Main> head ruleData
(["can you *"],["Perhaps you would like to be able to _...","You want me to _?","You want me to be able to _?"])

こりゃあ優れものだ。

ruleData を眺めていたら、パタンのリストが複数の要素を含むものを見つけた。

*Main> ruleData !! 2
(["you are *"," you're *"],["What makes you think I am _?","Does it please you to believe I am _?","Perhaps you would like to be _...","Do you sometimes wish you were _?"])

パターンがどちらの値を取ってもいいのだろう。

*Main> applyRules "you are pretty"
["What makes you think I am pretty?","Does it please you to believe I am pretty?","Perhaps you would like to be pretty...","Do you sometimes wish you were pretty?"]
*Main> applyRules "you're gorgeous"
["What makes you think I am gorgeous?","Does it please you to believe I am gorgeous?","Perhaps you would like to be gorgeous...","Do you sometimes wish you were gorgeous?"]

機械が動揺しているように見えて楽しい。

いずれにせよ、applyRules はクライアントの発言に対するカウンセラの発言を選び出す、elilz.hs プログラムの中核部分であることがわかる。個々のコードの検討は長くなりそうだし、手を焼きそうなので別の記事にする。
[PR]
by tnomura9 | 2012-07-20 07:22 | Haskell | Comments(0)