2. 言語仕様
Euphoiraでは、すべてのデータオブジェクトは、アトム(単体の数値)またはシーケンス(数値の集合や文字列)になります。 アトムやシーケンスを組み合わせて構成されたシーケンスはオブジェクトに該当します。シーケンスはオブジェクトの集合をブレースブラケット、カンマで区切って記述できます。アトムは、任意の整数や倍精度浮動小数点数値を記述できます。扱える正確な数値の範囲は、ほぼ-1e300から +1e300(-1から10までの倍数の300乗)です。10進数では15桁の精度となります。なお、これらはEuphoriaのオブジェクトです: -- アトムの記述例: 0 1000 98.6 -1e6 -- シーケンスの記述例: {2, 3, 5, 7, 11, 13, 17, 19} {1, 2, {3, 3, 3}, 4, {5, {6}}} {{"jon", "smith"}, 52389, 97.25} {} -- 空要素(0個)のシーケンス 16進数はこのように記述できます。例えば: #FE -- 254 #A000 -- 40960 #FFFF00008 -- 68718428168 -#10 -- -16 A, B, C, D, E, F といった大文字の場合のみ16進数として扱われます。16進数は常に正数として扱われます。ただし、# 文字の前にマイナス符号を付けると負数として扱われます。なお、実例として#FFFFFFFF (4294967295)は非常に大きい正数であり、*-1ではありません。* そう誤解する機械語プログラマもいると思います。しかし違います。 さて、シーケンスは何層にも渡って入れ子できます。すなわち、シーケンスにさらにシーケンスを入れ子にして、さらにさらにシーケンスを何層にも渡って入れ子にできます(メモリー不足になるまで)。ブレースブラケットは式のリストで構成されたシーケンスを定義するのに使用されます。これらの式は実行時に定数または評価済みの式となります。すなわち、 {x+6, 9, y*w+2, sin(0.5)} Euphoriaの頭字語の一部である"Hierarchical Objects(階層型オブジェクト)" は、この特徴的な階層構造を持つ入れ子型のシーケンスに由来します。しかし、これを特定のオブジェクト指向言語のクラス階層と混同すべきではありません。 なぜ、数値のことをアトム(原子)と呼ぶのですか? そして"数値"では好ましくない理由は? さて、たしかにアトムは数値です。しかし、アトムの語源であるギリシャ語のAtomon(これ以上分割できない)は、その特徴を強く現していると感じます。無論、物理学の世界では何十年も前にアトム(原子)をさらに小さく分割できると解明されてはいるものの、Euphoriaではアトム(数値)はそれ以上分割することはできません。それらは基本構成要素であり、すべてのデータはEuphoriaのプログラムから操作できるからです。この例えは他の原子や分子を元としてシーケンスを分子として構成するものと考えられるでしょう。わかりやすく例えると、シーケンスはディレクトリようなもの、アトムはファイルのようなものです。ちょうど、あなたのコンピューター上のフォルダとファイル、その他のフォルダの両方を格納できるように、シーケンスはアトムとその他のシーケンスの両方を含めることは可能です(およびそれらのシーケンスにアトムやシーケンスその他のものを含めることも可能です)。 さて、Euphoriaのシーケンスは非常にシンプルかつ非常に強力であるという理由をご理解いただけたかと思います。そして、アトムとシーケンスを理解することはEuphoriaを理解する鍵なのです。
2.1.2 文字と文字列 文字列は文字の集合で構成されたシーケンスです。記述する場合はクォートで括ります。すなわち、 "ABCDEFG" 文字列は他のシーケンスと同様に操作や制御できます。たとえば上述の文字列は、これら数値の集合で構成されたシーケンスと完全に等価です: {65, 66, 67, 68, 69, 70, 71}これらには、数値と対応するASCIIコードを含みます。Euphoriaコンパイラは上述の数値を含むシーケンスを"ABCDEFG"に変換します。ある意味では、Euphoriaにおいて数値の集合で構成された"文字列"だけがシーケンスではありません。実際には文字列をクォートで括ると、いちいちASCIIコードを全て入力する必要はないので記述しやすくなります。 続いて、""は{}と等価です。両方とも長さ0の空のシーケンスとして扱われます。この件に関してのプログラミング作法としては長さ0の文字を含むシーケンスに対しては""を使用し、その他の種類のシーケンスに対しては{}を使用すると自然です。 文字はアトムでもあります。文字はシングルクォートで括る必要があります。また、文字(アトム)と長さ1の文字列(シーケンス)には違いがあります。すなわち、 'B' -- アトムの66と等価 - ASCIIコードでは半角英数大文字のB "B" -- シーケンスの{66}と等価 もう一度言います。'B'は注釈のとおりシーケンスに66と記述したものと等価です。しかし、"文字"は厳密には数値(アトム)ではありません。 覚えておいて欲しいのはアトムは単要素のシーケンスと同じ値を持ちますが等価ではありません。同様に、いくつかの組み込みルーチンにおいては使い分けを必要とします。 特殊記号の記述にバックスラッシュを必要とする場合があります: \n 改行 \r キャリッジリターン \t タブ \\ バックスラッシュ \" ダブルクォート \' シングルクォート たとえば、"こんにちわ、世界\n"または'\\'が該当します。Euphoria標準エディタ(ed)では文字列は緑色で表示されます。 コメントはダッシュ(2本のハイフン・マイナス記号)から行末までコメントとみなされます。すなわち、 -- これはコメントです。 コメントはコンパイラによって無視され、実行速度には影響はありません。 Euphoriaエディタ(ed)ではコメントは赤色で表示されます。 プログラムの最初の一行目(に限ります)の始まりとして#!という特殊コメント(Shebang 構文)を使用できます。すなわち、 #!/home/rob/euphoria/bin/exuこれはLinuxシェルにEuphoriaインタプリタのフルパスを与えることによりファイルを実行できるようにするためです。この記述のファイル作成して実行するとファイル名を入力するだけで実行され、"exu"の入力を省略できます。 DOSとWindowsではこの記述はコメントとして扱われます(Windows版のApacheウェブサーバーでは構文として認識されます)。なお、shroudで中間コードへ変換したファイル(拡張子 : .li)を実行する場合はexuの代わりにbackenduを使用してください。
2.2 式 他のプログラミング言語と同様に、Euphoriaでは式を記述することにより演算を行い結果を得られます。さらにEuphoriaでは、ひとつの式でシーケンス内のデータ対して一度に一括で演算を施すことは可能です。これと同じことを従来の言語(大半の言語実装では)で行おうとすると、配列やリストといったデータの集合全体に演算を行いたい場合はループを形成して一つずつ要素を取り出しつつ演算を施す必要があります。また、Euphoriaではシーケンスにたくさんの単数を含めて一つに扱えます。それは複製したり、サブルーチンの引数や結果として返したり、または一つの演算結果として扱えます。すなわち、 {1,2,3} + 5この式はの例ではシーケンスの {1,2,3} にアトムの 5 を加算すると、演算結果としてシーケンスの {6,7,8} を返します。 なお、この件に関しての豊富な例題は後述します。 関係演算子 < > <= >= = != は、 1 (真) または 0 (偽) いずれかの演算結果を返します。 8.8 < 8.7 -- 未満 : 8.8 は 8.7 より小さいか(偽) -4.4 > -4.3 -- 超過 : -4.4 は-4.3 より大きいか(偽) 8 <= 7 -- 以下 : 8 は 7 より小さいかあるいは等しいか(偽) 4 >= 4 -- 以上 : 4 は 4 より大きいかあるいは等しいか(真) 1 = 10 -- 等価 : 1 は 10 と等しいか (偽) 8.7 != 8.8 -- 非等価 : 8.7 は 8.8 と等しくはないか(真) なお、関係演算子はシーケンスにも適用することができます。 論理演算子はand, or, xor および notがあり、式の結果が"真"であるか決定します。すなわち、 1 and 1 -- 1 (真) 1 and 0 -- 0 (偽) 0 and 1 -- 0 (偽) 0 and 0 -- 0 (偽) 1 or 1 -- 1 (真) 1 or 0 -- 1 (真) 0 or 1 -- 1 (真) 0 or 0 -- 0 (偽) 1 xor 1 -- 0 (偽) 1 xor 0 -- 1 (真) 0 xor 1 -- 1 (真) 0 xor 0 -- 0 (偽) not 1 -- 0 (偽) not 0 -- 1 (真) これらの演算子は 1 または 0 以外の数値にも適用できます。この規則は : 0 は偽を、1は真を意味します。たとえば: 5 and -4 -- 1 (真) not 6 -- 0 (偽) なお、これらの演算子はシーケンスにも適用することができます。詳しくは下記を参照してください。 いつくかの場合では短絡評価式においてandまたはorを使用します。 「加算、減算、乗算、除算、単項マイナス、 単項プラス」といった一般的な算術演算子があります。 3.5 + 3 -- 加算 - 結果 : 6.5 3 - 5 -- 減算 - 結果 : -2 6 * 2 -- 乗算 - 結果 : 12 7 / 2 -- 除算 - 結果 : 3.5 -8.1 -- 単項マイナス - 結果 : -8.1 +8 -- 単項プラス - 結果 : +8 計算結果が非常に大きい場合(たとえば-1e300 to +1e300の範囲を超えた場合)は+infinityまたは-infinityといった特別な値を持つアトムを返します。これらはprint関数を実行した場合infまたは-infとして出力されます。さらにnanまたは-nanの発生する場合があります。"nan"は"非数(not a number)"を意味します。たとえば未定義値がそれに該当します(infをinfで除算した場合)。これらはIEEE浮動点小数規格(IEEE floating-point standard)で定義されています。もし、これらの特殊な値が次々と出力される場合は通例として、あなたが書いたプログラムのロジックにエラーがあることを示唆しています。その場合でも中間結果としてinfを出力します。ただし、いくつかの場合において許容できるかもしれません。たとえば、1/infは0で、もしかすると、あなたのアルゴリズムでは "はい(right)"という結果が0で良い場合があります。 0除算は負数の平方根や負数(非正な)対数など不正な引数を算術ライブラリルーチンに与えると発生します。0除算が発生するとエラーメッセージを出力してプログラムを強制終了します。 なお、単項プラスを使用すべき唯一の理由は、あなたが書いたプログラムを読む人が正数であることをはっきり認識できるためです。実際のところ、これに関してはインタプリタは一切の計算も行いません。 すべての関係演算子、論理演算子と算術演算子は既に解説済みではあるものの、さらに算術ルーチンは第二部 - ライブラリルーチンに掲載されており、これらは単数(アトム)とシーケンスに使用できます。 単項演算子(ひとつのオペランド)はシーケンスの各要素に適用され、処理結果として同一の長さのシーケンスを返します。もし、これら要素の一つでもシーケンス自身ならば再度同じ規則が再帰的に適用されます。すなわち、 x = -{1, 2, 3, {4, 5}} -- x の結果は {-1, -2, -3, {-4, -5}} もし、二項演算子(ふたつのオペランド)が 両方のシーケンスのオペランド(被演算子)を持つならば、ふたつのシーケンスの長さは同一でなければならないです。二項演算子は、ふたつのシーケンスから対応する要素が(演算対象として)適用され、結果としてシーケンスを返します。すなわち、 x = {5, 6, 7, 8} + {10, 10, 20, 100} -- x の結果は {15, 16, 27, 108} もし、二項演算子がひとつのオペランドを持ち、 いずれかのシーケンスが単数 (アトム)その他である間ならば、単数はシーケンスオペランドと同一の長さのシーケンスを生成するために効率的に繰り返されます。この演算規則はふたつのシーケンスに適用されます。いくつかの例として: y = {4, 5, 6} w = 5 * y -- w の結果は {20, 25, 30} x = {1, 2, 3} z = x + y -- z の結果は {5, 7, 9} z = x < y -- z の結果は {1, 1, 1} w = {{1, 2}, {3, 4}, {5}} w = w * y -- w の結果は {{4, 8}, {15, 20}, {30}} w = {1, 0, 0, 1} and {1, 1, 1, 0} -- {1, 0, 0, 0} w = not {1, 5, -2, 0, 0} -- w の結果は {0, 0, 0, 1, 1} w = {1, 2, 3} = {1, 2, 4} -- w の結果は {1, 1, 0} -- 注意事項として最初の '=' は判定結果の代入で -- 次の '=' は 関係演算子で値が等価であるか -- 判定します。注意: 二つの文字列を比較したい場合(または他のシーケンス)は、'=' 演算子は使用できません(Euphoria以外のプログラミング言語の一部では使用できます): if "APPLE" = "ORANGE" then -- エラー!=' は '+', '*' などの演算子と同様に処理され、それに対応するシーケンスの要素が適用され、さもなくばシーケンスは同じ長さである必要があります。等しい長さの場合、結果は1や0のシーケンスです。等しくない長さの場合は、結果はエラーになります。if条件文以降はシーケンスではなくアトムである必要があり、そうでなければエラーになります。よって、代わりにequal()組み込みルーチンを使用してください。 if equal("APPLE", "ORANGE") then -- 正しい一般に、比較にはcompare()組み込みにルーチンと関係演算子が使用できます。: if compare("APPLE", "ORANGE") = 0 then -- 正しい同様に、他の比較方法としてcompare()が使用できます。: if compare("APPLE", "ORANGE") < 0 then -- 正しい -- もし、 "APPLE" は "ORANGE" より小さいならば (真) シーケンスに角括弧の中に要素の番号を与えることにより要素のひとつを選択できます。 要素の番号は1から開始されます。非整数の添字は整数まで切り下げられます。 たとえば、もし x が {5, 7.2, 9, 0.5, 13} を含むならば x[2] は 7.2 を返します。 さて、(私たちは)先ほどの x[2] に別の値を割り当てると仮定します: x[2] = {11,22,33} その結果 x はこうなります: {5, {11,22,33}, 9, 0.5, 13} さらに、x[2]を問い合わせると {11,22,33} を返して、x[2][3]を問い合わせるとアトムの33を返します。添字の番号を要素番号1の範囲外にしようとした場合は添字関連のエラーを返します。たとえば、x[0], x[-99]またはx[6]はエラーが発生します。したがって x[1]においてもx[1][3]は有効なシーケンスではありません。これら添字や変数の数には制限がありません。しかし、変数としてのシーケンスの入れ子の深さは保守性や扱いやすさを考慮して、なるべく浅くする必要があります。なお、二次元配列は他の言語では一般的であり、シーケンスに対する(二次元構造の)シーケンスを使うことで簡単に記述できます: x = { {5, 6, 7, 8, 9}, -- x[1] {1, 2, 3, 4, 5}, -- x[2] {0, 1, 0, 1, 0} -- x[3] }番号を振ることにより構造を明瞭にできます。演算形式x[i][j]は任意の要素のアクセスに使用されます。 二次元配列は非対称であり、全体から"列"は x[i] で選択可能ではあるものの、桁の選択は単純ではない演算を必要とします。その他の論理構造として、たとえばn-次元配列、文字列の配列、構造体、構造体配列などがあります。 これらも同様に簡単かつ柔軟に扱うことができます: 三次元配列: y = { {{1,1}, {3,3}, {5,5}}, {{0,0}, {0,1}, {9,1}}, {{-1,9},{1,1}, {2,2}} } y[2][3][1] の結果は 9 文字列の配列: s = {"Hello", "World", "Euphoria", "", "Last One"} s[3] の結果は "Euphoria" 構造体: employee = { {"John","Smith"}, 45000, 27, 185.5 } 構造体内の"フィールド"または要素へのアクセスにおいて、良いプログラミング作法は様々なフィールドに対して定数を作成・定義して名前をつけることです。 これにより、あなたの作成したプログラムの可読性が向上します。たとえば、上述の定数を使って書きなおしたとします: constant NAME = 1 constant FIRST_NAME = 1, LAST_NAME = 2 constant SALARY = 2 constant AGE = 3 constant WEIGHT = 4 人々の名前は employee[NAME] にて、最後の要素にある人物の名前を知りたい場合は employee[NAME][LAST_NAME] で得られます。 構造体配列: employees = { {{"John","Smith"}, 45000, 27, 185.5}, -- a[1] {{"Bill","Jones"}, 57000, 48, 177.2}, -- a[2] -- .... など。 } employees[2][SALARY] は 57000 を返します。組み込み関数 length() はシーケンスの長さを返します。 シーケンス s の最後の要素を得たい場合は: s[length(s)]これを略記すると: s[$]同様に、 s[length(s)-1]簡略化できます: s[$-1]記号 $ はシーケンスの長さと等価です。角括弧の内側のみ $ を記述できます。たとえば、どこにでも入れ子ができます: s[$ - t[$-1] + 1]最初の $ は s を参照した長さを、次の $ は t の長さを返します(そうなります)。 この例では $ を用いることで入力の手間が省け、コードが明瞭になり、高速に処理できます: longname[$][$] -- 最後の要素に属する最後の要素これは比べてみると同じです: longname[length(longname)][length(longname[length(longname)])]添字および関数の副作用: 代入構文では添字は左側になります: lhs_var[lhs_expr1][lhs_expr2]... = rhs_expr全ての添字は左から右へ解釈され、式として評価されます。右辺式または、任意の左辺式から関数呼び出しが可能です。もし、関数呼び出しがlhs_varを変更してしまう副作用があるときは代入が一度で完了してしまうため、それらの変更はlhs_varの最後の値に適用されるかは未定義です。なにが起きるか明白であり、別の命令文により関数呼び出しが実行されてしまうため、同じ命令文による二つの異なる方法でlhs_varを修正しないでください。左辺値ではない添字は、副作用によるlhs_varの変更とは一切関係なく、常にlhs_varの最終値がrhs_exprの値であると見なされます。 Euphoriaのデータ構造は、ほぼ無限の柔軟性を兼ね備えています。他の言語では配列の要素数は固定かつ同じ型でなければならないという不自然さがあります。Euphoriaでは、これらの制限を排除しています。あなたは容易に新しい構造体をemployeeシーケンスに追加または異様に長い名前をNAMEフィールドに格納してもEuphoriaが解決します。あなたはemployee"構造体"に様々な違うものを格納できますし、サイズの違うものをすべて一つのシーケンスに格納できます。 Euphoriaのプログラムにおいて、すべての従来のデータ構造を容易に実現できます。しかし、これを従来の言語で宣言しようとすると非常に大変です。詳しくは2.3 Euphoriaと従来言語の比較を参照してください。 注意事項として、この演算は一般に変数であって添字を扱うことはできません。たとえば、 {5+2,6-1,7*8,8+1}[3] は非対応であり、またdate()[MONTH]でもありません。あなたはシーケンスの割り当られた変数にdate()が返されたならば添字の変数の値で月を得られます。 シーケンスは連続した要素であり、始点と終点の要素番号を与えることにより選択できます。たとえば、もし x が {1, 1, 1, 2, 2, 2, 1, 1, 1} ならば x[3..5] は {2, 2, 2} のシーケンスが返されます。 x[3..3] または x[3..2] ならば {2} のシーケンスが返されます。長さ0の場合は {} のシーケンスとして評価されます。もし y の値が {"fred", "geoge", "mary"} ならば y[1..2] は {"fred", "george"} が返されます。 変数の一部分を切り出して(スライス)上書きすることができます。x[3..5] = {9, 9, 9} または x[3..5] = 9 ならば結果は {1, 1, 9, 9, 9, 1, 1, 1} となります。y を {0, "Euphoria", 1, 1} 仮定します。 y[2][1..4] ならば "Euph" が返されます。もし y[2][1..4] = "ABCD" ならば y は {0, "ABCDoria", 1, 1} となります。 一般に、変数名は0またはそれ以上の添字であり、スライスは0また1の順にしたがいます。変数は添字またはスライスであって式ではありません。 空のスライスを定義するための明確な規約があります。ここではスライス s[i..j] の s を長さn と考えます。iからjの範囲をスライスとし j = i - 1 および i >= 1 を範囲として、さらにもしi = n + 1 ならば空のシーケンスを生成します。このように 1..0 および n + 1..n のすべての間は合法的な(空の)スライスです。実際のところ空のスライスは多くのアルゴリズムで有用です。しかしiからjの範囲をスライスとし i <= 1 を範囲としたときは違法です。たとえば、そのような"逆方向の"スライス s[5..3] はなにも指していません。 また、スライスに対しても略記 $ を使うことができます。すなわち、 s[2..$] s[5..$-2] s[$-5..$] s[$][1..floor($/2)] -- 最初の半分から最後までの要素s 2.2.7 シーケンスとアトムの連結 - '&' 演算子 任意の二つのオブジェクトを連結したい場合は & 演算子を使用します。これは、連結前のオブジェクトの長さの合計と等しい長さを持つシーケンスが結果として返ってきます(ここでは、アトムの長さは 1 であるとみなします)。すなわち、 {1, 2, 3} & 4 -- {1, 2, 3, 4} 4 & 5 -- {4, 5} {{1, 1}, 2, 3} & {4, 5} -- {{1, 1}, 2, 3, 4, 5} x = {} y = {1, 2} y = y & x -- y は {1, 2} のままで変化しません。 シーケンス s から 任意の要素 i を読み飛ばしたシーケンスを生成して連結することにより i を削除できます: s = s[1..i-1] & s[i+1..length(s)]i を 1 または length(s)と仮定し、s[1..0] または s[length(s)+1..length(s)] と記述しても空要素がスライスされるため合法です。 最後に、シーケンスは構造としてブレースブラケットとカンマを使用します: {a, b, c, ... }これらは演算子です。nオペランド(nは0またはそれ以上)を取得して、それらの値からn要素のシーケンスを作成します。たとえば、 x = {apple, orange*2, {1,2,3}, 99/4+foobar} シーケンス構造演算子は後述の評価優先順位表にあるとおり最低優先順位となっています。 シーケンスに対して、いくつかのその他重要な演算を特殊文字より明瞭な意味を持つ英語名の関数で行うことができます。これらはex.exe, exw.exe, exwc.exe, exuに組み込まれており、常に利用可能で動作も高速です。詳細は第2部 - ライブラリルーチンで詳しく解説されています。これらはサブルーチンを呼び出すかのような挙動を持ちます。しかし実際はもっと効率的に実装されています。 length()はシーケンス s の長さを返します。 この s は要素数です。これら(親)シーケンスの内部にある(子)シーケンスが要素として含まれている場合がありますが、その場合でも要素として返されるのは"上位"のシーケンスの長さであって、(親子合わせた)すべてのシーケンスの合計要素数ではありません。なお、length()に対してアトムの長さを求めた場合はエラーになります。たとえば、 length({5,6,7}) -- 3 length({1, {5,5,5}, 2, 3}) -- 4 (親シーケンス内の子シーケンスは1要素として数えられるので 6 ではない!) length({}) -- 0 length(5) -- エラー! repeat(item, count) repeat()はitem を count 回繰り返した結果をシーケンスとして作成します。 すなわち、 repeat(0, 100) -- {0,0,0,...,0} すなわち100個 の0 repeat("Hello", 3) -- {"Hello", "Hello", "Hello"} repeat(99,0) -- {} 任意のアトムまたはシーケンスであるitemが繰り返して出力されます。
append()はシーケンス s の末尾に itemを追加したシーケンスを作成します。 prepend()はシーケンス s の先頭に itemを追加したシーケンスを作成します。たとえば、 append({1,2,3}, 4) -- {1,2,3,4} prepend({1,2,3}, 4) -- {4,1,2,3} append({1,2,3}, {5,5,5}) -- {1,2,3,{5,5,5}} prepend({}, 9) -- {9} append({}, 9) -- {9} 新規に作成したシーケンスはオリジナルのシーケンスより常に1以上の長さを持ちます。シーケンスには任意のアトムまたはシーケンスを追加することができます。 これらappend() および prepend() の2種類の組み込み関数は連結演算子 & といくつかの類似点があります。これらには明確な差があります。すなわち、 -- シーケンスの追加処理での相違点 append({1,2,3}, {5,5,5}) -- {1,2,3,{5,5,5}} {1,2,3} & {5,5,5} -- {1,2,3,5,5,5} -- アトムの追加処理は同じ append({1,2,3}, 5) -- {1,2,3,5} {1,2,3} & 5 -- {1,2,3,5} 2.2.10 優先順位表 演算子の評価優先順位は次のとおりです: 最高優先順位: 関数/型の呼び出し 単項- 単項+ not * / + - & < > <= >= = != and or xor 最低優先順位: { , , , } このように2+6*3 は 2+(6*3) を意味するものの、むしろ(2+6)*3とすると自然です。上述の評価優先順に従い、演算子は同じ行の左から右方向へ解釈・評価されます。丸括弧 ( ) を演算子の前後に配置することにより、式の評価優先順位を任意の評価順へ強制することができます。 等価記号 '=' は、演算子ではなく代入構文 として使用されており、さらにこの構文は言語の一部です。
2.3 Euphoriaと従来の言語との比較 Euphoriaはシンプル、一般的、再帰的なデータ構造を基点とすることで、通常プログラミング言語でよく見られる(膨大な量の)うんざりする複雑さを回避しています。配列、構造体、共用体、構造体配列、多次元配列などは他の言語よりEuphoriaでシーケンスを使うと(すべての点において)、簡単に記述できます。さらに、リスト、スタック、キュー、ツリー(木)などの高度なデータ構造を実現できます。 さらに、Euphoriaにおいてあなたは混成型(mixed type)のシーケンスを扱うことができます。どんなものもシーケンスの要素に割り当てることができます。そして、動的メモリーアロケーションに関する問題を心配せずにシーケンスの長さを簡単に拡大また縮小できます。正確なデータ構造の雛形(設計)は事前に宣言される必要はなく、必要に応じて動的に変更することができます。一般的に用いられるコードは簡単に記述でき、実例として、ひとつのスタックを使用して様々なデータオブジェクトを push または pop することができます。Euphoriaでは様々な異なる種類のデーターオブジェクトを含む柔軟なリストを作成すことは些細なことであり、ほかの言語では何十行にもわたる醜悪なコードの記述を必要とします。 Euphoriaインタプリタでは巨大なデータオブジェクトを複製する場合は位置を指すだけなのでデータ構造の操作は非常に効率的です。 Euphoriaのプログラミングでは作成および操作が柔軟な動的シーケンスデータに完全に基づいています。シーケンスは それ自身 - ほかのデータ構造を習得する必要はありません。あなたは単純、安全、順応性のある値の世界で操作でき、それにより柔軟性がなく、退屈で、危険なビットの世界、バイト、ポインタおよびマシンクラッシュから遠く開放されます。 LISPやSmalltalkのような他の言語とは異なり、Euphoriaの"ガーベージコレクション"は連続したプロセスでプログラム実行時にランダムな遅延を決して発生させず、事前に大規模な記憶領域を割り当てません。 言語仕様としてC, C++, Adaなどの従来の言語は非常に複雑です。ほとんどのプログラマは言語のサブセットしか使いこなせていません。これらANSI規格の言語仕様は、複雑な法的文書を読むのと似ています。 あなたは単にデータを複写したり、対象の現在の長さを調べたり、連結したり、別のデータと比較したりなどするためにデータの型によって違うコードを書くことを強制されます。 それらの言語ではマニュアルには "strcpy", "strncpy", "memcpy", "strcat", "strlen", "strcmp", "memcmp", その他のルーチンの使用方法などがぎっしり記載されています。 それは、多種のデータ型に対して各々ひとつのみ動作するだけです。 複雑さの多くはデータ型を取り巻く問題です。あなたは、どうやって新しい型を定義しますか? データのどの型を混合(mixed)できますか? あなたはコンパイラにとって適切な型変換を別のどのような方法で行いますか? あなたが実行に柔軟性を必要として何かをする必要があるとき、あなたはしばしばコンパイラの裏をかくことを試みていることに気づきます。 これらの言語では数値の4(たとえば)は int, char, short, double, int * など依存性のある異なる意味を持たせることができます。 Euphoriaでは、 4 は アトムの 4 だけです。 Euphoria の型呼び出しは後ほどお目見えします。そして、それは非常に単純な概念です。 ほかの言語における動的ストレージアロケーションおよび開放の問題はプログラマにとってコーディングおよびデバッグにかかる時間を膨大に浪費させ、結果として非常に理解が困難なプログラムができあがります。これにより、連続稼動をしなければならないプログラムにおいて、膨大な不要メモリブロックを安全に開放することが守れなくなり しばしば "メモリーリーク" を引き起こします。 ポインタ変数はプログラミングにおいて幅広く使用されています。ポインタはデータ構造の"go to"と呼ばれることがあります。それはプログラマにデータがメインメモリのどこに存在するのか常に意識することを強制し、あらゆる種類の「低レベルで、再利用性や可搬性がなく、悪い意味でトリッキーな手法」による(メインメモリに対する)データ操作を強いられるからです。しかも、あなたがプログラムを実行する際に思い描く動作と実際のハードウェアの動作はかけ離れています。なお、Euphoriaにはポインタはありませんし、必要もありません。
2.4 宣言
識別子は通常は任意の長さ(可変長)の変数名および、その他のユーザ定義済みシンボルから構成されています。大小文字は区別されます。識別子は文字―正確には英文字、数値またはアンダースコア―から開始される必要があります。次の 予約語はEuphoriaでは特別な意味を持つため、識別子としては使用しないでください:
Euphoria標準エディタでは予約語は青色で表示されます。 識別子は下記の名称で使用されています:
これらは計算可能であり、またパラメータリストを持ちます。すなわち、 procedure empty() end procedure procedure plot(integer x, integer y) position(x, y) puts(1, '*') end procedure これにより名前が与えられたパラメータの個数が決定されます。しかし、全パラメータに対する制限はなく任意のオブジェクトを可変長シーケンスとして与えることができます。大半の言語では、可変長パラメータリストは重要です。伝統的な言語では、うんざりするほどの量の複雑で奇妙な仕組みや仕掛けを設定・記述しなければならず、平均的なプログラマは親切で正確な解説書や身近な熟練技術者がいない限り使いこなすことができません。 各引数の値はコピーされて渡されます。形式的にはパラメータ変数は手続きの内側で変更されることはあるものの(元の)、引数の値には影響はありません。
関数 関数は手続きのように見えますが、式や値を返します。 すなわち、 function max(atom a, atom b) if a >= b then return a else return b end if end function Euphoriaでは、どんなオブジェクトも返すことができます。シーケンスオブジェクトにより、複数の値をもつオブジェクト(multiple return value)を返すことができます。すなわち、 return {x_pos, y_pos} サブルーチン"または単純に"ルーチン"という用語は、いずれも手続きや関数を意味します。 これらは変数の値(種類)を指すための宣言で使用される特別な関数です。型は単一のパラメータを持ち、真(非0)または偽(0)いずれかのアトムを返します。型は、その他の関数のように呼び出せます。詳しくは 2.4.3 変数の型指定を参照してください。 これらは実行中に値を割り当てます。すなわち -- x は整数のみ割り当て可能です。 integer x x = 25 -- a, b および c は*なんでも*割り当てられます。 object a, b, c a = {} b = a c = 0 変数の宣言をすると、変数に名前を付けたり(スペルミス防止)、プログラム実行中に変数に割り当てる値を合法的に指定できます。 これらは決して変更できない初期値を変数に割り当てます。すなわち、 constant MAX = 100 constant Upper = MAX - 10, Lower = 5 constant name_list = {"Fred", "George", "Larry"} 定数割り当ては任意の演算結果を返し、さらに演算には以前定義した関数の呼び出しも含めることができます。定数変数の値は"固定(lock in)"されるため割り当ては一度のみです。 定数はサブルーチンの内側では宣言できません。 シンボルのスコープはプログラムごとに割り当てられシンボルの宣言により影響を受けます。例として、シンボルは可視性を持ちます。 Euphoriaには豊富な定義済みの手続き、関数および型があります。これらは任意のプログラム開始時に自動的に定義されます。これらはEuphoria標準エディタでは紫紅色(マゼンタ)で表示されます。これらは定義済み名称(pre-defined names)であって言語において予約されていません。これらは、あなたが宣言することにより変数またはルーチンを上書き(override)できます。 毎回ユーザ定義シンボルは使用前に宣言する必要があります。あなたは始めから終わりまで、任意のユーザ定義変数またはルーチンとの遭遇を除き(内容を知らなくても)、まだそれが定義されていなくても、Euphoriaのプログラムを読むは可能です。ソースの後方に来るルーチンは呼び出し可能であり、その場合は特別な関数である routine_id() および call_func() または call_proc() のいずれかの使用を必要とします。詳しくは第二部 - ライブラリルーチン - 動的呼び出しをお読みください。 手続き、関数および型は自分自身を再帰的に呼び出す相互再帰はルーチンAはルーチンBを呼び出し、(ルーチンBが)ルーチンAを直接的または間接的いずれかの方法で呼び出しますが、この手法には routine_id() 機構が要求されます。 シンボルの定義範囲は宣言から最後までがスコープになります。 手続きまたは関数の変数宣言においてスコープの終端は手続きまたは関数内(はプライベート変数)から終端までとなります。その他の変数、定数、手続き、関数および型のスコープの終端はソースファイルの終端までとなりますが、global(グローバル)キーワードが先に宣言されていなければローカルで参照された範囲となり、いずれの場合もスコープは無期限に拡張(保持)されます。 メインファイルからEuphoriaのファイルをインクルードできますが(2.6 特殊最上位構文を参照してください)、global キーワードで宣言された変数およびルーチンのみメインファイルからアクセスまたは参照することができます。インクルードファイルを(メイン)ファイル終端までインクルードし忘れる(シンボルの記述はあるが宣言が定義されていない)か、インクルードファイル全体が、その他の非グローバル宣言である場合は、エラーメッセージ "not declared" が表示されてエラーが発生します。もし、これらを使用する場合はメインファイルを使用してください。 global が付けられたシンボルは外部から使用(参照)できます。その他のシンボルは、そのファイル自身の内部から使用できます。この情報はメンテナンスまたは機能拡張、またはファイルの使用方法を習得する上で役に立ちます。内部ルーチンおよび変数の変更は、その他のファイルの詳細試験または他のユーザによるインクルードファイルの通知や詳細な解説を必要とせずに使用できます。 時々、ほかの開発者によるインクルードファイルを使用していると、名前衝突に遭遇する場合があります。既にほかの開発者によるインクルードファイルにおいて同名のグローバルシンボルが使用されているからです。そういうソースに遭遇した場合は、あなたは単純にエディタでインクルードファイルの問題を修正するでしょうが、新しいインクルードファイルのバージョンがリリースされるたびに同じことをする必要があります。この問題をEuphoriaでは非常に単純な方法で解決しています。include構文の拡張機能を使い、例えばこう書くことができます: include johns_file.e as john include bills_file.e as bill john:x += 1 bill:x += 2この場合、変数 x は 2つの違うファイルで宣言され、あなたは自分のファイルから2つの変数を参照したいとします。名前空間識別子はjohnまたはbillいずれかを使用することができ、いずれかの識別子をxの先頭に付け加えることによりxの参照先を指定できます。johnの名前空間のひとつが参照されるとき、その間においてbillは別の異なる名前空間として扱われます。名前空間識別子は任意のユーザ定義変数、定数、手続きまたは関数を加えることができます。これにより衝突を解決でき、また簡単かつ明瞭にすることができます。名前空間識別子はローカルスコープを持ちます。それはファイル内の宣言のみ知る(扱う)ことができ、たとえばファイルのinclude構文を含みます。別のファイルは同じインクルードファイルを参照するためにそれぞれ別の名前空間を定義することがあります。 Euphoriaにおいてシンボルのスコープを制限することを推奨します。もしプログラム全体ですべてのシンボルが自動的にグローバルシンボルになるとしたら、特に大規模プログラムはたくさんの違うプログラマが書いたファイルから構成されるため、大量の名前衝突を抱えることになるかもしれません。名前衝突はコンパイラのエラーメッセージの原因となるかもしれず、またプログラムの違った部分に気づかずに同じ変数名を誤って変更を加えてしまい非常に発見しづらいバグを発生させることがあります。できるだけスコープをなによりも制限してお使いください。可能な限りルーチンにおいてはプライベート変数を使用して、それが不可能な場合は、プログラム全体をグローバルにするより、ファイルをローカルにしてください。 Euphoriaはシンボルの宣言を調べる場合、最初に現在のルーチン、現在のファイル、他のグローバル属性のファイルの順に調査します。ローカルシンボルはグローバルシンボルを上書き(override)します。ローカルシンボルのスコープの終わりに、再びグローバルシンボルが可視状態へ復帰します。もし多数のグローバルシンボルが見つかったとき、もしファイル内に存在する一致するシンボルがひとつだけならば直接または間接的にファイルからインクルードされたいずれかのシンボルの使用を試みます。この方法により、もし衝突するシンボルがあってもサードパティ製品のライブラリを同じアプリケーションで使用可能です。ライブラリを使用するアプリケーションコードはシンボルの衝突を回避するために明示的な名前空間を使用する必要があるものの、ライブラリ自体の編集は不要です。 サブルーチンの内側には 定数を宣言することはできません。また、使用できる定数のスコープはグローバルまたはローカルのみであり プライベートにはできません。 サブルーチンの実行構文の前に、全ての変数宣言がサブルーチン内の始めに現れる必要があります。 上位宣言は、全ての場合においてサブルーチンの外側に記述されるものであり、ループまたはif構文の入れ子の内側にあってはいけません。 制御変数はforループで使用される特別な変数です。それはループ開始時に自動的に宣言され、およびそのスコープの終わりはforループの終わりまでになります。もしループが関数または手続き内にあるときループ変数はプライベート変数であり他のプライベート変数と同名であってはいけません。ループが上位(top level)にあるとき、どの関数または手続きの外側であっても、ループ変数はローカル変数でありファイル内のどのローカル変数も同名であってはいけません。ループが入れ子にならない限りforループ内で同名を使うことができます。ループ変数は他の変数として宣言できません。for構文の範囲において指定された値は、合法的なループ変数の値を定義します - なお型指定は不要であり許可されていません。 これまで、あなたは既にいくつか変数の型の例を見てきましたが、今から正確な型を定義します。 変数の宣言は、型名とそれに続く変数名のリストによって宣言されます。例えば、 object a global integer x, y, z procedure fred(sequence q, sequence r) 型:object, sequence, atom および integer は 定義済み(predefined)です。 object型変数は任意の値を持たせることができます。sequence型で宣言されたものは常にシーケンスです。atom型で宣言されたものは常にシーケンスです。 integer 型で宣言されたものは常に整数 -1073741824 から +1073741823 までの範囲を持つアトムです。あなたは最大15桁の10進数といった、より大きい整数値を記述して正確に計算可能ではあるものの、integer ではなく atomとして宣言してください。
定義済み型(predefined types)を増やすには、ユーザ定義型(user-defined types)を作成できます。単一の引数を持つ関数を定義するだけではあるものの、実際は function ... end function の代わりに type ... end type を宣言として使用します。例えば、 type hour(integer x) return x >= 0 and x <= 23 end type hour h1, h2 h1 = 10 -- 正常 h2 = 25 -- エラー!プログラムはエラーメッセージともに中止されます。 変数 h1 および h2 は整数の0から23の値のみ割り当て可能です。インタプリタは h1 または h2を各々割り当て後 hour() を呼び出して、新しい値を通します。最初にそれが値が整数どうか検査します(なぜなら "integer x" であるため)。もしそれがそうならば、return構文は x の値をテストするために実行されます(すなわち h1 または h2 が新しい値である)。もし hour() から真が返された場合は、正常に処理を継続します。もし hour() から偽が返された場合は、プログラムは適切な診断メッセージとともに中断されます。 同様に、"hour" はサブルーチンにおいて引数の宣言に使用できます: procedure set_time(hour h) set_time()は引数 h の値が正当な場合のみ呼び出されますが、そうでなければプログラムはメッセージとともに中断されます。 変数の型は変数へ代入した後に検査され(例外としてコンパイラがそれをあらかじめ決定している場合は検査は必要でありません)、型関数が検査結果として偽を返した場合プログラムは即刻停止します。サブルーチンの引数の型はサブルーチンが呼ばれたときにそれぞれ検査を行います。この検査は、変数がその変数の型に一致していない値を決して保持することができないことを保証します。 他の言語と違い、変数の型は変数のどのような計算であっても影響を与えません。変数の値のみ演算式において重要です。型によるエラーチェックは変数のあらゆる種類の"破壊"を防ぐために役立ちます。 ユーザ定義型はあなたのプログラムの例外論理エラーを捕まえることができます。ただし、ユーザーの入力エラーを修正したり捕まえたりできるように設計されていません。 型検査はサブルーチンの間で with type_check または without type_check といった特殊構文を使うことにより無効または有効にできます。初期規定値は with type_check です。
Euphoriaの型定義の方法は他の言語で見受けられるものより単純であり、さらにEuphoriaはプログラマに対して非常に柔軟で合法的な値を定義するためのデータ型を提供します。 任意のアルゴリズムにおいても使用する値を含めたり除外できます。あなたは変数に任意の値を持たせることを可能にする object 型としても宣言可能です。ルーチンを特化指定型のまたは特化汎用型として動作するように記述できます。 大半のプログラムにおいて、新しい型を定義することは小さな優位点があり、さらにあなたは4つの定義済み型(predefined types)を思いのままに付けることができます。Euphoriaの型機構を使用するかどうかあなたが決定できます。あなたはプログラムを作成する上で必ずしも型は必要ではありません。 しかし、大規模プログラムにおいて、厳格な型定義はデバッグ過程において手助けになります。 論理エラーはソース内の発生箇所付近で捕えられ、さらに残りのプログラムによって微妙なやり方(悪影響)で伝播することを許可しません。さらに、変数と常に関係する合法的な値を持つと保障されるならばコード断片に含まれる不正行為について推論するのはより容易ではあるものの、理想の値(結果)とは限りません。 型はあなたのプログラムにおいて有意義な機械検査可能なドキュメンテーションを提供でき、後日あなたか他の人があなたの書いたコードを理解するのをより簡単にします。添字検査、未初期化変数検査、およびそれらとその他検査は常に組み合わされた形で提供され、その他大半の言語と比較して Euphoria の厳格な実行時型検査はデバッグを非常に容易にします。同様に他の言語のテスト段階で発見できなかった多数の潜在的なバグはEuphoriaでは捕まえることができるでしょうから最終的にプログラムの信頼性が向上します。
2.5 構文 次の種類の実行可能な構文があります: Euphoriaではセミコロン( ; )は使用しませんが、複数の構文はお好みのまま一行にまとめたり、一つの構文を複数行に渡り分割するという風に自由に配置できます。ただし、識別子、文字列、数値またはキーワードは構文の中間で分割しないでください。 代入構文は単一変数、または添字や変数の分割に対して演算を行い値を割り当てます。すなわち、 x = a + b y[i] = y[i] + 1 y[i..j] = {1, 2, 3} なお、変数全体を指して代入処理を行った場合は以前の値およびデータ構造は破棄されます。たとえば、初期化によってxが1000個の要素をもつシーケンスと仮定すると: object x x = repeat(0, 1000) -- 1000個のゼロをシーケンスを代入そして、xにアトムを割り当てます: x = 7x はobject型として宣言されているため完全に合法です。x は以前の値が1000個の要素をもつシーケンスでしたがこれにより簡単に消滅します。1000個の要素をもつシーケンスによって消費されていた領域はEuphoriaの動的ストレージアロケーション機能によって自動的に開放され再利用されます。 注意事項として、Euphoriaでは等価記号`=`は両辺とも等価演算子のみ使用できます。それは式として使用できません(C言語では)。 Euphoriaの代入構文にはいくつか追加の記法が提供されています。 これにより、入力量を減らしてコードを簡潔にし、代入を単一の演算子によってまとめることができます: たとえば、これは: mylongvarname = mylongvarname + 1こう記述できます: mylongvarname += 1これは: galaxy[q_row][q_col][q_size] = galaxy[q_row][q_col][q_size] * 10こう記述できます: galaxy[q_row][q_col][q_size] *= 10そして、これは: accounts[start..finish] = accounts[start..finish] / 10こう記述できます: accounts[start..finish] /= 10 一般に代入の構文は: 左側 = 左側 演算子 演算式しかし、こう記述できます: 左側 演算子= 演算式演算子は次のどれかひとつです: + - * / & 通常、左側に複数の添字・分割が含まれているとき、演算子=は長い構文より実行速度が早いです。これを使うことにより、演算子=構文を見つけたときと比較すると長い構文より少し読みやすくなり、見た目で比較しなくても右側は左側自身の複製であることがわかります。 手続きの呼び出しは、オプションとして引数のリストがあれば引数の値を手続きに渡して実行を開始します。すなわち、 plot(x, 23) 2.5.3 if構文 if構文は if が 0(偽) または 非0(真) であるかを条件判断をし条件が該当すると then に続く構文を適切に実行します。これに elsif および else 文が付加されることがあります。すなわち、 if a < b then x = 1 end if if a = 9 and find(0, s) then x = 4 y = 5 else z = 8 end if if char = 'a' then x = 1 elsif char = 'b' or char = 'B' then x = 2 elsif char = 'c' then x = 3 else x = -1 end if 注意事項として、 elsif は else if の短縮であり、比較すると end if は不要となるため簡潔です。end if は if構文全体と一対であり、その多数の elsif でさえも、それに含まれます。 if およびelsifは短絡評価を用いて判別しています。 while構文は条件が非0(真)であるか判断して、真である間はループ内を実行します。すなわち、 while x > 0 do a = a * 2 x = x - 1 end while 短絡評価 条件はif, elsif, または while を含む and または or 演算子によりテストされ、短絡評価として使用されます。たとえば、 if a < 0 and b > 0 then ...If a < 0 が偽ならば、 Euphoriaはもし b が 0 より大きくても両辺のテストをしません。 その結果すべて偽と仮定します。同様に、 if a < 0 or b > 0 then ...if a < 0 が真ならば Euphoriaは直ちに真であると決定して、 b の値をテストしません。 一般的に、条件文の構文は: A and B A and Bが任意の2つの演算式のとき、Euphoriaは A が偽であれば即時に演算式 Bの判別処理を省略して全ての結果を偽と判断します。 同様に: A or BA が真ならば Euphoriaは 演算式 Bの評価を省略して、結果は真であると判断します。 演算式 B が関数呼び出しがあるならば、さらに関数は副作用を引き起こす危険性があります。すなわち、それは結果として関数から大量の値を含むものを返されるかもしれないため、コンパイル時に警告を受け取ることがあります。 非常に古いEuphoriaのバージョン(2.1以前)では短絡評価は使用できなかったため、いくつかの古いコードは正しく動作しない恐れがありますが、RapidEuphoria.comのArchiveページに掲載されている全てのプログラムにおいて、この方法に依存する副作用は発見されていません。 演算式 B にはランタイムエラーの発生原因となるものが含まれています。もしEuphoriaがBの評価を行わないならばエラーは発見されません。 実例として: if x != 0 and 1/x > 10 then -- ゼロ除算エラーが回避されてしまう。 while 1 or {1,2,3,4,5} do -- 結果として違法なシーケンスが回避されてしまうB には未初期化変数、範囲外の添字などが含まれています。 これは粗雑なコーディングのように見えますが、実際のところこの記法はより単純に記述でき、より可読性に優れている方法です。実例として: if atom(x) or length(x)=1 thenアトム x に問題があるならば短絡評価が行われずアトムには length は定義されません。短絡評価が行われたならば、length(x) は シーケンス x のみ検査を行います。 同様に: -- s から 'a' または 'A' を探し出す。 i = 1 while i <= length(s) and s[i] != 'a' and s[i] != 'A' do i += 1 end whileこのループにおいて、変数 i は いずれlength(s)より大きくなることがあります。短絡評価が行われないとき、繰り返しの最後に s[i] を評価した際に添字範囲外エラーが発生します。短絡評価が行われるとき、 i <= length(s) が偽ならばすぐにループは中断されます。Euphoriaは s[i] != 'a' および s[i] != 'A' を評価しません。なおこの場合は添え字に関するエラーは発生しません。 短絡評価としてand および or は if, elsif および while条件文だけで扱うことができます。他の文脈では使用できません。例えば、代入構文においては: x = 1 or {1,2,3,4,5} -- x には {1,1,1,1,1} が 代入されます。もし短絡評価をこのように使用したならば、x には 1 を代入した後、一様に {1,2,3,4,5} は見えなくなります。これは間違っています。短絡評価はifの結果として真または偽かを注意するだけでif/elsif/while条件文にて使用できるものの、条件文は結果としてアトムを返すことを必要とします。 for構文はループ変数制御によって初期値から最終値まで上下させて実行する特殊なループ(反復処理)を定義します。すなわち、 for i = 1 to 10 do ? i -- ? は print() の略記 end for -- 小数も使用できます。 for i = 10.0 to 20.5 by 0.3 do for j = 20 to 10 by -2 do -- カウントダウン ? {i, j} end for end for ループ変数は自動的に宣言され、ループの終了まで存在します。ループの外側の変数は値を持たず宣言さえされません。もし最終値が必要なら、ループを抜ける前に別の変数にコピーしてください。コンパイラはループ変数に対して代入を一切許可していません。初期値・ループ制限値および加算のすべてはアトムでなければなりません。もし、加算値が指定されていなければ +1 とみなされます。ループに入るとき制限・加算値が定義され、ループ実行中は影響を一切受けません。ループ変数のスコープについては 2.4.2 スコープを参照してください。 return構文構文はサブルーチンから直接的に値を返します。もし、サブルーチンが関数または型ならば値を返します。すなわち、 return return {50, "FRED", {}} 2.5.7 exit構文 exit構文は whileループ または forループの内側に記述される場合があります。 ループ後に最初の構文に制御を渡して、すぐにループ終了をします。すなわち、 for i = 1 to 100 do if a[i] = x then location = i exit end if end for よくある一般的な記述です: constant TRUE = 1 while TRUE do ... if some_condition then exit end if ... end whileすなわち、"無限" whileループによって生成されたループ本体は exit構文よって任意の位置で終了できます。
ex.exeにおいて、もしあなたがうっかり無限ループを生成してしまった場合は、出入力の発生なしで、それを停止する簡単な方法はありません。Control-Alt-Delete を入力して再起動するか、(Windows 配下では)DOSプロンプトセッションを終了させる必要があります。復帰後にプログラムが書き込みを行うためにファイルを開いていた場合、scandisk(ボリュームのエラーチェック) によるファイルシステムの整合性検査を行うことが望ましいです。プログラムがキー入力受付状態による待機中のみ、 control-c によってプログラムを中断できます(allow_break(0)が指定されていない場合に限ります)。 exw.exe または exuであれば control-c によっていつでもプログラムを即時強制終了できます。
2.6 特殊上位構文 任意の構文を実行する前にEuphoriaフロントエンドは初めにあなたのプログラムを素早く読み込み、全ての構文の文法を検査したあと低レベル中間言語(low-level intermediate language : IL)へ変換します。インタプリタは直ちにILを実行します。トランスレータはILをC言語へ変換します。バインダー/シュラウダーは後日に実行するためにILを保存します。これら三つのツールは全て同じフロントエンドを共有しています(Euphoriaで記述されています)。 もしあなたのプログラムがルーチンと変数宣言を含むのみで、しかも上位実行構文がないならば実行は発生しません(その他文法検査のみ行われます)。なぜなら上位構文を呼び出すにはメインルーチンを必要とします(1.1 サンプルプログラムを参照してください)。さらに、プログラムが実行可能な上位構文やルーチンを持たないことはすべて可能です。たとえば、Euphoriaを簡単な電卓として使用したいときは、いくつかの print (またはその略記である ?)命令文をファイルに記述後、それを実行するといいでしょう。 今まで見てきたように任意のEuphoriaの 構文は forループ, whileループ, if 構文など (returnではない)を含む上位(top-level)にて使用できます。すなわち、任意の関数 または手続きの外側にて使用できます。 さらに次のような特殊上位構文が上位(top-level)のみに現れるかもしれません:
大規模プログラムの作成時にinclude構文を使用することにより、論理的にファイルを分割することはしばしば役に立ちます。ときどきあなたは過去に書いたものや、他の誰かが書いたいくつかのコードを再利用したい場合があります。メインプログラムからコピーアンドペーストするのではなく、そのコードを含んでいるファイルを参照するためにinclude構文を使用したほうが良いです。次はinclude構文の第一構文です:
2.6.2 with / without これらの特殊構文はEuphoriaがあなたのプログラムを内部形式に翻訳する場合に影響を及ぼします。これはあなたのプログラムのロジックは変更しませんが、プログラムを実行することにより得られる診断情報に影響があります。詳しくは3. デバッグとプロファイルを参照してください。
with の後にめったに使用されない特殊な with のオプションとしてコードナンバーが現れる場合があります。以前のリリースにおいてこのコードはファイルから除外されており古い""パブリックドメイン""版では構文数を増やすためにRDSによって利用されていました。 任意の設定を組み合わせたり設定変更できますが、設定変更はサブルーチンの間で行う必要があり、サブルーチン内で行うことはできません。唯一の例外としてプログラムのプロファイリングを行うためにひとつ有効にできるということです。 インクルードファイルは with / without の設定を継承しインクルードされた時点で影響を受けます。これらはインクルードファイルにて設定を変更できますが、インクルードファイル(のスコープ)が終わると元の状態に復帰します。インクルードファイルとその他のインクルードファイルの警告を無効(初期値)にしても、メインファイルでは無効になりません。
|