コンテンツにスキップ

フロー制御

条件分岐

if / elseif / else 構文

式の結果が真であれば、以降に続く { } 内を処理する。条件式は ( ) で括ってもよい。

request
{
    if !i {
        "iは0である。"
    }
}

すぐ後に elseif 節を付加できる。これは if の判定が偽であった場合のみ処理される。いくつでも連結できる。また、if〜elseif の最終端には else 節を置ける。これは先行する if および elseif の判定がすべて偽であった場合に処理される。

request
{
    if !i {
        "iは0である。"
    }
    elseif i == 5 {
        "iは5である。"
    }
    elseif "A" _in_ TOUPPER(i) {
        "iは文字列で、aもしくはAを含んでいる。"
    }
    else {
        "iは0でも5でもaを含む文字列でもない何物かである。"
    }
}

if、elseif、else が処理する { } 内にスクリプトが 1 つしか存在しない場合は、{ } を省略できる。したがって上の例は次のように書き直せる。

request
{
    if !i
        "iは0である。"
    elseif i == 5
        "iは5である。"
    elseif "A" _in_ TOUPPER(i)
        "iは文字列で、aもしくはAを含んでいる。"
    else
        "iは0でも5でもaを含む文字列でもない何物かである。"
}

ただし if が重なっている場合は、{ } の省略はできない。次は C 言語では正しいが、YAYA では誤り(読み込み時にエラーになる)。

if i == 0
    if j == 0
        "iとjはともに0である。"

次のように { } が必要。

if i == 0 {
    if j == 0
        "iとjはともに0である。"
}

case / when 構文

ラベルベースの分岐。case 文が式を評価し、一致するラベル値を持った when 節が実行される。

request
{
    case i {
    when 0 {
        "iは0である。"
    }
    when "A"
        "iは文字列Aである。"
    others {
        "iは0でもAでもない。"
    }
    }
}

others 節は、すべてのラベルに合致しなかった場合に実行される部分。others は省略でき、省略した場合は何もしない。

ヒットさせるラベル値はコンマで列挙できる。また、マイナス符号でヒットさせる範囲を指定することもできる(例:"value1"-"value5")。

request
{
    case name+(i+1) {
    when "Pentium3","Pentium4"
        "Pen!!!は1999年、Pen4は2000年発売発売。"
    when "Pentium5"-"PentiumX" {
        "まだ無い。"
    }
    others
        "分からない。"
    }
}

when に記述するラベル値は必ず即値でなければならない。変数や関数、演算子を含む数式は書けない。when、others が処理する { } 内にスクリプトが 1 つしか存在しない場合は、if と同様に { } を省略できる。

一般形は次のとおり。

case 式
{
  when 値1
  {
    // 値1の場合
  }
  when 値2, 値3
  {
    // 値2または値3の場合
  }
  others
  {
    // どれにもマッチしない場合
  }
}

