2. 言語仕様


2.1 オブジェクト


2.1.1 アトムとシーケンス

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を理解する鍵なのです。

パフォーマンスノート:
アトムは8バイトの浮動小数点数としてメモリーに格納される意味を理解できますか? いいえ。通常はEuphoriaインタプリタは使用メモリーの節約と実行速度を向上させるため、整数値であるアトムをマシン整数(4バイト)として格納しています。小数や巨大な数が結果として発生すると、自動的に浮動小数点数変換処理を行います。

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.1.3 コメント

コメントはダッシュ(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} を返します。

なお、この件に関しての豊富な例題は後述します。


2.2.1 関係演算子

関係演算子 <   >   <=   >=   =   !=  は、 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 と等しくはないか(真)

なお、関係演算子はシーケンスにも適用することができます。


2.2.2 論理演算子

論理演算子は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を使用します。


2.2.3 算術演算子

「加算、減算、乗算、除算、単項マイナス、 単項プラス」といった一般的な算術演算子があります。

        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除算が発生するとエラーメッセージを出力してプログラムを強制終了します。

なお、単項プラスを使用すべき唯一の理由は、あなたが書いたプログラムを読む人が正数であることをはっきり認識できるためです。実際のところ、これに関してはインタプリタは一切の計算も行いません。


2.2.4 シーケンスの演算

すべての関係演算子、論理演算子と算術演算子は既に解説済みではあるものの、さらに算術ルーチンは第二部 - ライブラリルーチンに掲載されており、これらは単数(アトム)とシーケンスに使用できます。

単項演算子(ひとつのオペランド)はシーケンスの各要素に適用され、処理結果として同一の長さのシーケンスを返します。もし、これら要素の一つでもシーケンス自身ならば再度同じ規則が再帰的に適用されます。すなわち、

        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" より小さいならば (真)


2.2.5 シーケンスの添字

シーケンスに角括弧の中に要素の番号を与えることにより要素のひとつを選択できます。 要素の番号は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"
s[3][1] の結果は 'E'

構造体:

        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()が返されたならば添字の変数の値で月を得られます。


2.2.6 シーケンスのスライス

シーケンスは連続した要素であり、始点と終点の要素番号を与えることにより選択できます。たとえば、もし 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)] と記述しても空要素がスライスされるため合法です。


2.2.8 シーケンスの構造

最後に、シーケンスは構造としてブレースブラケットとカンマを使用します:

        {a, b, c, ... }
これらは演算子です。nオペランド(nは0またはそれ以上)を取得して、それらの値からn要素のシーケンスを作成します。たとえば、
        x = {apple, orange*2, {1,2,3}, 99/4+foobar}

シーケンス構造演算子は後述の評価優先順位表にあるとおり最低優先順位となっています。


2.2.9 その他のシーケンス演算

シーケンスに対して、いくつかのその他重要な演算を特殊文字より明瞭な意味を持つ英語名の関数で行うことができます。これらはex.exe, exw.exe, exwc.exe, exuに組み込まれており、常に利用可能で動作も高速です。詳細は第2部 - ライブラリルーチンで詳しく解説されています。これらはサブルーチンを呼び出すかのような挙動を持ちます。しかし実際はもっと効率的に実装されています。


length(s)

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()はシーケンス 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 宣言


2.4.1 識別子

識別子は通常は任意の長さ(可変長)の変数名および、その他のユーザ定義済みシンボルから構成されています。大小文字は区別されます。識別子は文字―正確には英文字、数値またはアンダースコア―から開始される必要があります。次の 予約語はEuphoriaでは特別な意味を持つため、識別子としては使用しないでください:

    and            end             include          to
    by             exit            not              type
    constant       for             or               while
    do             function        procedure        with
    else           global          return           without
    elsif          if              then             xor

Euphoria標準エディタでは予約語は青色で表示されます。

識別子は下記の名称で使用されています:

  • 手続き
  • 関数
  • 変数
  • 定数


手続き

これらは計算可能であり、またパラメータリストを持ちます。すなわち、

        procedure empty()
        end procedure

        procedure plot(integer x, integer y)
            position(x, y)
            puts(1, '*')
        end procedure

これにより名前が与えられたパラメータの個数が決定されます。しかし、全パラメータに対する制限はなく任意のオブジェクトを可変長シーケンスとして与えることができます。大半の言語では、可変長パラメータリストは重要です。伝統的な言語では、うんざりするほどの量の複雑で奇妙な仕組みや仕掛けを設定・記述しなければならず、平均的なプログラマは親切で正確な解説書や身近な熟練技術者がいない限り使いこなすことができません。

各引数の値はコピーされて渡されます。形式的にはパラメータ変数は手続きの内側で変更されることはあるものの(元の)、引数の値には影響はありません。

