.. 6-detail .. index:: single: osh(1) single: value .. _label6: 6. 式と値 ================================== .. omake provides a full programming-language including many system and IO functions. The language is object-oriented – everything is an object, including the base values like numbers and strings. However, the omake language differs from other scripting languages in three main respects. * Scoping is dynamic. * Apart from IO, the language is entirely functional – there is no assignment operator in the language. * Evaluation is normally eager – that is, expressions are evaluated as soon as they are encountered. omakeは多くのシステムとIO関数を含んでいる、フル機能のプログラミング言語です。この言語はオブジェクト指向言語であり、数値や文字列のような基本の型もすべてオブジェクトで表現されます。しかしながら、このomake言語は3つの点において、他のスクリプト言語とは異なっています。 * 動的なスコーピングを行います。 * IOを除いて、この言語は全体が関数的です。この言語は代入という操作が存在しません。 * 値の評価は通常の場合その場で行われます。これは、式が表れた瞬間に評価されるということを意味しています。 .. To illustrate these features, we will use the osh(1) omake program shell. The osh(1) program provides a toploop, where expressions can be entered and the result printed. osh(1) normally interprets input as command text to be executed by the shell, so in many cases we will use the value form to evaluate an expression directly. これらの機能を確かめるため、今回私たちは ``osh(1)`` omakeプログラムを使いました。 ``osh(1)`` プログラムは式を入力したら、すぐに結果が出力されるインタープリターとなっています。 ``osh(1)`` は通常シェル上で実行するために、入力された文章をコマンド文として解釈しますので、式を直接評価するためには多くの場合 ``value`` 文を使用します。 :: osh> 1 *** omake error: File -: line 1, characters 0-1 command not found: 1 osh> value 1 - : "1" : Sequence osh> ls -l omake -rwxrwxr-x 1 jyh jyh 1662189 Aug 25 10:24 omake* .. index:: single: 動的なスコーピング single: CC single: CFLAGS single: private .. _label6.1: 6.1 動的なスコーピング ---------------------------------- .. Dynamic scoping means that the value of a variable is determined by the most recent binding of the variable in scope at runtime. Consider the following program. 動的なスコーピングは言い換えると、変数の値が実行されているスコープ中で、もっとも近くで束縛されている変数によって決定されることを意味しています。以下のプログラムについて考えて見ましょう。 :: OPTIONS = a b c f() = println(OPTIONS = $(OPTIONS)) g() = OPTIONS = d e f f() .. If f() is called without redefining the OPTIONS variable, the function should print the string OPTIONS = a b c. ``f()`` が ``OPTIONS`` 変数を再定義することなく呼び出した場合、この関数は文字列 ``OPTIONS = a b c`` が出力されます。 .. In contrast, the function g() redefines the OPTIONS variable and evaluates f() in that scope, which now prints the string OPTIONS = d e f. 対照的に、関数 ``g()`` は ``OPTIONS`` 変数を再定義し、 ``f()`` をそのスコープ中で評価しますので、 この関数は文字列 ``OPTIONS = d e f`` が出力されます。 .. The body of g defines a local scope – the redefinition of the OPTIONS variable is local to g and does not persist after the function terminates. ``g`` の内容はローカルスコープを定義しています。 ``OPTIONS`` 変数の再定義は ``g`` についてローカルであり、この関数が終了した場合、この定義も終了します。 :: osh> g() OPTIONS = d e f osh> f() OPTIONS = a b c .. Dynamic scoping can be tremendously helpful for simplifying the code in a project. For example, the OMakeroot file defines a set of functions and rules for building projects using such variables as CC, CFLAGS, etc. However, different parts of a project may need different values for these variables. For example, we may have a subdirectory called opt where we want to use the -03 option, and a subdirectory called debug where we want to use the -g option. Dynamic scoping allows us to redefine these variables in the parts of the project without having to redefine the functions that use them. 動的なスコーピングはプロジェクトでのコードを簡略化するための非常に有用なツールです。例えば、 ``OMakeroot`` ファイルでは関数の集合、 ``CC`` や ``CFLAGS`` などの変数を使ったプロジェクトのビルドルールについて定義しています。しかしながら、プロジェクト中の異なったパートでは、これらの変数がそれぞれ異なった値であってほしいと思うことがあるでしょう。例えば、サブディレクトリ ``opt`` では、私たちは ``-O3`` オプションを、サブディレクトリ ``debug`` では ``-g`` オプションを用いてビルドしたいものとします。この問題は動的なスコーピングを用いて、関数を再定義することなく、プロジェクト中の一部の変数を置き換えることができます。 :: section CFLAGS = -O3 .SUBDIRS: opt section CFLAGS = -g .SUBDIRS: debug .. However, dynamic scoping also has drawbacks. First, it can become confusing: you might have a variable that is intended to be private, but it is accidentally redefined elsewhere. For example, you might have the following code to construct search paths. しかしながら、動的なスコーピングは欠点も持っています。はじめに、この機能は分かりずらいです。あなたはプライベートにしたい変数があるとします。しかしこれはどこか別の場所で再定義される恐れがあります。例えば、あなたは以下の検索パスを組み立てるコードを持っていたとします。 :: PATHSEP = : make-path(dirs) = return $(concat $(PATHSEP), $(dirs)) make-path(/bin /usr/bin /usr/X11R6/bin) - : "/bin:/usr/bin:/usr/X11R6/bin" : String .. However, elsewhere in the project, the PATHSEP variable is redefined as a directory separator /, and your function suddenly returns the string /bin//usr/bin//usr/X11R6/bin, obviously not what you want. しかしながら、プロジェクトのどこかで ``PATHSEP`` 変数がディレクトリのセパレータ ``/`` で再定義された場合、この関数は突如文字列 ``/bin//usr/bin//usr/X11R6/bin`` を返すようになります。あなたは明らかにそれを望んでいないのにです。 .. The private block is used to solve this problem. Variables that are defined in a private block use static scoping – that is, the value of the variable is determined by the most recent definition in scope in the source text. ``private`` ブロックはこの問題を解決するために用いられます。 ``private`` ブロック内で定義された変数は静的なスコーピングを用います。これは、変数の値がソーステキストのスコープ中で、もっとも最近の定義によって決定されることを示しています。 :: private PATHSEP = : make-path(dirs) = return $(concat $(PATHSEP), $(dirs)) PATHSEP = / make-path(/bin /usr/bin /usr/X11R6/bin) - : "/bin:/usr/bin:/usr/X11R6/bin" : String .. _label6.2: 6.2 関数評価 ---------------------------------- .. Apart from I/O, omake programs are entirely functional. This has two parts: * There is no assignment operator. * Functions are values, and may be passed as arguments, and returned from functions just like any other value. IOを除いて、omakeのプログラムは全体が関数的です。これは二つの意味を持っています。 * 代入という操作が存在しません。 * 関数は引数を渡して、別の値を返す『値(value)』です。 .. The second item is straightforward. For example, the following program defines an increment function by returning a function value. 二番目についてはそのままの説明です。例えば、以下のプログラムでは関数の値を返すことによって加算する関数を定義しています。 :: incby(n) = g(i) = return $(add $(i), $(n)) return $(g) f = $(incby 5) value $(f 3) - : 8 : Int .. The first item may be the most confusing initially. Without assignment, how is it possible for a subproject to modify the global behavior of the project? In fact, the omission is intentional. Build scripts are much easier to write when there is a guarantee that subprojects do not interfere with one another. 一番目については恐らく最初は困惑することでしょう。代入なしで、いったいどのようにして、サブプロジェクトにプロジェクトのグローバルな振る舞いを修正することができるのでしょうか?実際、この省略された説明は意図的にされています。サブプロジェクトが他のプロジェクトの邪魔をしないことを保障されているとき、ビルドスクリプトはより書きやすくなります。 .. However, there are times when a subproject needs to propagate information back to its parent object, or when an inner scope needs to propagate information back to the outer scope. しかしながら、サブプロジェクトが親のオブジェクトに情報を伝える必要がある場合や、内部のスコープが外部のスコープに情報を伝える必要がある場合が存在することも確かです。 .. index:: single: export single: .PHONY .. _label6.3: 6.3 環境のエクスポート ---------------------------------- .. The export directive can be used to propagate all or part of an inner scope back to its parent. If used without arguments, the entire scope is propagated back to the parent; otherwise the arguments specify which part of the environment to propagate. The most common usage is to export some or all of the definitions in a conditional block. In the following example, the variable B is bound to 2 after the conditional. The A variable is not redefined. ``export`` 文によってすべて、あるいは一部の内部スコープの情報を親スコープに伝えることができます。もし引数が存在しない場合、全体のスコープの情報が親に伝えられます。さもなければ引数の変数のみが伝えられます。もっともよく使うやり方は、条件分岐中のいくつか、あるいはすべての定義をエクスポートする場合です。以下の例では、変数 ``B`` は評価された後に、2に束縛されます。変数 ``A`` は再定義されません。 :: if $(test) A = 1 B = $(add $(A), 1) export B else B = 2 export .. If the export directive is used without an argument, all of the following is exported: * The values of all the dynamically scoped variables (as described in Section 5.5). * The current working directory. * The current Unix environment. * The current implicit rules and implicit dependencies (see also Section 8.11.1). * The current set of “phony” target declarations (see Sections 8.10 and 8.11.3). ``export`` 文が引数なしに用いられた場合は、以下のすべてが出力されます。 * 動的にスコープされたすべての値 (":ref:`label5.5`"で説明しました) * 現在のワーキングディレクトリ * 現在のUNIX環境 * 現在の暗黙のルールと暗黙の依存関係 (詳細は ":ref:`label8.11.1`" を参照してください) * 現在の"phony"ターゲット宣言の集合 (詳細は ":ref:`label8.10`",":ref:`label8.11.3`" を参照してください) .. If the export directive is used with an argument, the argument expression is evaluated and the resulting value is interpreted as follows: * If the value is empty, everything is exported, as described above. * If the value represents a environment (or a partial environment) captured using the export function, then the corresponding environment or partial environment is exported. * Otherwise, the value must be a sequence of strings specifying which items are to be propagated back. The following strings have special meaning: o .RULE — implicit rules and implicit dependencies. o .PHONY — the set of “phony” target declarations. All other strings are interpreted as names of the variables that need to be propagated back. ``export`` 文が引数ありで用いられた場合は、引数の式が評価され、返される値は以下のようになります。 * もし値が空であるなら、上で説明したすべてがエクスポートされます。 * もし値が環境(environment)、あるいは部分的な環境を表現しているのなら( 詳細は ":ref:`label9.3.41`" を参照してください)、対象の環境または部分的な環境がエクスポートされます。 * そうでなければ、値は出力したい項目を指定している、文字列のシーケンスでなければなりません。また、以下の文字列は特別な意味を持ちます。 * ``.RULE`` ー 暗黙のルールと、暗黙的な依存関係 * ``.PHONY`` ー "phony"ターゲット宣言の集合 すべての他の文字列は、出力する必要のある変数名として解釈されます。 例えば以下の(わざとらしい)例では、変数 ``A`` と ``B`` がエクスポートされ、さらに暗黙のルールはこのセクションが終わった後も、環境の中で保持されます。しかし、変数 ``TMP`` とターゲット ``tmp_phony`` は変更されずに、このセクションの中にとどまります。 :: section A = 1 B = 2 TMP = $(add $(A), $(B)) .PHONY: tmp_phony tmp_phony: prepare_foo %.foo: %.bar tmp_phony compute_foo $(TMP) $< $@ export A B .RULE .. index:: single: export .. _label6.3.1: 6.3.1 区域のエクスポート ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. This feature was introduced in version 0.9.8.5. *この機能はバージョン0.9.8.5で導入されました。* .. The export directive does not need to occur at the end of a block. An export is valid from the point where it is specified to the end of the block in which it is contained. In other words, the export is used in the program that follows it. This can be especially useful for reducing the amount of code you have to write. In the following example, the variable CFLAGS is exported from the both branches of the conditional. ``export`` 文はブロックの最後で実行する必要はありません。エクスポートはブロック中のブロックも、ブロックの終わりでエクスポートされます。言い換えると、 ``export`` はその文の次にくるプログラムでも用いられます。これはコード量を減らすという点で特に有用です。以下の例では、変数 ``CFLAGS`` は両方の条件分岐文からエクスポートされます。 :: export CFLAGS if $(equal $(OSTYPE), Win32) CFLAGS += /DWIN32 else CFLAGS += -UWIN32 .. index:: single: export single: return .. _label6.3.2: 6.3.2 エクスポートされた区域から値を返す ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. This feature was introduced in version 0.9.8.5. *この機能はバージョン0.9.8.5で導入されました。* .. The use of export does not affect the value returned by a block. The value is computed as usual, as the value of the last statement in the block, ignoring the export. For example, suppose we wish to implement a table that maps strings to unique integers. Consider the following program. ブロックによって返された値は ``export`` を使用してもエクスポートされません。この値は普通に計算された場合、ブロック最後の状態の値として解釈され、エクスポートを無視します。例えば、私たちはマップの文字列をユニークな整数値に改良したテーブルを作りたいと思い、以下のプログラムを考えたとします。 :: # 空のマップ table = $(Map) # テーブルにエントリーを追加 intern(s) = export if $(table.mem $s) table.find($s) else private.i = $(table.length) table = $(table.add $s, $i) value $i intern(foo) intern(boo) intern(moo) # "boo = 1" と出力 println($"boo = $(intern boo)") .. Given a string s, the function intern returns either the value already associated with s, or assigns a new value. In the latter case, the table is updated with the new value. The export at the beginning of the function means that the variable table is to be exported. The bindings for s and i are not exported, because they are private. 文字列 ``s`` が与えられると、関数 ``intern`` は ``s`` に既に関連付けられている値を返し、そうでない場合は新しい値を関連付けます。この場合、このテーブルは新しい値にアップデートされます。関数の初めに ``export`` をつけることによって、 ``table`` 変数はエクスポートされます。一方、 ``s`` や ``i`` に束縛されている値はプライベートなので、エクスポートされません。 .. Evaluation in omake is eager. That is, expressions are evaluated as soon as they are encountered by the evaluator. One effect of this is that the right-hand-side of a variable definition is expanded when the variable is defined. omakeでの評価は先行して行われます。これは評価文に遭遇した場合、即座に式の評価が行われることを意味しています。この効果の一つとして、変数が定義されたときに、右側の変数定義が展開されることが挙げられます。 :: osh> A = 1 - : "1" osh> A = $(A)$(A) - : "11" .. In the second definition, A = $(A)$(A), the right-hand-side is evaluated first, producing the sequence 11. Then the variable A is redefined as the new value. When combined with dynamic scoping, this has many of the same properties as conventional imperative programming. 二番目の定義文の右側 ``A = $(A)$(A)`` は初めに評価され、シーケンス ``11`` が生成されました。変数 ``A`` は新しい値として再定義されます。動的なスコーピングを用いて束縛した場合、これは従来の命令型プログラミングと同じ多くの特性を持ちます。 :: osh> A = 1 - : "1" osh> printA() = println($"A = $A") osh> A = $(A)$(A) - : "11" osh> printA() 11 .. In this example, the print function is defined in the scope of A. When it is called on the last line, the dynamic value of A is 11, which is what is printed. この例では、出力関数は ``A`` のスコープ中で定義されます。最後の行でこの関数が呼び出されたとき、 ``A`` の動的な値は ``11`` であるので、この値が出力されます。 .. However, dynamic scoping and imperative programming should not be confused. The following example illustrates a difference. The second printA is not in the scope of the definition A = x$(A)$(A)x, so it prints the original value, 1. しかしながら、動的なスコーピングと命令型のプログラミングは混同すべきではありません。以下の例では違いについて示しています。二番目の ``printA`` は ``A = x$(A)$(A)x`` が定義されているスコープには存在していませんので、この関数は元の値 ``1`` を出力します。 :: osh> A = 1 - : "1" osh> printA() = println($"A = $A") osh> section A = x$(A)$(A)x printA() x11x osh> printA() 1 .. See also Section 7.5 for further ways to control the evaluation order through the use of “lazy” expressions. 遅延評価式の使用で評価順序を制御する詳細については、":ref:`label7.5`"を参照してください。 .. index:: single: オブジェクト single: extends single: class single: instanceof .. _label6.4: 6.4 オブジェクト ---------------------------------- .. omake is an object-oriented language. Everything is an object, including base values like numbers and strings. In many projects, this may not be so apparent because most evaluation occurs in the default toplevel object, the Pervasives object, and few other objects are ever defined. omakeはオブジェクト指向型言語です。数や文字列のような基本的な値を含む、すべてはオブジェクトで表現されます。多くのプロジェクトの場合、通常のトップレベルのオブジェクト中でほとんどの式が評価されるため、これを見ることはあまりありませんが、 ``Pervasives`` オブジェクトと、2,3個の他のオブジェクトが最初から定義されています。 .. However, objects provide additional means for data structuring, and in some cases judicious use of objects may simplify your project. しかしながら、オブジェクトはデータを構築するための追加手段を提供し、さらにオブジェクトを慎重に使用することで、あなたのプロジェクトをより簡単にしてくれるでしょう。 .. Objects are defined with the following syntax. This defines name to be an object with several methods an values. オブジェクトは以下の構文で定義されます。これは ``name`` をいくつかのメソッドと値を持ったオブジェクトとして定義しています。 :: name. = # += も同じくらいよく使います extends parent-object # なくても構いません class class-name # なくても構いません # フィールド X = value Y = value # メソッド f(args) = body g(arg) = body .. An extends directive specifies that this object inherits from the specified parent-object. The object may have any number of extends directives. If there is more than on extends directive, then fields and methods are inherited from all parent objects. If there are name conflicts, the later definitions override the earlier definitions. ``extends`` 文はこのオブジェクトが指定された ``parent-object`` に継承されていることを指定します。オブジェクトは任意の数の ``extends`` 文を含めることができます。もしおおくの ``extends`` 文が存在した場合、すべての親オブジェクトのメソッドとフィールドは継承されます。もし名前が衝突していた場合、前の定義は後の定義でオーバーライドされます。 .. The class directive is optional. If specified, it defines a name for the object that can be used in instanceof operations, as well as :: scoping directives discussed below. ``class`` 文はなくても構いません。もし指定されていた場合、 ``instanceof`` 演算子を使うことでオブジェクト名を新たに定義することができるようになります。これは下で議論する ``::`` スコープ文と同様です。 .. The body of the object is actually an arbitrary program. The variables defined in the body of the object become its fields, and the functions defined in the body become its methods. オブジェクトには任意の内容のプログラムを記述できます。オブジェクトの中に定義された変数はフィールドと定義され、関数はメソッドと定義されます。 .. index:: single: フィールド single: メソッド .. _label6.5: 6.5 フィールドとメソッドの呼び出し ---------------------------------- .. The fields and methods of an object are named using object.name notation. For example, let's define a one-dimensional point value. オブジェクトのフィールドとメソッドは ``object.name`` 表記を用いて命名されます。例えば、一次元の点の値について定義してみましょう。 :: Point. = class Point # 通常の値 x = $(int 0) # 新しい点を生成 new(x) = x = $(int $(x)) return $(this) # ひとつ進める move() = x = $(add $(x), 1) return $(this) osh> p1 = $(Point.new 15) osh> value $(p1.x) - : 15 : Int osh> p2 = $(p1.move) osh> value $(p2.x) - : 16 : Int .. The $(this) variable always represents the current object. The expression $(p1.x) fetches the value of the x field in the p1 object. The expression $(Point.new 15) represents a method call to the new method of the Point object, which returns a new object with 15 as its initial value. The expression $(p1.move) is also a method call, which returns a new object at position 16. ``$(this)`` は常に現在のオブジェクトに置き換える変数です。式 ``$(p1.x)`` はオブジェクト ``p1`` の ``x`` の値を呼び出します。式 ``$(Point.new 1)`` は ``Point`` オブジェクトの ``new`` メソッドを呼び出す式で、初期値として15が保持された新しいオブジェクトを返します。 ``$(p1.move)`` もメソッドの呼び出しで、16が保持された新しいオブジェクトを返します。 .. Note that objects are functional — it is not possible to modify the fields or methods of an existing object in place. Thus, the new and move methods return new objects. オブジェクトは関数的であり、その場で存在しているオブジェクトのフィールドやメソッドを修正することは不可能であることに注意してください。よって、 ``new`` と ``move`` メソッドは新しいオブジェクトを返します。 .. index:: single: オーバーライド .. _label6.6: 6.6 メソッドのオーバーライド ---------------------------------- .. Suppose we wish to create a new object that moves by 2 units, instead of just 1. We can do it by overriding the move method. 1つ移動させる代わりに、2つ移動させるメソッドを持った新しいオブジェクトを作ることについて考えてみましょう。 ``move`` メソッドをオーバーライドすることでこれを実現することができます。 :: Point2. = extends $(Point) # moveメソッドをオーバーライド move() = x = $(add $(x), 2) return $(this) osh> p2 = $(Point2.new 15) osh> p3 = $(p2.move) osh> value $(p3.x) - : 17 : Int .. However, by doing this, we have completely replaced the old move method. しかし、これを行うと古い ``move`` メソッドは完全に置き換わります。 .. _label6.7: 6.7 親の呼び出し ---------------------------------- .. Suppose we wish to define a new move method that just calls the old one twice. We can refer to the old definition of move using a super call, which uses the notation $(classname::name ). The classname should be the name of the superclass, and name the field or method to be referenced. An alternative way of defining the Point2 object is then as follows. 新しい ``move`` メソッドを、古い ``old`` メソッドを二回呼び出すことで定義したい場合について考えてみましょう。これは表記 ``$(classname::name )`` を用いることで親を呼び出すことができます。 ``classname`` は親クラスの名前で、 ``name`` のメソッドやフィールドが関連付けられている必要があります。それでは、 ``Point2`` オブジェクトを別の方法で定義してみましょう。 :: Point2. = extends $(Point) # 古いメソッドを2回呼び出す move() = this = $(Point::move) return $(Point::move) .. Note that the first call to $(Point::move) redefines the current object (the this variable). This is because the method returns a new object, which is re-used for the second call. 最初の ``$(Point::move)`` の呼び出しは現在のオブジェクト( ``this`` 変数)を再定義していることに注意してください。なぜならこのメソッドは新しいオブジェクトを返し、二回目の呼び出しで再利用されるからです。