when と others は、case 式 に続く { } の中に書く。case 式 の直後に { がなく when が続く書き方は、読み込み時にエラー(E0051 caseに続く{がありません)になる。case 式 { のように { を同じ行に書いてもよい。

switch 構文

{ } 内の出力候補からは出力が無作為に選ばれるが、switch を使用すると、選択する候補を位置で指定できる。

request
{
    switch id {
        "idは0である。"
        "idは1である。"
        {
            "idは2である。"
            "idはtwoである。"
        }
        "idは3である。"
    }
}

変数 id の値によって、出力される文字列が指定される。指定は 0 オリジン。id が 2 のときは、"idは2である。" もしくは "idはtwoである。" が出力される。この内包された { } 部分では、選択は無作為。

switch が評価する値に対応する候補が { } 内に存在しない場合は、空の文字列が出力される。たとえば上の例で id が 100 だった場合は、空の文字列が出力される。

{ } の中の候補は 1 行に 1 つ書く。switch 1 { "a" "b" "c" }(各候補を行に分けて書く)は "b" になる。

{ } を候補の数だけ並べて、ブロックごとに位置を対応させる書き方もできる。この場合、位置に対応するブロックの中の候補からランダムに選ばれる。

switch _n
{
  "ゼロ番目"
}
{
  "一番目"
}

switch 内に出力確定子(--)がある場合は、各ブロックの該当する位置にある候補が選択される。

request
{
    switch 1 {
        "かわいい"
        "天才"
        "サル"
        --
        "とは言い難い"
        --
        "ですね。"
        "かもしれません。"
    }
}

request の出力は "天才かもしれません。" になる。中間のブロックには指定位置に候補が無いので、出力が空の文字列になっていることに注意(関数)。

ループ構造

while、for、foreach の 3 種類のループ構造がある。

基礎設定ファイルの looplimit でループ回数の上限を設定できる。足りない場合は変更すること。標準は 1000000 回(Tc574-10 / Tc606-4以降。それまでは 10000 回)。限界に到達したときは shiori.OnLoopLimit(関数名, 行) が呼ばれる。

while ループ

while が評価する式が真である間は、{ } 内を繰り返し処理する。条件式はカッコで囲んでも囲まなくてもよい(while (_i < 3) と while _i < 3 は同じ動作になる)。

while 条件式
{
  // 処理
}
request
{
    _i = 1
    _j = 0
    while _i < 11 {
        _j += _i
        _i++
    }

    "1から10をすべて足すと%(_j)である。"
}

上の例は while の機能を簡単に説明している。次の例では、異なる 10 個の文字列を発生している。request の出力は、1〜10 のうちいずれかの平方根を報告する文字列になる。

request
{
    _i = 1
    while _i < 11 {
        "%(_i)の平方根は%(SQRT(_i))である。"
        _i++
    }
}

for ループ

for は while と同様の先判定ループ構造だが、初期化式、脱出判定式、ループごとに実行する式を一箇所にまとめて書ける点が優れている。C 言語と異なり、すべてのパートが必須。for ( ; ; ) のような無限ループは使えない。

for 初期化 ; 条件 ; インクリメント
{
  // 処理
}

次は、while で挙げた平方根を報告する例を for で書き直したもの。

request
{
    for _i = 1; _i < 11; _i++ {
        "%(_i)の平方根は%(SQRT(_i))である。"
    }
}

_i = 1 はループを始める直前に実行される。_i < 11 はループ判定式で、これが真である間はループが続く。_i++ はループの一単位が完了して先頭へ戻るときに実行される式。

無限ループを作る場合は for 1;1;1 などとする。ただし while 1 で済むため、無限ループを作るときは、可読性の点からも動作速度の点からも while を使うべきである。

Tc603-6以降では、ヘッダ全体を括弧で囲んでもよい。foreach も同じ。

for (_i = 0; _i < 3; _i++) { ... }
foreach (_a; _k, _v) { ... }
  • ヘッダを複数行に分けてもよい。for ( のように ( だけで終わる行や、) { のように ) から始まる行も書ける
  • 文ごとに括弧を閉じる書き方(for (_i = 0); (_i < 3); (_i++))や、括弧なしの書き方は、これまでどおり使える
  • for (_i = 0; _i < 3); _i++ のように、最後の文より手前で括弧を閉じる書き方は、これまでどおりエラーになる
  • for (; _i < 3; _i++) や foreach (_a; ) のように、括弧を外すと空になる部分があるときは、E0027 だけが出る(後続の連鎖エラーは出ない)

Tc603-5 までは、ヘッダ全体を囲む括弧が別々の文に分かれて、E0019 などのエラーになっていた。for と foreach の条件式を ( ) でくくるとエラーになるのは、このバージョンまでの仕様。

foreach ループ

簡易配列、もしくは汎用配列の各要素値を順番に取り出す(SETDELIM で設定したカスタムデリミタも認識される)。

foreach 配列 ; 変数
{
  // 変数に要素が格納されて実行
}

次の例では、簡易配列の要素値を取り出して数値へ変換し、すべての合計を計算している。

request
{
    _str = "1,3,5,7,9"
    _t   = 0
    foreach _str; _i {
        _t += TOINT(_i)
    }
    _t
}

foreach に続けて処理対象を記述する。上の例では簡易配列 _str を指定している。次に書かれた _i は、取り出された要素値を格納する変数で、必ず変数でなければならない。処理対象の変数のデリミタが SETDELIM によって変更されていても、foreach はそのデリミタを認識して正常に動作する。

foreach は汎用配列も処理できる。

request
{
    _sent = ("I", "am", 31, "years", "old.")
    _t   = ""
    foreach _sent; _i {
        _t += (_i + " ")
    }
    _t
}

request は "I am 31 years old. " を出力する。

foreach ループ内で、要素取り出し対象の簡易配列・汎用配列を書き換えてもかまわない。その場合でも、列挙は書き換え前のデータを対象とする。

Tc600-3以降では、ハッシュも foreach で回せる。変数には値がキーの順に入る。値が配列やハッシュなら、変数も配列やハッシュになる。

2変数の foreach(Tc600-3以降)

変数をコンマで区切って 2 つ書くと、1 つ目にキー、2 つ目に値が入る。配列と簡易配列でも使え、その場合の 1 つ目には 0 から始まるインデックス(整数)が入る。

foreach 配列またはハッシュ ; キー変数, 値変数
{
  // 処理
}
_h = IHASH("a", 1, "b", 2)
foreach _h ; _k, _v { _s += _k + '=' + _v + ' ' }   // "a=1 b=2 "
foreach ("p", "q") ; _i, _v { ... }                  // _i = 0, 1
foreach "x,y" ; _i, _v { ... }                       // 簡易配列も同じ

変数にはローカル変数とグローバル変数のどちらも書ける。変数が 3 つ以上のとき(_a, _b, _c)や、変数でないものを書いたとき(_k, 1、_a + _b)はエラー E0043 になる。

break

ループ中に break が現れると、現在実行中の最も深いループから脱出する。

request
{
    _j = 0
    for _i = 0; _i < 100; _i++ {
        _j = _i*_i
        if  _j >= 100
            break
    }
    _i - 1
}

上の例では、for は初期値 0 の _i が 100 に達するまでループを実行しようとする。しかし、ループ内には「_i を二乗した結果が 100 以上になったらループから抜ける」ように break が仕込まれている。したがって、実際には _i = 10 の時点でループが終了する。request は _i から 1 を減じた値を返すので、二乗した結果が 100 を超えない最大の整数(9)を求める関数になっている。

continue

ループ中に continue が現れると、その位置からすぐにループ先頭へ戻り、次のループ反復に進む。

request
{
    _j = ""
    for _i = 0; _i < 3; _i++
    {
        _j += "go "
        if  _i > 0
            continue
        _j += "ahead "
    }
    _j
}

_i は 0、1、2 と変化するが、1 と 2 では continue が働くので、_j に "ahead " を追加する式が実行されない。したがって request の出力は "go ahead go go " になる。

関数の戻り値

return

return が現れると、その関数の実行はそこで終わる。関数の出力は、それまでに蓄積された候補から選ばれる。

to_rad
{
    if GETTYPE(_argv[0]) == 3 {
        -1
        return
    }
    _argv[0]*2.0*3.14/360.0
}

関数 to_rad は degree を radian へ変換する。引数に文字列が与えられた場合は、if の判定でそれを発見して -1 を返すようにプログラムされている。return の時点で出力の候補は -1 しかないので、これが出力される。

引数つき return(Tc573-1以降)

return 式 の形で戻り値を明示できる。引数には定数だけでなく数式も書ける。

to_rad
{
    if GETTYPE(_argv[0]) == 3 {
        return -1
    }
    return _argv[0]*2.0*3.14/360.0
}

引数つきの return は、それまでに蓄積した候補をすべて破棄して指定した値に置き換える。次の例では返り値は必ず "D" になり、"A" "B" "C" は破棄される。

test_func
{
    "A"
    "B"
    "C"
    return "D"
}