コンテンツにスキップ

文字列内埋め込み要素の展開

概要

文字列の中に変数や関数を埋め込んで、これらの実行結果を当該位置へ挿入できる。埋め込み方は「範囲付き展開」と「名称最長一致展開」の 2 種類。

範囲付き展開

要素を %( ) で囲んで埋め込む。

request
{
    _i = "pen"
    "This is a %(_i)."
}

request を実行すると、"This is a pen." が出力される。%( ) はいわゆる eval(文字列をスクリプトコードと解釈して実行する)のような動作を行う。単一の関数や変数だけでなく、数式も埋め込める。

request
{
    "1+2+3は%(1+2+3)です。"
}

request は "1+2+3は6です。" を出力する。

%( ) の中にはダブルクォートを含められない。したがって、文字列を含む数式は埋め込めない。次はエラーになる。

request
{
    "This is a %(_i = "pen")."
}

このような場合は、埋め込みを使用せず通常の数式として結合する。

request
{
    "This is a " + (_i = "pen") + "."
}

ブラケット ( ) による演算順序制御は、範囲付き展開でも同様に働く。次の例を見てほしい。

request
{
    "遊星「%(_i = planet)」は遠い。この遊星は%(color(_i))色をしている。"
}

planet
{
    "mars"
    "saturn"
    "pluto"
}

color
{
    case _argv[0] {
    when "mars";   "red"
    when "saturn"; "yerrow"
    when "pluto";  "blue"
    others;        "unknown"
    }
}

最も ( ) が深いのは color(_i) の引数なので、_i = planet の前に color が呼び出されてしまい、_i がまだ空のため "unknown" になる。ブラケットを過剰に付与して演算順序を調整する。

"遊星「%((_i = planet))」は遠い。この遊星は%(color(_i))色をしている。"

これで矛盾のない文字列が得られる。

特徴:

  • 複雑な式や数学演算をサポートする
  • 文字列内にダブルクォートを含められない
  • 辞書の読み込み時に静的に解析されるため、DICLOAD などで後から定義した関数は認識されない。どうしてもそのような使い方をしたい場合は、EVAL の利用を検討すること

名称最長一致展開

( ) を付けず、単に % のみでも埋め込み展開は機能する。

request
{
   o      = "pen"
   obj    = "eraser"
   object = "world"
   "This is a %object."
}

obje
{
    "television"
}

展開対象は、% 以降の文字列に一致する最も長い名前を持った変数 / 関数になる。上の例の場合、もっともよく一致するのは変数 object なので、これが採用される。結果は "This is a world." になる。

変数は刻々と作成されたり消えたりするので、展開対象が状況によって変化する。これは範囲付き展開には見られない特徴。

request
{
   val   = "red"
   trans
   --
   value = "blue"
   trans
}

trans
{
    "%value"
}

trans は 2 度実行されるが、最初と 2 度目では "%value" の動きが変わる。最初は変数 val + "ue"、2 度目は変数 value と解釈される。request の出力は "redueblue" になる。

展開結果の再利用

%[ ] という書式で、過去の展開結果を再利用できる。

request
{
    "「%planet」は遠い。「%city」も遠い。もっとも%[1]になら行けなくもない。"
}

planet
{
    "mars"
    "saturn"
    "pluto"
}

city
{
    "newyork"
    "moscow"
    "madrid"
}

%[i] は 0 オリジンで i 番目の展開結果を得る。上の例では、%[0] が %planet の、%[1] が %city の展開結果を示している。

%[ ] は範囲付き展開では使えない。範囲付き展開で過去の結果を再利用したい場合は、それを変数へ入れる。

速度

名称最長一致による展開は、実行するたびに展開対象を検索しなおすので、範囲付き展開と比べて動作速度が劇的に遅い。本当に必要な場合は別として、通常は範囲付き展開を利用すること。

名前を読み込み時に確定する

名称最長一致展開は、実行した時点で存在する変数で名前を探す。そのため、URL などの % を含む文字列が、ある時点から急に置き換わることがある。

"https://example.com/?p=100%off"  // 変数 o や of が作られると、その値に置き換わる

設定ファイルに embed.lazy, off と書くと(Tc604-1以降)、カッコなしの % の後の名前を辞書の読み込み時に確定する。

  • 名前は、ユーザー関数・システム関数と、辞書の式に名前が書かれたグローバル変数(EVAL などで実行した式も含む)の最長一致で決める。セーブファイルから復元しただけの変数や、LETTONAME で作っただけの変数は対象にならない。こうした変数を埋め込むときは %(名前) と書く
  • 確定した変数がまだ値を持っていないときは、空文字列になる(%名前 のまま残らない)
  • どの名前とも一致しなかったものは、% を含めてそのまま文字列として扱う。英字や全角文字で始まるものには警告 W0031 を出す。%E3 のような 16 進数 2 桁(URL エンコード)や、数字・記号で始まるものは警告しない
  • ローカル変数(%_name)は、従来どおり実行時に名前を探す
  • %[i] の番号は、文字列のまま残ったものも 1 つと数える(embed.lazy の設定で番号は変わらない)