パフォーマンスノート:
実際のところインタプリタではシーケンスまたは浮動小数点数は必要になるまでコピーされません。たとえば、
            y = {1,2,3,4,5,6,7,8.5,"ABC"}
            x = y
x = y 構文において、 y の新しいコピーは作成されません。x と y の両方とも単純に同じシーケンスを"指している"だけです。もし、x[3] = 3 ならばシーケンスは別々に分けられて新しい x がメモリに作成されます。(たとえ、これらが 8.5 や "ABC" といった静的な共有コピーであっても)。応用として引数はコピーされてサブルーチンに渡されます。

関数

関数は手続きのように見えますが、式や値を返します。 すなわち、

        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)"されるため割り当ては一度のみです。

定数はサブルーチンの内側では宣言できません。


2.4.2 スコープ

シンボルのスコープはプログラムごとに割り当てられシンボルの宣言により影響を受けます。例として、シンボルは可視性を持ちます。

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構文の範囲において指定された値は、合法的なループ変数の値を定義します - なお型指定は不要であり許可されていません。


2.4.3 変数の型指定

これまで、あなたは既にいくつか変数の型の例を見てきましたが、今から正確な型を定義します。

変数の宣言は、型名とそれに続く変数名のリストによって宣言されます。例えば、

        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として宣言してください。

注意:
手続きまたは関数パラメータ(引数)リストは上述の fred() のように、型名のあとに単一のパラメータ(引数)が続けて記述される場合があります。

パフォーマンスノート:
通常は変数を含めた計算式を用いる場合は変数を整数として宣言するとアトムとして宣言するより幾分か高速です。もし浮動小数点数ハードウェアを搭載している場合、Euphoriaは整数として表せないものを操作するためにアトムを使用します。もし浮動小数点数ハードウェアを搭載していない場合、Euphoriaは ex.exe (またはWindowsの)に含まれている浮動小数点数ソフトウェア算術ルーチンを呼び出します。強制的に浮動小数点数ハードウェア(FPU)を使用禁止にする場合は、環境変数を設定してください:
            SET NO87=1
ソフトウェアルーチンを使用する場合は動作速度がやや遅くなりますが、これには若干の利点があり初期のいくつかのPentiumチップを使用する場合に浮動小数点数のFDIVバグを心配しなくて済むようになります。

