OSDN Git Service

第13章翻訳完了.
[omake-japanese/omake_trans.git] / detail.rst
1 .. 6-detail
2
3 .. index::
4    single: osh(1)
5    single: value
6 .. _label6:
7
8 6. 表現と値
9 ==================================
10 omakeは多くのシステムとIO関数を含んでいる、フル機能のプログラミング言語です。この言語はオブジェクト指向言語であり、数値や文字列のような基本の型もすべてオブジェクトで表現されます。しかしながら、このomake言語は3つの点において、他のスクリプト言語とは異なっています。
11
12 * 動的なスコーピングを行います。
13 * IOを除いて、この言語は全体が関数的です。この言語は代入という操作が存在しません。
14 * 値の評価は通常の場合その場で行われます。これは、式が表れた瞬間に評価されるということを意味しています。
15
16 これらの機能を確かめるため、今回私たちは ``osh(1)`` omakeプログラムを使いました。 ``osh(1)`` プログラムは式を入力したら、すぐに結果が出力されるインタープリターとなっています。 ``osh(1)`` は通常シェル上で実行するために、入力された文章をコマンド文として解釈しますので、式を直接評価するためには多くの場合 ``value`` 文を使用します。 ::
17
18     osh> 1
19     *** omake error: File -: line 1, characters 0-1 command not found: 1
20     osh> value 1
21     - : "1" : Sequence
22     osh> ls -l omake
23     -rwxrwxr-x  1 jyh jyh 1662189 Aug 25 10:24 omake*
24
25 .. index::
26    single: 動的なスコーピング
27    single: CC
28    single: CFLAGS
29    single: private
30 .. _label6.1:
31
32 6.1 動的なスコーピング
33 ----------------------------------
34 動的なスコーピングは言い換えると、変数の値が実行されているスコープ中で、もっとも近くで束縛されている変数によって決定されることを意味しています。以下のプログラムについて考えて見ましょう。 ::
35
36     OPTIONS = a b c
37     f() =
38        println(OPTIONS = $(OPTIONS))
39     g() =
40        OPTIONS = d e f
41        f()
42
43 もし ``f()`` が ``OPTIONS`` 変数を再定義することなく呼び出した場合、この関数は文字列 ``OPTIONS = a b c`` が出力されます。
44
45 対照的に、関数 ``g()`` は ``OPTIONS`` 変数を再定義し、 ``f()`` をそのスコープ中で評価しますので、 この関数は文字列 ``OPTIONS = d e f`` が出力されます。
46
47 ``g`` の内容はローカルスコープを定義しています。 ``OPTIONS`` 変数の再定義は ``g`` についてローカルであり、この関数が終了した場合、この定義も終了します。 ::
48
49     osh> g()
50     OPTIONS = d e f
51     osh> f()
52     OPTIONS = a b c
53
54 動的なスコーピングはプロジェクトでのコードを簡略化するための非常に有用なツールです。例えば、 ``OMakeroot`` ファイルでは関数の集合、 ``CC`` や ``CFLAGS`` などの変数を使ったプロジェクトのビルドルールについて定義しています。しかしながら、プロジェクト中の異なったパートでは、これらの変数がそれぞれ異なった値であってほしいと思うことがあるでしょう。例えば、サブディレクトリ ``opt`` では、私たちは ``-O3`` オプションを、サブディレクトリ ``debug`` では ``-g`` オプションを用いてビルドしたいものとします。この問題は動的なスコーピングを用いて、関数を再定義することなく、プロジェクト中の一部の変数を置き換えることができます。 ::
55
56     section
57        CFLAGS = -O3
58        .SUBDIRS: opt
59     section
60        CFLAGS = -g
61        .SUBDIRS: debug
62
63 しかしながら、動的なスコーピングは欠点も持っています。はじめに、この機能は分かりずらいです。あなたはプライベートにしたい変数があるとします。しかしこれはどこか別の場所で再定義される恐れがあります。例えば、あなたは以下のサーチパスを組み立てるコードを持っていたとします。 ::
64
65    PATHSEP = :
66    make-path(dirs) =
67       return $(concat $(PATHSEP), $(dirs))
68
69    make-path(/bin /usr/bin /usr/X11R6/bin)
70    - : "/bin:/usr/bin:/usr/X11R6/bin" : String
71
72 しかしながら、プロジェクトのどこかで ``PATHSEP`` 変数がディレクトリのセパレータ ``/`` で再定義された場合、この関数は突如文字列 ``/bin//usr/bin//usr/X11R6/bin`` を返すようになります。あなたは明らかにそれを望んでいないのにです。
73
74 ``private`` ブロックはこの問題を解決するために用いられます。 ``private`` ブロック内で定義された変数は静的なスコーピングを用います。これは、変数の値がソーステキストのスコープ中で、もっとも最近の定義によって決定されることを示しています。 ::
75
76    private
77       PATHSEP = :
78    make-path(dirs) =
79       return $(concat $(PATHSEP), $(dirs))
80
81    PATHSEP = /
82    make-path(/bin /usr/bin /usr/X11R6/bin)
83    - : "/bin:/usr/bin:/usr/X11R6/bin" : String
84
85 .. _label6.2:
86
87 6.2 関数評価
88 ----------------------------------
89 IOを除いて、omakeのプログラムは全体が関数的です。これは二つの意味を持っています。
90
91 * 代入という操作が存在しません。
92 * 関数は引数を渡して、別の値を返す『値(value)』です。
93
94 二番目についてはそのままの説明です。例えば、以下のプログラムでは関数の値を返すことによって加算する関数を定義しています。 ::
95
96    incby(n) =
97       g(i) =
98          return $(add $(i), $(n))
99       return $(g)
100
101    f = $(incby 5)
102
103    value $(f 3)
104    - : 8 : Int
105
106 一番目については恐らく最初は困惑することでしょう。代入なしで、いったいどのようにして、サブプロジェクトにプロジェクトのグローバルな振る舞いを修正することができるのでしょうか?実際、この省略された説明は意図的にされています。サブプロジェクトが他のプロジェクトの邪魔をしないことを保障されているとき、ビルドスクリプトはより書きやすくなります。
107
108 しかしながら、サブプロジェクトが親のオブジェクトに情報を伝える必要がある場合や、内部のスコープが外部のスコープに情報を伝える必要がある場合が存在することも確かです。
109
110 .. index::
111    single: export
112    single: .PHONY
113 .. _label6.3:
114
115 6.3 環境のエクスポート
116 ----------------------------------
117 export文によってすべて、あるいは一部の内部スコープの情報を親スコープに伝えることができます。もし引数が存在しない場合、全体のスコープの情報が親に伝えられます。さもなければ引数の変数のみが伝えられます。もっともよく使うやり方は、条件分岐中のいくつか、あるいはすべての定義をエクスポートする場合です。以下の例では、変数 ``B`` は評価された後に、2に束縛されます。変数 ``A`` は再定義されません。 ::
118
119     if $(test)
120        A = 1
121        B = $(add $(A), 1)
122        export B
123     else
124        B = 2
125        export
126
127 もし ``export`` 文が引数なしに用いられた場合、以下のすべてが出力されます。
128
129 * 動的にスコープされたすべての値 (":ref:`label5.5`"で説明しました)
130 * 現在のワーキングディレクトリ
131 * 現在のUNIX環境
132 * 現在の暗黙のルールと暗黙の依存関係 (詳細は ":ref:`label8.11.1`" を参照してください)
133 * 現在の"phony"ターゲット宣言の集合 (詳細は ":ref:`label8.10`",":ref:`label8.11.3`" を参照してください)
134
135 .. index::
136    single: export
137 .. _label6.3.1:
138
139 6.3.1 区域のエクスポート
140 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
141 *この機能はバージョン0.9.8.5で導入されました。*
142
143 ``export`` 文はブロックの最後で実行する必要はありません。エクスポートはブロック中のブロックも、ブロックの終わりでエクスポートされます。言い換えると、 ``export`` はその文の次にくるプログラムでも用いられます。これはコード量を減らすという点で特に有用です。以下の例では、変数 ``CFLAGS`` は両方の条件分岐文からエクスポートされます。 ::
144
145     export CFLAGS
146     if $(equal $(OSTYPE), Win32)
147         CFLAGS += /DWIN32
148     else
149         CFLAGS += -UWIN32
150
151 .. index::
152    single: export
153    single: return
154 .. _label6.3.2:
155
156 6.3.2 エクスポートされた区域から値を返す
157 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
158 *この機能はバージョン0.9.8.5で導入されました。*
159
160 ブロックによって返された値は ``export`` を使用してもエクスポートされません。この値は普通に計算された場合、ブロック最後の状態の値として解釈され、エクスポートを無視します。例えば、私たちはマップの文字列をユニークな整数値に改良したテーブルを作りたいと思い、以下のプログラムを考えたとします。 ::
161
162     # 空のマップ
163     table = $(Map)
164
165     # テーブルにエントリーを追加
166     intern(s) =
167         export
168         if $(table.mem $s)
169             table.find($s)
170         else
171             private.i = $(table.length)
172             table = $(table.add $s, $i)
173             value $i
174
175     intern(foo)
176     intern(boo)
177     intern(moo)
178     # "boo = 1" と出力
179     println($"boo = $(intern boo)")
180
181 文字列 ``s`` が与えられると、関数 ``intern`` は ``s`` に既に関連付けられている値を返し、そうでない場合は新しい値を関連付けます。この場合、このテーブルは新しい値にアップデートされます。関数の初めに ``export`` をつけることによって、 ``table`` 変数はエクスポートされます。一方、 ``s`` や ``i`` に束縛されている値はプライベートなので、エクスポートされません。
182
183 omakeでの評価は先行して行われます。これは評価文に遭遇した場合、即座に式の評価が行われることを意味しています。この効果の一つとして、変数が定義されたときに、右側の変数定義が展開されることが挙げられます。 ::
184
185     osh> A = 1
186     - : "1"
187     osh> A = $(A)$(A)
188     - : "11"
189
190 二番目の定義文の右側 ``A = $(A)$(A)`` は初めに評価され、シーケンス ``11`` が生成されました。変数 ``A`` は新しい値として再定義されます。動的なスコーピングを用いて束縛した場合、これは従来の命令型プログラミングと同じ多くの特性を持ちます。 ::
191
192     osh> A = 1
193     - : "1"
194     osh> printA() =
195         println($"A = $A")
196     osh> A = $(A)$(A)
197     - : "11"
198     osh> printA()
199     11
200
201 この例では、出力関数は ``A`` のスコープ中で定義されます。最後の行でこの関数が呼び出されたとき、 ``A`` の動的な値は ``11`` であるので、この値が出力されます。
202
203 しかしながら、動的なスコーピングと命令型のプログラミングは混同すべきではありません。以下の例では違いについて示しています。二番目の ``printA`` は ``A = x$(A)$(A)x`` が定義されているスコープには存在していませんので、この関数は元の値 ``1`` を出力します。 ::
204
205     osh> A = 1
206     - : "1"
207     osh> printA() =
208         println($"A = $A")
209     osh> section
210              A = x$(A)$(A)x
211              printA()
212     x11x
213     osh> printA()
214     1
215
216 遅延評価式の使用で評価順序を制御する詳細については、":ref:`label7.5`"を参照してください。
217
218 .. index::
219    single: オブジェクト
220    single: extends
221    single: class
222    single: instanceof
223 .. _label6.4:
224
225 6.4 オブジェクト
226 ----------------------------------
227 omakeはオブジェクト指向型言語です。数や文字列のような基本的な値を含む、すべてはオブジェクトで表現されます。多くのプロジェクトの場合、通常のトップレベルのオブジェクト中でほとんどの式が評価されるため、これを見ることはあまりありませんが、 ``Pervasives`` オブジェクトと、2,3個の他のオブジェクトが最初から定義されています。
228
229 しかしながら、オブジェクトはデータを構築するための追加手段を提供し、さらにオブジェクトを慎重に使用することで、あなたのプロジェクトをより簡単にしてくれるでしょう。
230
231 オブジェクトは以下の文法で定義されます。これは ``name`` をいくつかのメソッドと値を持ったオブジェクトとして定義しています。 ::
232
233     name. =                     # += も同じくらいよく使います
234        extends parent-object    # なくても構いません
235        class class-name         # なくても構いません
236
237        # プロパティ
238        X = value
239        Y = value
240
241        # メソッド
242        f(args) =
243           body
244        g(arg) =
245           body
246
247 ``extends`` 文はこのオブジェクトが指定された ``parent-object`` に継承されていることを指定します。オブジェクトは任意の数の ``extends`` 文を含めることができます。もしおおくの ``extends`` 文が存在した場合、すべての親オブジェクトのメソッドとプロパティは継承されます。もし名前が衝突していた場合、前の定義は後の定義でオーバーライドされます。
248
249 ``class`` 文はなくても構いません。もし指定されていた場合、 ``instanceof`` 演算子を使うことでオブジェクト名を新たに定義することができるようになります。これは下で議論する ``::`` スコープ文と同様です。
250
251 オブジェクトには任意の内容のプログラムを記述できます。オブジェクトの中に定義された変数はプロパティと定義され、関数はメソッドと定義されます。
252
253 .. index::
254    single: プロパティ
255    single: メソッド
256 .. _label6.5:
257
258 6.5 プロパティとメソッドの呼び出し
259 ----------------------------------
260 オブジェクトのプロパティとメソッドは ``object.name`` 表記を用いて命名されます。例えば、一次元の点の値について定義してみましょう。 ::
261
262    Point. =
263       class Point
264
265       # 通常の値
266       x = $(int 0)
267
268       # 新しい点を生成
269       new(x) =
270          x = $(int $(x))
271          return $(this)
272
273       # ひとつ進める
274       move() =
275          x = $(add $(x), 1)
276          return $(this)
277
278    osh> p1 = $(Point.new 15)
279    osh> value $(p1.x)
280    - : 15 : Int
281
282    osh> p2 = $(p1.move)
283    osh> value $(p2.x)
284    - : 16 : Int
285
286 ``$(this)`` は常に現在のオブジェクトに置き換える変数です。式 ``$(p1.x)`` はオブジェクト ``p1`` の ``x`` の値を呼び出します。式 ``$(Point.new 1)`` は ``Point`` オブジェクトの ``new`` メソッドを呼び出す式で、初期値として15が保持された新しいオブジェクトを返します。 ``$(p1.move)`` もメソッドの呼び出しで、16が保持された新しいオブジェクトを返します。
287
288 オブジェクトは関数的であり、その場で存在しているオブジェクトのプロパティやメソッドを修正することは不可能であることに注意してください。よって、 ``new`` と ``move`` メソッドは新しいオブジェクトを返します。
289
290 .. index::
291    single: オーバーライド
292 .. _label6.6:
293
294 6.6 メソッドのオーバーライド
295 ----------------------------------
296 1つ移動させる代わりに、2つ移動させるメソッドを持った新しいオブジェクトを作ることについて考えてみましょう。 ``move`` メソッドをオーバーライドすることでこれを実現することができます。 ::
297
298    Point2. =
299       extends $(Point)
300
301       # moveメソッドをオーバーライド
302       move() =
303          x = $(add $(x), 2)
304          return $(this)
305
306    osh> p2 = $(Point2.new 15)
307    osh> p3 = $(p2.move)
308    osh> value $(p3.x)
309    - : 17 : Int
310
311 しかし、これを行うと古い ``move`` メソッドは完全に置き換わります。
312
313 .. _label6.7:
314
315 6.7 親の呼び出し
316 ----------------------------------
317 新しい ``move`` メソッドを、古い ``old`` メソッドを二回呼び出すことで定義したい場合について考えてみましょう。これは表記 ``$(classname::name <args>)`` を用いることで親を呼び出すことができます。 ``classname`` は親クラスの名前で、 ``name`` のメソッドやプロパティが関連付けられている必要があります。それでは、 ``Point2`` オブジェクトを別の方法で定義してみましょう。 ::
318
319    Point2. =
320       extends $(Point)
321
322       # 古いメソッドを2回呼び出す
323       move() =
324          this = $(Point::move)
325          return $(Point::move)
326
327 最初の ``$(Point::move)`` の呼び出しは現在のオブジェクト( ``this`` 変数)を再定義していることに注意してください。なぜならこのメソッドは新しいオブジェクトを返し、二回目の呼び出しで再利用されるからです。