descartes-src (ソースパッケージ descartes-src-0.26.0.tar.gz) | 2012-09-09 20:57 |
descartes-win (Windows用バイナリパッケージ descartes-win-0.26.0.zip) | 2012-09-09 20:52 |
会話キャラクター: ツンデレ アプリケーション (会話キャラ:ツンデレ v1.0 for Windows) | 2010-04-29 13:41 |
会話キャラクター: 2人の女の子 ダブルキャラクター (会話キャラクター 2人の女の子 ダブルキャラクター 1.0 for Windows) | 2011-10-02 22:23 |
会話キャラクター: Eliza風英語版 (会話キャラ:Eliza風英語版 v1.0 for Windows) | 2010-05-11 01:06 |
会話キャラクター: 猫耳メイド アプリケーション (会話キャラ:猫耳メイド v1.0 for Windows) | 2010-04-27 21:15 |
会話キャラクター: イライザ風日本語版 (会話キャラ:イライザ風日本語版 v1.0 for Windows) | 2010-04-30 21:53 |
経済指標表示プログラム for Windows (経済指標表示プログラム V1.0) | 2011-08-18 22:04 |
ニュースヘッドライン表示プログラム (ニュースヘッドライン表示プログラム V1.0 for Windows) | 2011-08-16 12:31 |
デカルト言語 example (デカルト言語の例題 example-0.7.0.zip) | 2009-03-01 19:47 |
電力状況表示プログラム for Windows (2011年夏版 全国電力供給状況表示プログラム V1.0) | 2011-08-15 13:25 |
さて、次はクロージャと高階関数を組み合わせてみましょう。
クロージャと高階関数についても組み合わせて考えることは簡単です。
クロージャは引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とするでした。 つまり関数内のローカル変数の状態が保存されました。
このクロージャと高階関数を組み合わせると、関数内のローカル変数を操作する関数をその関数の外の関数型の変数から呼び出せるようになります。
次の例を見てください。
// sample program for enhanced PL/0 var INC:func, DEC:func, DOUBLE:func, PR:func; int: function counter(); begin var n:int; int: function Inc(); begin n = n + 1; print n; end int: function Dec(); begin n = n - 1; print n; end int: function Double(); begin n = n * 2; print n; end int: function Pr(); begin print n; end n = 0; INC = Inc; DEC = Dec; DOUBLE = Double; PR = Pr; end begin counter(); PR(); INC(); INC(); INC(); INC(); INC(); INC(); INC(); DEC(); DEC(); DEC(); INC(); INC(); DOUBLE(); PR(); end
関数counterの中で、変数nとInc, Dec, Double, Pr関数をローカル関数として定義しています。 そして、変数nは0に、外部の変数INCに関数Inc, 変数DECに関数Dec、変数DOUBLEに関数Double、および変数PRに関数Prを設定しています。
これにより、変数nは関数counter内のクロージャ変数となります。
変数INC、DEC、DOUBLE、PRには、関数counter内のローカル定義関数が高階関数として設定され、counter関数の外からも関数として呼び出されるようになりました。
このようにクロージャと高階関数と組み合わせるとあたかも関数をオブジェクト指向のオブジェクトのように扱うことができるようになるのです。
六式言語も五式言語と同様で、ベースに四式言語である高階関数をサポートするプログラミング言語を使いましょう。これは手続き言語に高階関数を実現したものでした。
この四式言語に、クロージャをサポートする言語である三式言語の改造点を加えます。
- C言語への出力のローカル変数の定義にstaticを付ける
これにより、関数内のローカル変数が関数の実行終了後も値を保持し続けます。
さらにこの修正に加えてこれはオマケですが、六式言語では配列の定義もできるようにしましょう。
定義はこんな感じで配列の大きさを指定できるようにします。
var a:int[10], b:int[100], c:int;
変数名の後に型を指定してから,で大きさを指定します。
使うときには[, ]で位置を指定します。
x = a[3] - a[7]; y = b[78]*2+1;
// Enhanced PL/0 /* * Support of closure * Support of array * */ <program> <setVar Line 1> <print "#include <stdio.h>"> <print "#include <stdlib.h>"> <print "typedef int (*func)();"> <print> <print "int main() "> <print "{"> {<Comment>} <block> <print "}"> {<Comment>} ; <block> [ "const" <emsg "constant name."> <printf "#define "> <ident> <emsg "constant definition."> "=" <printf " ("> <number> <print ")"> {"," <printf "#define "> <ident> <emsg "constant definition."> "=" <printf " ("> <number> <print ")"> } <emsg "';' is missing."> ";" ] {<Comment>} [ "var" <emsg "variable name."> <printf "static "> <ident #var1> ":" <ident #type1> <emsg "variable definition."> <printf #type1 " " #var1> [ "[" <printf "["> <expression> "]" <printf "]"> ] <print ";"> {"," <printf "static "> <ident #var2> ":" <ident #type2> <emsg "variable definition."> <printf #type2 " " #var2> [ "[" <printf "["> <expression> "]" <printf "]"> ] <print ";"> } ";" ] {<Comment>} { <ident #typef> ":" "function" <emsg "function name."> <printf #typef " "> <ident> <emsg "function definition."> "(" <printf "("> [ <ident #var3> ":" <ident #type3> <printf #type3 " " #var3> { "," <printf ", "> <ident #var4> ":" <ident #type4> <printf #type4 " " #var4> } ] ")" <print ")"> ";" <print "{"> <block> <print "}"> } {<Comment>} <statement> ; <statement> <SKIPSPACE> ::sys <line #Line> <setVar Line #Line> {<Comment>} ( <ident #var> "[" <emsg "expression."> <printf #var "["> <expression> "]" <printf "]"> "=" <emsg "expression."> <printf " = "> <expression> <print ";"> ";" <emsg "syntax error."> | <ident #var> "=" <emsg "expression."> <printf #var " = "> <expression> <print ";"> ";" <emsg "syntax error."> | <ident #typef> ":" "function" <emsg "function name."> <printf #typef " "> <ident> <emsg "function definition."> "(" <printf "("> [ <ident #var3> ":" <ident #type3> <printf #type3 " " #var3> { "," <printf ", "> <ident #var4> ":" <ident #type4> <printf #type4 " " #var4> } ] ")" <print ")"> ";" <print "{"> <block> <print "}"> | <ident #func> <emsg "syntax error."> "(" <emsg "function call."> <printf #func "("> [<expression> {"," <expression>}] ")" <printf ")"> <print ";"> ";" <emsg "syntax error."> | "return" <printf "return "> <expression> <print ";"> ";" <emsg "syntax error."> | "var" <emsg "variable name."> <printf "static "> <ident #var1> <emsg "variable definition."> ":" <ident #type1> <printf #type1 " " #var1> [ "[" <printf "["> <expression> "]" <printf "]"> ] <print ";"> {"," <printf "static "> <ident #var2> <emsg "variable definition."> ":" <ident #type2> <printf #type2 " " #var2> [ "[" <printf "["> <expression> "]" <printf "]"> ] <print ";"> } ";" | "begin" <print "{"> { <statement> } "end" <print "}"> | "if" <emsg "if sentence."> <printf "if ("> <condition> "then" <print ") {"> <statement> ["else" <print "} else {"> <statement> ] <print "}"> | "while" <emsg "while sentence."> <printf "while ("> <condition> "do" <print ") {"> <statement> <print "}"> | "for" <emsg "for sentence."> <printf "for ("> <ident> "=" <printf " = "> <expression> ";" <printf ";"> <condition> ";" <printf ";"> <ident> "=" <printf " = "> <expression> "do" <printf ") {"> <statement> <print "}"> | "print" <emsg "print sentence. "> [ ( <strings #str> <printf 'printf("%s ", "'#str'" );'><print> | <printf 'printf("%d ", '> <expression> <print ');'> ) { "," ( <strings #str2> <printf 'printf("%s ", "'#str2'" );'><print> | <printf 'printf("%d ", '> <expression> <print ');'> ) } ] <print 'printf("\n");'> ";" <emsg "syntax error."> ) ; <condition> "odd" <emsg "odd syntax."> <printf "(("> <expression> <printf ") & 1)"> | <expression> ("=" <printf " == "> |"#" <printf " != "> |"<=" <printf " <= "> |"<" <printf " < "> |">=" <printf " >= "> |">" <printf " > "> ) <expression> ; <expression> [ "+" <printf "+"> |"-" <printf "-"> ] <term> { ("+" <printf "+"> |"-" <printf "-"> ) <term>}; <term> <factor> { ("*" <printf "*"> |"/" <printf "/"> ) <factor>}; <factor> "$" <printf "(*"> <ident #f> "(" <printf #f ")("> [ <expression> {"," <printf ", "> <expression>}] ")" <printf ")"> |<ident #f> "(" <printf #f "("> [ <expression> {"," <printf ", "> <expression>}] ")" <printf ")"> |<ident #f> "[" <printf #f "["> <expression> "]" <printf "]"> | <ident> | <number> | "(" <printf "("> <expression> ")" <printf ")"> ; <ident #id> <ID #id> <reserved #id> ; <ident> <ident #id> <printf #id> ; <number #sign #n> ["+" <is #sign ""> | "-" <is #sign "-"> | <is #sign ""> ] <NUM #n> ; <number> <number #sign #n> <printf #sign #n> ; <strings #str> <STRINGS #str> ; <strings> <strings #str> <printf #str> ; <Comment> "//" <SKIPCR> ; <reserved #id> <not ::sys <member #id (var begin end if then while for do function return print odd)>> ; <emsg #x> <x <getVar #Line Line> <warn "error : " #Line ":" #x> <exit>> ; <emsg2 #x> <getVar #Line Line> <warn "error : " #Line ":" #x> <exit> ; <compile> ::sys<args #x> ::sys<nth #inputfile #x 1> ::sys<suffix #outputfile #inputfile c> <print inputfile #inputfile> <print outputfile #outputfile> ::sys<openw #outputfile ::sys<openr #inputfile <program>>> ; ? <compile>;
それでは、六式言語用のサンプルプログラムを作成してコンパイルしてみましょう。
まず、「8.3 六式言語のソース」を pl06.dec と名前をつけて保存しておいてください。
次のサンプルプログラムのソースを使います。
// sample program for enhanced PL/0 var INC:func, DEC:func, DOUBLE:func, PR:func; int: function counter(); begin var n:int; int: function Inc(); begin n = n + 1; print n; end int: function Dec(); begin n = n - 1; print n; end int: function Double(); begin n = n * 2; print n; end int: function Pr(); begin print n; end n = 0; INC = Inc; DEC = Dec; DOUBLE = Double; PR = Pr; end begin counter(); PR(); INC(); INC(); INC(); INC(); INC(); INC(); INC(); DEC(); DEC(); DEC(); INC(); INC(); DOUBLE(); PR(); end
この章の最初の項で示した関数オブジェクトのサンプル・プログラムです。
関数counterが呼ばれると内部のローカル変数nが0に初期化され、INC, DEC, DOUBLE, PRのそれぞれのローカル変数にcounter関数内で定義した関数を設定します。
counter関数内の変数nが、外部からの関数呼び出しで操作されるかどうかを試します。
このサンプルプログラムをc.pl0という名前で保存してコンパイルします。
$ descartes pl06.dec c.pl0 inputfile c.pl0 outputfile c.c result -- <compile> -- true
c.c にコンパイル結果が出力されました。
インデントを直してc.cを見ると次のようになります。
#include <stdio.h> #include <stdlib.h> typedef int (*func) (); int main() { static func INC; static func DEC; static func DOUBLE; static func PR; int counter() { { static int n; int Inc() { { n = n + 1; printf("%d ", n); printf("\n"); } } int Dec() { { n = n - 1; printf("%d ", n); printf("\n"); } } int Double() { { n = n * 2; printf("%d ", n); printf("\n"); } } int Pr() { { printf("%d ", n); printf("\n"); } } n = 0; INC = Inc; DEC = Dec; DOUBLE = Double; PR = Pr; } } { counter(); PR(); INC(); INC(); INC(); INC(); INC(); INC(); INC(); DEC(); DEC(); DEC(); INC(); INC(); DOUBLE(); PR(); } }
関数の引数や変数の定義にconstが定義されています。
また、func型が定義されていて、calc関数の引数で使われています。
それではgccでコンパイルして実行します。
$ gcc c.c -o a.out $ ./a.out 0 1 2 3 4 5 6 7 6 5 4 5 6 12 12
関数counter内の変数nが変更されていますね。
次に配列を使った例を試してみましょう。
const N = 10; var ary1:int[N], ary2:int[N*2], i:int; int:function a(i:int); begin return ary1[i]; end int:function b(i:int); begin return ary2[i]; end begin for i = 0; i < N; i = i+1 do begin ary1[i] = i; ary2[i] = N - i; ary2[i*2] = i; end print a(7), b(3), b(2*7); end
配列としてary1, ary2を使います。
このサンプルプログラムをr.pl0という名前で保存してコンパイルします。
コンパイルしてインデントを直すと次のように変換されます。
#include <stdio.h> #include <stdlib.h> typedef int (*func) (); int main() { #define N (10) static int ary1[N]; static int ary2[N * 2]; static int i; int a(int i) { { return ary1[i]; } } int b(int i) { { return ary2[i]; } } { for (i = 0; i < N; i = i + 1) { { ary1[i] = i; ary2[i] = N - i; ary2[i * 2] = i; } } printf("%d ", a(7)); printf("%d ", b(3)); printf("%d ", b(2 * 7)); printf("\n"); } }
実行するとこんなです。
$ gcc r.c -o a.out $ ./a.out 7 7 7
[PageInfo]
LastUpdate: 2013-01-04 22:25:48, ModifiedBy: hniwa
[License]
Creative Commons 2.1 Attribution
[Permissions]
view:all, edit:login users, delete/config:login users