F# Advent Calendar 2013 – connpass
http://connpass.com/event/3935/
の22日目…なのですが、既に23日深夜という…ええ、GMTで計算しても22日目に入らないですね。本来ならば、Fortranの関数や構造体をF#で使おう、というネタで書こうと思ったですが、もろもろの件が忙しくてkindleで「Programing F# 3.0」を眺めるだけで精一杯で先に進みませんでした。いやいや、言い訳よりもまずは手を動かさなくちゃね、ってことで、構想だけつらつらと1時間ほど書き連ねます。
■F#をFortranモジュールと連携する
構造解析をFortranで作っていると画面作成をどうするのか?で悩みますが、手元のものはC++で作ってあります。Fortranの関数や構造体をC言語レベルに書き直して、C++で読み込ませるという手順ですね。実は、Fortranにも.NETと連結できるソフトウェアもあるのですが、手元の Intel Fortran はそのままでは .NET とは繋がりません。これをやるならば、Fortran –> C言語 –> C++/CLI とするか、直接 C言語 –> C# ってスタイルになります。F# は関数言語といえ、.NET言語の仲間ですから、F#からC言語のライブラリを呼び出すことも可能(だと思う)でしょう。C言語のAPIレベルをひとつひとつ再定義するのは結構な手間ですし、ネイティブのデータとのコンバートに時間がかかってしまっていては、せっかくFortranを使っている意味がありません。
そこで、以前、画像解析の時に使っていた、パターンで、Fortran –> C++/CLI –> F# というコンバートのパターンを考えます。本来のF#の利用しどころはさておき、実際のコアな計算は、Fortranの中に入っているので、F#からアクセスしたいデータはもう少し「データを簡便に利用したい」というところです。
GUIのようなグラフィックの部分は、DirectXとWPFを使っています。旧来のDirectXなので、これ専用にライブラリ化されてしまってまったく手が出せない感じになっています。また、WPFのほうは、FortranからC言語下へのデータコンバートをしたのち、C#を使って描画させます。このあたり、専用グラフィック用のC++と、別途ツールのWPFとが混在していて、えらいことになっているんですが…まあ、先行きはわかりません。なかなか大変です。
現在のソフトウェア構造としては、
- データを保持しているところのFortoran
- データを高速に扱うためのFortranコード
- データをDirectXで3D描画するためのC++
- GUIを楽に作るためのC#とWPFの画面
というパターンになっています。
■関数を関数として扱うためのF#
さて、このソフトウェア構造の中でF#に何を期待するのか?といえば、「式を式のまま記述したい」という希望があります。一応、30年前ぐらいの関数言語の教科書(関数言語の発祥は、オブジェクト指向と同じころにあります)を通読したのですが、数式を数式のまま扱うという入口から私は入っています…つーか、まだ入り口の前なんですが。
たとえば、こんな式があります。私の時代の材料工学は荷重する点に対して正確な計算を行うのですが、最近の構造解析では適当なメッシュを切って膨大な計算で済ませます。適当な荷重を計算した上で、メッシュ(格子点以外に三角点のセルもあります)に対しての荷重を再計算します。
メッシュに対する荷重を計算した後は、それぞれの部材の相互の斥力を計算するわけですが、これはFotranのライブラリの中に入っています。かつてはFortranに直していたわけですが、このFortranのデータ構造と数式の書き方は、やっぱりコンピュータに沿った書き方なんですね。いわゆるベクトルを扱う訳ですが、これがforループします。30年ほど前はそれはそれでよかったのですが、そのforループにちょっと疑問があるわけです。
全てのメッシュ(あるは交点)に対して、同じ式を適用するのであれば話は簡単です。
do y=1, YMAX do x=1, XMAX … 式を書く enddo enddo
とすればOKです(実は、最近のFortranはベクトルが使えるので、もっと簡単に1行で書けます)。
ですが、メッシュを使う場合、均一に式を適用するのではなくて、必ず「境界条件」というのが入ってきます。メッシュの端っこの部分です。他の部材と接するところや、ボルトで固定されているところとか、素材が異なるところですね。この境界条件あるいは特定条件を指定するために、ループの中にif文を入れます。
do y=1, YMAX do x=1, XMAX if ( 特定の条件の時 ) then … enddo enddo
プログラミングをしているときは、これは当たり前で自然な書き方なのですが(いやいや、matchを使うと違うでしょ…って話は後ほど)、数式の場合にはこのif条件をこんな風に書きます。
y = g(x) y = 1 := {x|x=1} y = f(x) := {x|1<x,x<MAX} y = 1 := {x|x=MAX}
(ちょっと正確な書き方を忘れてしまった…工学部なのに orz)
1とMAXの時はy=1にして、1以上のときはf関数を適用する、というよくあるパターンですね。これをif文を使ってちまちま直すのがプログラミングだったわけですが、いやいや、それらの数式は数式のまま扱おうよ、という発想が関数言語の発祥です…と言い切っていいものかわからないのですが、最初の教科書を見るとそんな感じですね。
これをF#で書くとこんな感じになります。
let g x = match x with | 1 -> 1 | MAX -> 1 | _ -> f(x)
1の時が境界条件(特殊条件)で、そのほかの値の時はf関数を適用するのがわかりやすいのです。これをC言語で書くと、
if ( x == 0 ) { return 1; } else if ( x == MAX ) { return 1; } else { return f(x); }
な感じになります。「慣れ」といえば慣れなんですが、特殊条件のときと通常の条件(メインストリーム)のときの区別がわかりやすいかな、という利点があります。
実際コーディングしたときに、F#の実行スピードとFortranの実行スピードはどうなのかも問題なのですが、複雑な数式を適用させたときに、
- 直した数式が、プログラムのどれにあたるか即判断できるか?
- 直した数式が、プログラムと同じであるか即判断できるか?
ってところが関数言語の利点かなと思っています。構造の数式とか画像解析の数式を修正したときに、それがプログラムのどの部分にあたるのか?が判断しやす利点は、そのままトライ&エラーがやりやすいという利点でもあります。以前、テンプレートマッチングの式を OpneCV を使って自作していたのですが、C++ で組むよりも、この手の関数言語のほうが楽ではないか?と思っています。相関式とかさらっと書けるプログラマの方はよいのですが、境界条件も含めると結構複雑な感じになってしまうんですよね。このあたり、ゲームアルゴリズムも同じかと思っています。
■ロジックを書くのに最低限必要なF#の文法はなんだろうか?
…ってのが、私の当面の課題で、もろもろのF#の便利な機能はさておき、F# と設計について考えてみた #FsAdventJP – かたちづくり と似た感じで進めたいと思っています。
- ミニマムなデータ構造(Fortran、C言語互換)
- データ構造アクセス(C++/CLI経由?)
- 高速化計算(Fortran)
- 通常計算(F#)
- 数式の検証、試行錯誤(F#)
- GUI(C#)
なところですね、画像解析の場合は、OpenCVを使いたいので、このあたりがC++になります。また、DirectXを使ってC++/CX経由になっても、F#の使いどころはありそうです。
これを踏まえたとき、最低限必要なところといえば、
- 構造体アクセス(他アセンブリのアクセス、open)
- パターンマッチング(match?)
- リストアクセス(Listあるいは配列?)
だけで十分では?と乱暴なことを考えていたりするのですが、このあたりはもうちょっと先に実際にやってみてということで。
# タプルとか便利かな、と思っていくつか試したのですが、Fortranで扱うのが構造体のみだし。そのあたりは、うまいことC++/CLIでwrapしてF#から構造体へ直アクセスしたほうがよいかなと試行
&実験中です。
ピンバック: F# Weekly #52, 2013 – New Year Edition | Sergey Tihon's Blog