追加案: 計算可能な汎用変数の追加

Issue #400 resolved
k4nagatsuki repo owner created an issue

たぶん実現はWsn.2よりずっと後の話になるのではないかと思うのですが、端緒として立てておきます。

CardWirthにはフラグとステップという2種類の状態変数があります。これはプログラミングの知識が無くても取り回しが簡単で、それがシナリオの作りやすさに寄与している面も大きいと思いますが、少し複雑な事をしようとすると以下のような欠点が露呈します。

  • 大きな桁の数値を作るのが簡単ではありません。
  • 計算は非常に困難です。
  • 工夫すればある程度文字列を操れますが、柔軟な操作はできません。
  • カードの配置位置など様々な場面で使用する事ができません(それらの値は固定値で指定しなければなりません)。

これら全てを克服するには、いわゆるVariant変数のような、数値も文字列も、必要なら実数も扱え、しかも数値や文字列が必要となるデータから参照可能な、汎用的な種類の状態変数を1つ追加すればよいはずです。

しかし、その実装までには、いくつか困難な仕様上の決断を経る必要があります。

  • 計算はどのような方法と構造で実現するか。エディタでの編集はどのようにできればよいか。
  • 関数はどうするか。どのような関数を用意すればよいのか。
  • 文字列としてカードや称号の名前を取得するような機能を追加するとして、どこまでできるようにするべきか。

これはどうあがいても仕様衝突の可能性が非常に大きくなる機能ですから、慌ててもどうせ実装はできません。じっくり考えていけばよいと考えてはいます。

今の状況では仕様を考えても実現できないという事を鑑みて、優先度は少し低めにしておきます。

