以下はmmYYmmddさんが開発したVBAで関数型言語チックなことをやろうという野心的なライブラリである。
qiita.com
関数型言語を触ったことが無い方はいまいちその利点がピンと来ないかもしれないので簡単に説明しておく。
関数型言語とは、関数そのものを、文字列や整数といった普通の値と同じように扱える言語である。
これだけでは意味が分らないと思うので以下に例を挙げる。
まずVBAで適当に「計算」プロシージャを作成してみた。
Enum Ope p ' lus (足す) m 'inus (引く) t 'imes (掛ける) d 'ivide (割る) End Enum Sub Test単純VBA版() Call 計算(Ope.p, 1000, 20, 3, 9) Call 計算(Ope.m, 1000, 20, 3, 9) Call 計算(Ope.t, 1000, 20, 3, 9) Call 計算(Ope.d, 1000, 20, 3, 9) End Sub Sub 計算(Operation As Ope, ParamArray x()) Total = x(0) For i = 1 To UBound(x) Select Case Operation '※本来ループの外で判定した方が処理効率が良いが、 '今回はサンプルなのでコードの簡潔さを優先。 Case Ope.p: Total = Total + x(i) Case Ope.m: Total = Total - x(i) Case Ope.t: Total = Total * x(i) Case Ope.d: Total = Total / x(i) End Select Next Debug.Print Total End Sub
Ope列挙型を引数にとることで、どの種類の計算なのかをSelect文で判定させている。
「演算子以外は類似」という処理が複数できてしまい、あまり効率的ではない。
こうなる。
Sub Test関数型への憧れ妄想版() '実際には動きません。 Call 計算(+, 1000, 20, 3, 9) Call 計算(-, 1000, 20, 3, 9) Call 計算(*, 1000, 20, 3, 9) Call 計算(/, 1000, 20, 3, 9) End Sub Sub 計算(Ope, ParamArray x()) Total = x(0) For i = 1 To UBound(x) Total = Total Ope x(i) Next Debug.Print Total End Sub
上記のように、演算子そのものを引数として渡してしまえるため、
計算プロシージャのSelect文がいらなくなる。
残念ながらVBAではどうあがいても、ここまでスマートにはならない。
当然エラーになる。
ただし、事前に演算子に相当する関数を作成しておけば、擬似的に引数とすることが出来る。
そういうことをするためのライブラリが、上記のVBAHaskellだと思う。
これを使ってコードを書くと、以下のとおり。
Sub TestVBAHaskell版() Call 計算(p_plus, 1000, 20, 3, 9) Call 計算(p_minus, 1000, 20, 3, 9) Call 計算(p_mult, 1000, 20, 3, 9) Call 計算(p_divide, 1000, 20, 3, 9) End Sub Sub 計算(Ope As Variant, ParamArray x()) total = x(0) For i = 1 To UBound(x) total = applyFun2by2(Array(total, x(i)), Ope) Next Debug.Print total End Sub
計算プロシージャに渡しているp_plusなどはあらかじめVBAHaskellに用意されている足し算関数(への参照ポインタ)である。
演算子の代わりとなる関数ポインタを計算プロシージャにOpe引数として渡す。
※最初に作ったTest単純VBA版のOpe列挙型とは関係なく、新規に引数として定義した変数なので注意
Ope関数に第一引数totalと第二引数x(i)を渡して結果を得たいが、
残念ながら、Opeは関数ではなく、関数への参照ポインタなのでOpe(total,x(i))とは書けない。
そこで、まず引数を配列として作成し、
Array(total, x(i))
applyFun2by2を使って関数を適用してやる。
すると最初に作ったTestプロシージャと同じ結果となる。
見た目は少々複雑になっているが、こちらの方が問題解決の方法としては優秀だ。
標準のVBA命令で作成した場合、演算子の違いだけで類似の処理を複数つくら無ければならない。
サンプルではFor文中の1カ所だけなのでそれほどメリットを感じないかもしれないが、これが複数箇所にまたがると似たような処理をあちこちに作成するハメになるので、メンテナンスの面からも非効率である。
同様の経験をされた方、もっとスマートに書けないか悩んでいる方は、試してみると良いと思う。
作者のmmYYmmddさんはQiitaの他にブログでも情報発信されている。
mmyymmdd.hatenablog.com
ただし、ある程度関数型言語の基本が理解できてからでないと、解説記事やコメントを見てもサッパリ分からないと思うので、map、fold、iotaあたりは押さえておいた方が良いかもしれない。
初めて関数型言語を勉強するなら、以下の言語がオススメである。
Gauche(ゴーシュ)
- 作者: Kahuaプロジェクト,川合史朗
- 出版社/メーカー: オライリージャパン
- 発売日: 2008/03/14
- メディア: 大型本
- 購入: 22人 クリック: 713回
- この商品を含むブログ (244件) を見る
Scheme(スキーム)という言語の一種で、そのルーツはLISP(リスプ)である。
丸カッコだらけで見た目は変な言語だが、使いこなすとかなり柔軟な言語らしい。
関数型言語を学習するならまずSchemeがイチオシである。
Lispを初めその他の言語は利便性のためにいろんな機能を詰め込んでいるのに対し、Schemeはできるだけ言語をシンプルに維持するように作られているので学習に最適である。
Schemeの中でもGaucheは比較的色々機能を取り入れている方で実用的だと聞く。
ただマスターするに至らなかったので、私がGaucheで何か実用的なものが作れるようになった訳ではない。
それでも色々と勉強になることは多く、その後のプログラミングスタイルに多大な影響を与えてくれた言語だ。
これまで手続き型ばかりやっていた人にとっては、それはもう変な言語である。
まず足し算は1+2ではなく、(+ 1 2)と書く。
これはどうだろうか。
1 + 10 / 2 - 2
こうなる。
(- (+ 1 (/ 10 2)) 2)
関数型言語では、変数への「代入」とは言わず「束縛」と言うとか、一度「束縛」したら二度と値は変更できないとか、計算の実行ではなく、式の評価と言うとかで、全く手続き型とは文化が違う。
さらに、Schemeには手続き型言語の基本中の基本である、ループが存在せず、すべて再帰関数で書かれる。
再帰関数とは、自分自身を呼び出す関数である。
VBAで階乗を求めるプログラムを再帰関数で書いてみよう。
階乗とは、1からある数になるまで階段を登るみたいに1つずつ増やしながら掛けていくことで求まる数である。
例えば5の階乗は1*2*3*4*5=120である。
まずはループで書くとこうなる。
Sub test() Debug.Print Fact(5) End Sub Function Fact(n) As Integer Dim Sum As Integer Sum = 1 For i = 1 To n Sum = Sum * i Next Fact = Sum End Function
再帰を使うとこうなる。
Sub test() Debug.Print Fact(5) End Sub Function Fact(n) As Integer If n = 1 Then Fact = 1 Else Fact = n * Fact(n - 1) End If End Function
1ならそのまま返し、それ以外ならひとつnを減らしてFactを呼び出す。
Schemeはループが無いとプログラムなんて書けないという常識を壊してくれた。
何かと学びの多い言語である。
LISP(リスプ)
- 作者: M.D. ConradBarski,Conrad Barski,川合史朗
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/02/23
- メディア: 大型本
- 購入: 1人 クリック: 18回
- この商品を含むブログ (19件) を見る
それと、書籍ではないが、ノベルゲーム形式でLispを学べる以下のフリーソフトもオススメ。
lyrical.bugyo.tk
絵的にオタク趣味っぽくてオススメするのはちょっと恥ずかしいけど、内容はかなりマトモで、Lispがどんな言語なのか知りたい方はまずこちらを試してみて欲しい。
何を隠そう私が初めて関数型言語というのを知ったのもこのソフトである。
軽快な音楽とキャラの会話で楽しく学べるので、手軽に目から鱗体験をしたいならうってつけだと思う。
F#(エフシャープ)
F#はオブジェクト指向と関数型のマルチパラダイムが採用された言語だ。
Microsoftが作成した.Net Frameworkで動作する言語のひとつで、C#やVB.Netなどと連携することもできる。
- 作者: Chris Smith,頃末和義,鈴木幸敏
- 出版社/メーカー: オライリージャパン
- 発売日: 2010/08/26
- メディア: 大型本
- 購入: 4人 クリック: 47回
- この商品を含むブログ (19件) を見る
ディスプレイが大きい方は、OreilyのサイトからPDFを買えるのでそちらがオススメ。
紙の本だとどうしても、コード入力の際に画面みたり下みたりするので首が痛くなる。
書画カメラとかあると良さげだけれど、試してないのでオススメとまでは言えない。
- 出版社/メーカー: サンワサプライ
- メディア: Personal Computers
- 購入: 3人 クリック: 7回
- この商品を含むブログを見る
以下の本も高評価なので買ってみた。
- 作者: 荒井省三:いげ太
- 出版社/メーカー: 技術評論社
- 発売日: 2011/01/07
- メディア: 大型本
- 購入: 6人 クリック: 264回
- この商品を含むブログ (26件) を見る
これら以外のF#本は、肝心の関数型言語としての解説が薄いのでオススメできない。