最近の更新 (Recent Changes)

2014-01-01
2013-01-04
2012-12-22
2012-12-15
2012-12-09

Wikiガイド(Guide)

サイドバー (Side Bar)


← 前のページに戻る

8. 六式言語: クロージャと高階関数をサポートするプログラミング言語

8.1 クロージャと高階関数をサポートするプログラミング言語とは

さて、次はクロージャと高階関数を組み合わせてみましょう。

クロージャと高階関数についても組み合わせて考えることは簡単です。

クロージャは引数以外の変数を実行時の環境ではなく、自身が定義された環境(静的スコープ)において解決することを特徴とするでした。 つまり関数内のローカル変数の状態が保存されました。

このクロージャと高階関数を組み合わせると、関数内のローカル変数を操作する関数をその関数の外の関数型の変数から呼び出せるようになります。

次の例を見てください。


// 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関数の外からも関数として呼び出されるようになりました。

このようにクロージャと高階関数と組み合わせるとあたかも関数をオブジェクト指向のオブジェクトのように扱うことができるようになるのです。

8.2 六式言語の改造点

8.2.1 いかに実現するか?

六式言語も五式言語と同様で、ベースに四式言語である高階関数をサポートするプログラミング言語を使いましょう。これは手続き言語に高階関数を実現したものでした。

この四式言語に、クロージャをサポートする言語である三式言語の改造点を加えます。


- C言語への出力のローカル変数の定義にstaticを付ける

これにより、関数内のローカル変数が関数の実行終了後も値を保持し続けます。

さらにこの修正に加えてこれはオマケですが、六式言語では配列の定義もできるようにしましょう。

定義はこんな感じで配列の大きさを指定できるようにします。


var a:int[10], b:int[100], c:int;

変数名の後に型を指定してから,で大きさを指定します。

使うときには[, ]で位置を指定します。


  x = a[3] - a[7];
  y = b[78]*2+1;


8.3 六式言語のソース


// 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.4 六式言語のソースのコンパイル方法

それでは、六式言語用のサンプルプログラムを作成してコンパイルしてみましょう。

まず、「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