Comments (22)

  1. k4nagatsuki reporter

    issue #604のご意見も来ましたし、そろそろ具体的な事を考えるべきかと思います。

    汎用変数は、ゴールがかなりはっきりしている分、最初から完全なものを作ろうとせず、段階的に実装していく事が可能です。

    草稿にもならないものですが、以下に最初期段階の考えを記しておきます。

    1. 汎用変数は、フラグ・ステップとは、「値に名前をつけられない」という点で異なるので、それらの拡張ではなく独立したものとして作るべきです。
    2. 文字列も数値も扱えるべきです。
    3. 最低限、2つのイベントコンテントの追加が必要です。
      1. 演算コンテント。式を文字列で指定してあらゆる操作が行えるようにする。代入もこれで行う。
      2. 分岐コンテント。比較演算などの結果で2岐分岐を行う。
    4. いくつかの関数を使えるようにするべきです。以下はExcel等で使える関数を参考にしています。
      1. MAX・MIN関数は、数値ステップからの変換先として汎用変数を使う場合には必要です。
      2. 文字列の一部を取り出すLEFT・MID・RIGHT関数はほしいところです。
      3. 文字列を数値にする関数もあった方がいいです。
    5. 四則演算+剰余、比較、文字列連結に加え、andとorだけでも論理演算子が必要です。
    6. フラグ・ステップとは、最低限代入によって値のやり取りを行えるべきです。

    以下は検討して決めなくてはならない事です。

    1. 文字列の減算をした時など、計算エラーが起きた場合はどうする? イベントの中止か?
    2. 整数と実数の取り扱いをどうする? 整数同士の割り算は実数の結果を出すべきか?

    以下は、一番最初の段階では手をつけなくてもいいかと思っている事です。

    1. エディタでの式の記述の支援。初心者でも簡単に記述できるようにするべきですが、必須ではないので、後回しに刷る事は可能です。
    2. 分岐コンテント以外での使い道。たとえばカードやセルの配置情報に使うなど。
  2. k4nagatsuki reporter
    • 代入は「フラグ代入」「ステップ代入」で代入元に指定できればよさそうです。
    • 初期値をどうするか。当面、初期化に関数や式は使えないようにしてしまってもよさそうに思えます。さもないと循環参照などのややこしい問題が出てしまいます。
    • CWでは実数はほとんど出番がありません。当面は整数演算だけでよさそうに思えます。しかし整数演算はプログラマならぬ普通の感覚からすると不自然でもあります。
    • 計算エラーはイベント中止にしてしまってもよさそうです。後付でオプション化も可能です。
    • 整数・文字列の他に真偽値もほしい感じがします。

    フラグやステップの値の取得などの処理に関しては、専用の関数を用意すればよさそうに思えます。

    たとえばフラグ「A」の値を取る時:

    FlagValue("A")
    

    というようにできます。

    このやり方のメリットは、拡張が容易な事です。たとえばフラグの値の文字列の取得は:

    FlagText("A")
    

    というように簡単に定義できますし、特定の値の時の文字列を得るように拡張するのも簡単です:

    StepText("A", 4)
    

    一方で、汎用変数そのものは$NAMEのように専用の書式を用意してもよさそうです。

  3. k4nagatsuki reporter
    • 実数について。やはりあった方がよさそうです。まともな算数が通用するようになるという事の他に、三角関数のような便利な道具が将来導入できるというメリットがあります。私はExcelとCWXスクリプトを使って円弧を描くようにセルアニメを行うという事例を見た事があります。整数演算を行いたい向きにはINT関数を提供できます。
    • 真偽値について。整数を真偽判定で使えるようにすると、比較演算の結果を引き算したりするような火遊びじみた事ができてしまうので、やはり安全のために必要です。値はTRUEFALSEです。

    実は最も難しい問題は、この汎用変数の名前をどうするか、という事です。

    実はプログラミングの世界にはこのような変数を指す言葉がすでにあります。バリアント(Variant)というのですが、これがCWの世界ではすでに別の意味で使われてしまっています。

  4. k4nagatsuki reporter

    今考えている具体的な仕様です。

    • 汎用変数(仮)を定義できるようにする
    • 初期値にいずれかを設定可能にする
      • 整数または実数(小数点の有無で判定)
      • 文字列("で囲う)
      • 真偽値(TRUEFALSE)
    • 以下のコンテントを用意し、式を記入可能にする
      • 汎用変数(仮)演算コンテント … 式の処理結果を使用しないもの
      • 汎用変数(仮)分岐コンテント … 式の処理結果を真偽値として分岐を行うもの
    • 式内で大文字・小文字は区別しない
    • 以下の演算子を用意する。~=<>以外は文字列をが対象となったらエラー
      • + … 加算
      • - … 減算
      • * … 積算
      • / … 除算
      • % … 剰余
      • ~ … 連結。左右の値は文字列化される
      • or … 論理和。左右の値が真偽値でなければエラー
      • and … 論理積。〃
      • < … 未満
      • <= … 以下
      • > … 超過
      • >= … 以上
      • = … 一致。左右値のどちらかが文字列であれば文字列化比較する
      • <> … 不一致。〃
    • 以下の関数を用意する(str=文字列, n=数値, bool=真偽値, ?=式)
      • LEN(str) As n … 文字列の長さを返す。
      • LEFT(str, n) As str … 文字列の左側を取り出す。LEN(str) < nの場合は全て取り出す
      • RIGHT(str, n) As str … 文字列の右側を取り出す。〃
      • MID(str, n1, n2) As str … 文字列のn1からn2文字を取り出す。n1は1から開始する。文字数をオーバーした場合はn1以降を全て取り出す
      • STR(?) As str … ?を文字列化する
      • VALUE(?) As n … ?を数値化する。数値化不能な文字列であればエラー
      • INT(?) As n … ?を整数化する。数値化不能な文字列であればエラー
      • IF(bool, ?1, ?2) As ? … boolがTRUEであれば?1を、そうでなければ?2を返す
      • FLAGVALUE(str) As bool … strが指すフラグの値(真偽値)を返す
      • FLAGTEXT(str[, bool]) As str … strのフラグの値boolの文字列を返す。boolを省略した場合はFLAGTEXT(str, FLAGVALUE(str))と同様に動く
      • STEPVALUE(str) As n … strが指すステップの値(数値)を返す
      • STEPTEXT(str[, n]) As str … strのステップの値nの文字列を返す。boolを省略した場合はSTEPTEXT(str, STEPVALUE(str))と同様に動く
    • 式には$dir\nameという書式で汎用変数(仮)パスを記入できるようにする
    • フラグ代入・ステップ代入に、汎用変数(仮)の値が数値であれば代入する機能を追加
      • フラグ代入では真偽値でなければ何もしない
      • ステップ代入で実数は整数化する
      • ステップ代入で上下限オーバー時は丸める
    • 計算エラーが発生したり、分岐コンテントの結果が真偽値以外の時は、エラーとする

    名前とエラー処理のみ、まだ考え中です。どちらかというと、エラー値という特別な値を用意して伝播させた方がよさそうな気がします。

  5. k4nagatsuki reporter

    この機能はWsn.3での実装を無理に目指す事はないと思っていますが、Wsn.4までは目標にしたいです。

    おそらくこのような機能がCWに存在する事そのものに嫌悪感を示す人もいるはずです。議論を煮詰めるだけの時間が必要です。

  6. k4nagatsuki reporter

    名前についてですが、いわゆる普通の変数という意味での「コモン」辺りがいいんじゃないかなぁと思っています。カタカナ名であれば、フラグやステップと並べても違和感は無いはずです。

    定数でないという事で、変数を意味する「バリアブル」を持ってきてもいいかもしれません。

  7. req

    私は、汎用変数よりも先に数値ステップを 実装すべきと思います。 <Value>Step-0</Value> <Value>Step-1</Value> ... の行はかなり重たく、 シナリオ起動時に負担が大きいです。

    NEXTからPyへのシナリオ移行を視野に入れるためにも よろしくお願い致します。

  8. k4nagatsuki reporter

    上の方にも書きましたが、この機能の具体的な検討はissue #604に応じて進めています。変換に関していえば、この機能があれば数値ステップは不要という事です。

  9. k4nagatsuki reporter

    メッセージ等に変数値を出す方法を考えなくてはなりません。新書式(#714)では予め用意できるのであまり悩む必要はありませんが、新書式の実装はいつになるか分かりませんし、旧書式で表示できないようでは不便です。

    幸い、CWのメッセージは、$存在しないステップ$のように存在しない変数を指定すると、指定文字列がそのまま表示される仕様です。汎用変数は古いバージョンのシナリオには存在しませんから、古いバージョンのメッセージでは必ず指定文字列がそのまま表示される事になり、互換性問題は発生しません。好きな記号を使う事ができます。

    特殊文字ですでに使われている記号は#$%&、パス区切り文字として\です。余っている記号は!"'()-~[]{}@*:;+<>?/辺りでしょうか。この中では@$%と並べても違和感が無さそうに思います。

  10. k4nagatsuki reporter

    手元で式の試験的な実装を作ってみたのですが、真偽値反転の演算子を忘れていました。notでよいかと思います。

  11. k4nagatsuki reporter

    式の中で汎用変数値を読む際、$dir\nameという書式ではうまくいきません。というのも、CWの状態変数には好きな名前をつけられるので、文字列のように左右を囲わない場合、構文解析が非常に難しくなってしまうからです。

    そもそもフラグやステップは動的にパスを生成してFLAGTEXT(<path>)のようにできるのですが、汎用変数もそうした読み方ができるべきです。

    VAR("dir\name")のように関数で変数値を読めるようにし、その構文糖として$"dir\name"だとか{dir\name}のような書き方ができる、という風にするのがおそらく自然です。

    書式の要件はパスの左右を囲える事と、ただも文字列と区別が可能な事です。$"dir\name"{dir\name}のどちらでもよいわけですが、パスが文字列である事を考えると、それを意識した前者の方がよさそうな気がします。もう少し考えてみます。

  12. k4nagatsuki reporter

    ステップと同等の事をするには次の関数も必要です:

    • STEPMAX(str) As n … strが指すステップの最大値を返す
    • MAX(n, ...) … 可変個のnの中から最大の数値を返す
    • MIN(n, ...) … 可変個のnの中から最小の数値を返す

    ステップ判定コンテントに対応する次のコンテントも必要になります:

    • 汎用変数(仮)判定コンテント … 式の処理結果が真であれば以降のイベントツリーが出現する
  13. k4nagatsuki reporter

    演算子の優先順位です:

    1. +(単項) -(単項)
    2. / * %
    3. + - ~
    4. <= >= <> < > =
    5. not
    6. and
    7. or

    notがC言語の!等と異なってかなり低いのは、BASICやPythonの仕様に倣っての事です。これにより、not 1 <> 2のような式を括弧無しで書けるようになります。優先度の高い否定演算子が必要になった時には!演算子を用意する余地があります。

  14. k4nagatsuki reporter

    pull request #2403

    以上を踏まえた試験的な実装です。

    今はエンジン側の実装で精一杯なので、エディタ側の作業は全くしていません。そのうち時間のある時にやります。

    テキストエディタの手作業でシナリオを編集してわずかながらのテストを行いました。そのシナリオを添付しておきます。

    デバッガでコモンの値を編集する事が可能です。

  15. k4nagatsuki reporter

    pull request #2411

    エディタ側の実装を行いました。

    その過程で気づきましたが、フラグやステップへの代入は、代入コンテントではなくコモン設定コンテントで行った方が整理がついてよさそうです。フラグ・ステップのために用意されたコンテントにコモンの取り扱いを追加すると混乱を助長する可能性があります。とりあえず、対応を取り除いておきます。


    $"path"という形のコモン参照ですが、メッセージコンテントでの記号に合わせて@"path"にした方がいいかもしれません。予定はしていないにせよ、将来フラグやステップの値を簡単に参照する方法がほしくなる事もありえます。

    特に実装上の問題はないはずなので、これから変更してみます。

  16. k4nagatsuki reporter

    以上で基礎部分は実装まで完了です。

    関数の追加など新規の提案を行う場合は、個別のIssueを立ててください。

    基礎部分の変更の提案は、このIssueにお願いします。

  17. k4nagatsuki reporter

    pull request #2413

    文字の任意位置から右側を取る手段が無かったので、MID関数の長さ省略で対応しました。

  18. Log in to comment