定義済み型(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のプログラムにおいてほかの言語に対抗すべく動作速度を比較するプログラムを書く場合、あなたはファイルのトップに without type_check を指定する必要があります。これはEuphoriaに実行時の型検査を無効にする許可を与え、その結果として多少なりとも実行時間を節約できます。その場合は型検査を除きその他すべての検査は機能します。たとえば、添字検査、未初期化変数など。あなたが型検査を無効にしても、Euphoriaは優位的な特権検査機構を行使する権限を持ちますが、それでも大半の場合において実際はプログラムをより早く動作できます。それでも型検査が失敗して正常に動作しない場合は、型検査を無効にしてもよいです。型検査が有効また無効であっても、マシンレベルの例外は決して発生しません。ときどきあなたが間違いをしたときEuphoriaから常に意味のあるメッセージを得ることができます。(これはメモリをpokeで直接操作する場合、またはC言語や機械語で記述されたルーチンを呼び出す場合はそうではありません。)

Euphoriaの型定義の方法は他の言語で見受けられるものより単純であり、さらにEuphoriaはプログラマに対して非常に柔軟で合法的な値を定義するためのデータ型を提供します。 任意のアルゴリズムにおいても使用する値を含めたり除外できます。あなたは変数に任意の値を持たせることを可能にする object 型としても宣言可能です。ルーチンを特化指定型のまたは特化汎用型として動作するように記述できます。

大半のプログラムにおいて、新しい型を定義することは小さな優位点があり、さらにあなたは4つの定義済み型(predefined types)を思いのままに付けることができます。Euphoriaの型機構を使用するかどうかあなたが決定できます。あなたはプログラムを作成する上で必ずしも型は必要ではありません。

しかし、大規模プログラムにおいて、厳格な型定義はデバッグ過程において手助けになります。 論理エラーはソース内の発生箇所付近で捕えられ、さらに残りのプログラムによって微妙なやり方(悪影響)で伝播することを許可しません。さらに、変数と常に関係する合法的な値を持つと保障されるならばコード断片に含まれる不正行為について推論するのはより容易ではあるものの、理想の値(結果)とは限りません。

型はあなたのプログラムにおいて有意義な機械検査可能なドキュメンテーションを提供でき、後日あなたか他の人があなたの書いたコードを理解するのをより簡単にします。添字検査、未初期化変数検査、およびそれらとその他検査は常に組み合わされた形で提供され、その他大半の言語と比較して Euphoria の厳格な実行時型検査はデバッグを非常に容易にします。同様に他の言語のテスト段階で発見できなかった多数の潜在的なバグはEuphoriaでは捕まえることができるでしょうから最終的にプログラムの信頼性が向上します。

逸話その1:
大規模なC言語プログラムをEuphoriaへ移植時に、いくつか潜在的なバグが発見されることがあります。このC言語プログラムを完全に""正しい""と過信していましたが、結局のところバグが発見されました: 状況は初期化が済んでいない変数を読み込もうとしたり、配列に対して要素番号 ""-1"" の位置を指しており当たり前のように読み書きが行われていて、なおかつなにかがスクリーンに書き込まれる状況です。これらの問題は平常どおり検査していたのでは簡単に発見できないエラーなので、テストを行っても見つけづらいバグとしてC言語のコードに残りました。

逸話その2:
Writing Efficient Programs by Jon Bentleyの117ページで解説されているクイックソートアルゴリズムには添字に関する誤りが含まれています!このアルゴリズムは配列の開始位置からソートを行うために時々要素を読み込み、配列の終わりまでときどき要素を読み込みます。たとえどのようなゴミが読み込まれても、それでもなおアルゴリズムは動作し続けます。 - おそらくこれが決してバグ捕まえることができない理由です。しかし、もし配列が(仮想)メモリの直前または直後にない場合どうでしょうか?その後 Bentley はバグを解消するために、アルゴリズムを修正しています。 -- また、彼は修正版を発表しました。熟練者であっても添字検査は必要です!

パフォーマンスノート:
典型的なユーザ定義型は広範囲に渡り使用されるとき、型の検査には20〜40パーセントのわずかな実行時間が加わるだけです。あなたが本当に必要以上の動作速度を要求としない限り型の検査を有効にしておいてください。あなたは、わずかしかない非常に重いルーチンの実行を軽くするために型の検査を無効にすることも考慮するかもしれません。この問題を解決するにはプロファイリングが手助けになります。


2.5 構文

次の種類の実行可能な構文があります:

Euphoriaではセミコロン( ; )は使用しませんが、複数の構文はお好みのまま一行にまとめたり、一つの構文を複数行に渡り分割するという風に自由に配置できます。ただし、識別子、文字列、数値またはキーワードは構文の中間で分割しないでください。


2.5.1 代入構文

代入構文は単一変数、または添字や変数の分割に対して演算を行い値を割り当てます。すなわち、

        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 = 7
x は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

一般に代入の構文は:

      左側 = 左側 演算子 演算式
しかし、こう記述できます:
      左側 演算子= 演算式
演算子は次のどれかひとつです:    +   -   *   /   &

通常、左側に複数の添字・分割が含まれているとき、演算子=は長い構文より実行速度が早いです。これを使うことにより、演算子=構文を見つけたときと比較すると長い構文より少し読みやすくなり、見た目で比較しなくても右側は左側自身の複製であることがわかります。


2.5.2 手続きの呼び出し

手続きの呼び出しは、オプションとして引数のリストがあれば引数の値を手続きに渡して実行を開始します。すなわち、

        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

注意事項として、 elsifelse if の短縮であり、比較すると end if は不要となるため簡潔です。end ifif構文全体と一対であり、その多数の elsif でさえも、それに含まれます。

if およびelsif短絡評価を用いて判別しています。


2.5.4 while構文

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 B
A が真ならば 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 および orif, 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条件文にて使用できるものの、条件文は結果としてアトムを返すことを必要とします。


2.5.5 for構文

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 スコープを参照してください。


2.5.6 return構文

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構文よって任意の位置で終了できます。
パフォーマンスノート:
Euphoriaはこの方式のループを最適化します。実行時、上位ループのテストを実行しません。ちょうど、そこに単純な無条件ジャンプがループ内部の最初の構文からend whileに存在します。

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
  • with / without


2.6.1 include

大規模プログラムの作成時にinclude構文を使用することにより、論理的にファイルを分割することはしばしば役に立ちます。ときどきあなたは過去に書いたものや、他の誰かが書いたいくつかのコードを再利用したい場合があります。メインプログラムからコピーアンドペーストするのではなく、そのコードを含んでいるファイルを参照するためにinclude構文を使用したほうが良いです。次はinclude構文の第一構文です:

 include ファイル名

これはEuphoriaのソースを読み込みます(コンパイル)。

いくつかの例です:

    include graphics.e
    include \mylib\myroutines.e

インクルードファイルにおいても全ての上位(top level)コードが実行されます。

メインファイルにおいて既に定義された全てのグローバルシンボルはインクルードファイルからでも参照できます。

注意せよ。これらシンボルはインクルードファイルで global と定義された場合に限りプログラムの残りから参照(使用)できます。

絶対ファイル名が与えられたとき、Euphroriaはそれを開いて構文解析を開始します。相対ファイル名が与えられたとき、Euphoriaは下記の手順でディレクトリ配下から関係するファイルを探して開こうと試みます:

  1. 現在のソースファイルがあるディレクトリ。すなわち、ソースファイルにinclude構文を含むならば処理を開始します。

  2. コマンドラインからインタプリタ、トランスレータまたはバインダーに引数として与えられたメインファイルを含むディレクトリ。

  3. もし環境変数 EUINC が定義されているならば、Euphroiaは EUINC に記されている(左から右へ)各ディレクトリのリストを確認します。EUINC はディレクトのリストでなければいけませんし、さらにセミコロンで区切られて(Linux / FreeBSD上ではコロン)、PATH変数と同じ構文です。EUINC は Linux / FreeBSD または DOS では setコマンドにて / Windowsでは環境変数から追加できます。(Windows XPの場合はコントロールパネル / パフォーマンス & メンテナンス / システム / システムのプロパティ 経由にて、またはそれ以前のWindowsのバージョンにおいてAUTOEXEC.BATを編集します)。すなわち、

           SET EUINC=C:\EU\MYFILES;C:\EU\WIN32LIB
    EUINCはアプリケーションの分野に応じてインクルードファイルを組織化して、euphoira\include下のファイルを編集しなくて済む上にほかの多数のファイルの衝突を避けることができます。
  4. 最後に、もしファイルが見つからない場合は、euphoria\includeを検索します。このディレクトリはEuphoriaの標準インクルードファイルを含みます。環境変数EUDIRはEuphoiraがあなたのeuphoria関連ディレクトリを探すべきか指定します

なお、インクルードファイルから他のファイルをインクルードできます。実際は、インクルードファイルを深さ30階層まで"入れ子"にできます。

通常はインクルードファイル名の拡張子は .e または場合により .ew または .ew です(WindowsまたはLinuxの利用者が対象)。これは慣例ですので強制でありません。

>もし、ファイル名(またはパス)に空白が含まれている場合、ダブルクォートで括る必要がありますが、クォートは任意です。また、バックスラッシュを二重に記述してください。たとえば:

    include "c:\\program files\\myfile.e"

その他であれば場合により新しい名前空間識別子が定義されますが(下記参照)、もしinclude構文によって同じファイルが既にインクルードされている場合は静かに無視されます。

include構文は一行に書く必要があります。また、コメントは同じ行の構文の後に記述されることがあります。

次はinclude構文の第二構文です:
 include ファイル名 as 名前空間識別子

これは単なるinlclude構文に見えますが、名前空間識別子が定義されておりグローバルシンボルの付けられたインクルードファイルをメインファイルから参照できます。 これはこれらシンボル参照の曖昧さを排除するには必要であり、またコードの可読性が向上すると感じるかもしれません。 詳しくはスコープルールを参照してください。


2.6.2 with / without

これらの特殊構文はEuphoriaがあなたのプログラムを内部形式に翻訳する場合に影響を及ぼします。これはあなたのプログラムのロジックは変更しませんが、プログラムを実行することにより得られる診断情報に影響があります。詳しくは3. デバッグとプロファイルを参照してください。

with
オプション設定のうちひとつを有効にできます: profile, profile_time, trace, warning または type_check があります。warning および type_check オプションは初期値では有効であり、同時に profile, profile_time および trace は初期値では無効です。

これよりプログラム実行完了後に結果として様々な警告がスクリーンに表示されます。あまり重要ではない問題を警告は提示します。警告によってプログラムの実行が中止されることは決してありません。

without
上述したオプション設定のうちひとつを無効にできます:

with の後にめったに使用されない特殊な with のオプションとしてコードナンバーが現れる場合があります。以前のリリースにおいてこのコードはファイルから除外されており古い""パブリックドメイン""版では構文数を増やすためにRDSによって利用されていました。

任意の設定を組み合わせたり設定変更できますが、設定変更はサブルーチンので行う必要があり、サブルーチン内で行うことはできません。唯一の例外としてプログラムのプロファイリングを行うためにひとつ有効にできるということです。

インクルードファイルwith / without の設定を継承しインクルードされた時点で影響を受けます。これらはインクルードファイルにて設定を変更できますが、インクルードファイル(のスコープ)が終わると元の状態に復帰します。インクルードファイルとその他のインクルードファイルの警告を無効(初期値)にしても、メインファイルでは無効になりません。

 

3. デバッグとプロファイル に続きます...