Hatena::ブログ(Diary)

純粋関数型雑記帳 このページをアンテナに追加 RSSフィード Twitter

このページはHaskellを愛でるページです。
日刊形式でHaskellなどについての記事をだらだらと掲載しとります。
2004 | 07 | 08 | 09 | 10 | 11 | 12 |
2005 | 01 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 03 | 04 | 06 | 07 | 08 | 09 | 11 |
2007 | 03 | 04 | 05 | 07 | 08 | 09 | 12 |
2008 | 02 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 03 | 05 | 06 | 09 | 10 | 11 | 12 |
2010 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 12 |
2011 | 01 | 02 | 05 |
本体サイト

2004年07月15日(木) 外界とのおしゃべり

[][]Foreign function interface (FFI) Foreign function interface (FFI)を含むブックマーク Foreign function interface (FFI)のブックマークコメント

昨日適当テトリスが出来上がったけど、

まだまだHaskellネタが続くのです。

現時点であと二つほどは書きたいネタがあったりする。

Haskell上でのゲームプログラミングの方法論、

端的に言うとエレガントさの追求はまた今度。


とりあえず昨日までwxHaskellをやって、

それでゲームを作ったわけであるのだが、いくつかの点で限界を感じた。

  • 描画速度・描画機能
  • 音が出せない

普通にGUIアプリを作る分には良いんだろうけど、

(GUIのコントロール色々あるしね)

どうにもこうにもゲームには向いてないような印象だ。


そんなわけでSDLを使おうという話になった。(私の中で…)

Haskellというある意味異端なものを使いながら

結局超標準的なSDLに収まるのもなんともかんとも言えないが、

とりあえずやってみないと分からないのである。

もともと全く関数的でないライブラリをもとに

きちんと宣言的なインターフェースを用意できるのかどうか

ということにもそれなりに興味があるし。


で、まずはGoogleHaskell/SDLの存在を検索してみる。

有ったらそれ使えるので。だがまぁ、とりあえず存在しないらしい。

http://www.libsdl.org/languages.php

ここから調べるところろによると、公式にも認知されていないようである。


というわけで、FFI勉強を始めることにする。

恥ずかしながらHaskell暦は1年弱あるにもかかわらず

(積極的に使い始めたのはここ数ヶ月だけど)

FFIははじめてである。初めてなのでとんちんかんなことを

書いていても怒らないように。(こっそり教えたってください)

まぁ、wxHaskellも初めてだったんだけど。


まずは、HaskellコードをCから呼び出し。

多分使わないと思うんだけど…GHCのユーザーガイドの

FFIのとこの最初に載ってるから…。


まず、以下のようなfoo.hsを用意。

Haskell側の関数の実装。ドキュメントから引っ張ってきただけだが。

module Foo where

foreign export ccall foo :: Int -> IO Int

foo :: Int -> IO Int
foo n = return (length (f n))

f :: Int -> [Int]
f 0 = []
f n = n:(f (n-1))

foreign export のところがエクスポートする指定か。

エクスポートできるのはIOモナドだけっぽい。

まぁ、どっちでもあまり変わらないけど。


続いて呼び出し側のC。main.cというファイルにしてある。

#include <stdio.h>
#include "foo_stub.h"

int main(int argc,char *argv[])
{
  hs_init(&argc, &argv);
  printf("%d\n",foo(2004));
  hs_exit();
  return 0;
}

コンパイル方法。

ghc -cpp -fffi -o foo.exe foo.hs main.c

なんかよくわからんのだが、-fffiをつけると

FFIなコードがコンパイル出来るようである。

つけないとコンパイルとおらない。

で、FFIなコードをコンパイルすると、普段作られる

foo.hi,foo.oのほかにfoo_stub.cとfoo_stub.hが作られて、

さらにそれのコンパイルまで行われる。

なんだか至れり尽くせりな感じである。

そのスタブなヘッダをmain.cからincludeする形となっている。

それでとりあえずfoo()が呼び出せるようだが、

最初にhs_init()とhs_exit()を呼び出す必要がある。

(本当に要るのか?と思って省いてみると見事に落ちた)

>foo
2004

で、実行すると普通に動いた。というか、こんだけじゃ

ほんとに呼び出してるかわからんぞ???

もうちょっと複雑なnqueenでも動かさしてみようかの?


nqueen :: MonadPlus m => Int -> m [Int]
nqueen n = inner 0 [] where
  inner cur ls
    | n==cur = return ls
    | otherwise = msum [inner (cur+1) (n:ls) | n <- [1..n], canPut n ls]

  canPut n ls = not $ n `elem` (zip [1..] ls >>= \(d,n) -> [n,n-d,n+d])

MonadPlus版。

これ一つでリストモナドを使えば全解リスト

Maybeモナドを使えば一つの解を見つけるスグレモノである。

headとりゃいいやん、とか言っちゃ駄目。


というわけで、こんなソースをでっち上げた。

module Queen where
import Control.Monad

nqueen :: MonadPlus m => Int -> m [Int]
nqueen n = inner 0 [] where
  inner cur ls
    | n==cur = return ls
    | otherwise = msum [inner (cur+1) (n:ls) | n <- [1..n], canPut n ls]

  canPut n ls = not $ n `elem` (zip [1..] ls >>= \(d,n) -> [n,n-d,n+d])

foreign export ccall queenCnt :: Int -> IO Int

queenCnt :: Int -> IO Int
queenCnt n = return (length (nqueen n))

queenCntがエクスポートする関数で、解答の数を返す。


C側は

#include <stdio.h>
#include <stdlib.h>
#include "queen_stub.h"

int main(int argc,char *argv[])
{
  hs_init(&argc, &argv);
  printf("%d\n",queenCnt(atoi(argv[1])));
  hs_exit();
  return 0;
}

適当にこんな風に変えた。


コンパイル&実行。

>queen 8
92

>queen 10
724

よしよし、これで満足である。(満足なのか…)