文字列の比較


COMPARE

オプションであるから定義されているとは限らないが、2つのaddr len文字列について、その内容が一致するかどうかを判定するforth標準ワードがCOMPARE である。
COMPARE  ( c-addr1 u1 c-addr2 u2 -- code )
比較は大文字小文字が区別される(case-sensitive)。
結果は、完全に等しいときには0を返す。文字列の長さが異なり、短い方の内容が長い方の一部として完全に含まれている場合、u1の方がu2より小さいとき-1、それ以外なら1を返す。短い方の文字列についても不一致がある場合には、最初の不一致文字について、c-addr1 u1で特定される文字列に含まれる文字の方が小さいとき-1、そうでないとき1を返す。
出力コードがやや複雑であるので分岐で書くと:
  • 文字列内容が完全に一致 -- 0
  • 文字列内容が一致しない
    • 一方が他方を、その先頭部分として含む場合。
      • 前の方が短い -- −1
      • 前の方が長い -- 1
    • 不一致文字があるとき
      • 最初の不一致文字について、先の文字列の文字の方が小さい -- −1
      • 最初の不一致文字について、先の文字列の文字の方が大きい -- 1
である。要するに、完全に一致すれば0であり、そうでなければ1か−1、−1になるのは先の文字列の方が"小さい"とき、である。つまり、アルファベット順を前提にすると、辞書的順序で、前の文字列が早い位置なら-1、前の文字列が後の位置なら1、ということである。

CMPSTR

COMPAREは、Mopsではデフォルトでは定義されていないが、string+というオプションファイルをロードすれば、CMPSTRというワードが定義され、上のCOMPAREの拡張になっている。CMPSTRの入力と出力コードはCOMPAREと同じであるが、大文字小文字の別を操作できる。すなわち、case?というVALUEをフラグとして、これにtrueを格納すれば、COMAREと同じく、大文字小文字を区別して比較する。case?がfalseなら大文字小文字を区別せず比較する。case?フラグはデフォルトではfalseである。

S=

なお、Mopsではもっと簡易な文字列比較ワードとしてS=が定義されている。
S=  ( c-addr1 u1 c-addr2 u2 -- b )
これは単純に、一致するときtrue、それ以外はfalseを返す。この比較は常に大文字小文字は区別される。

文字列の印字


TYPE

文字列を画面上に印字するワードがTYPEである。先に述べたように、スタック上では文字列はアドレスと長さの2つの数値で表現されるのが一般である。したがって、TYPEは、その2つを入力として取る。
TYPE   ( addr len -- )  \ addr lenで特定された文字列を印字するのが効果
通常のforthの場合、印字はコマンド入力画面に行われる。Mopsではテキスト編集ビュー(コンソール)のキャレット位置から印字される。
ただし、PowerMopsまでは、TYPEはカレントポートに描画するという仕様であり、アプリケーションウィンドウに印字することもできたが、
iMopsでは、常にコンソールウィンドウのテキストビューに印字されるようになっており、それがないアプリケーションで実行はできない。iMopsで、
アプリケーションウィンドウに印字するには、TYPE以外の方法(CoreGraphicsシステムコールかTextViewのメソッド)を用いなければならない。
例えば、
: greeting   S" Hello world!"  type  ;
のようなワードを定義して実行すれば、画面に、Hello world!と印字されるだろう。

CR SPACE SPACES

文字列の印字の途中に、CRを実行すれば、改行が挿入される。また、SPACEを実行すれば、半角空白が挿入される。SPACESは、スタックから入力を1つとって、その個数分の半角空白を挿入する。負の数の場合は何もしない。
CR     ( -- )   \ 印字中に改行
SPACE  ( -- ) \ 印字中に空白を挿入
SPACES ( u -- ) \ 印字中にu個の空白を挿入

."

ドット(ピリオド)と二重引用符のこのワード." は、初めから印字を目的とした文字列をリテラルとしてコンパイルする。使い方はS"と同じく、二重引用符 " を区切り文字として、そこまでを文字列として取る。これは、何かのワード定義内で用いられることを前提とするが、そのワードが実行されたとき文字列が印字されることになる。
."   コンパイル時: ( “ccc<quote>” -- ) 実行時:( -- ) \ コンパイル時は文字列を取り、実行時はそれを印字。<quote>は最後が"であることを示す。
例えば、
: greeting  ." Hello world!"  ;
とすれば、上のTYPEを用いた例と同値である。

.(

ドットと左丸括弧からなるこのワード.( は、コンパイル時に文字列の印字を実行するものである。終わりの区切り文字は、右丸括弧) をとる。このワードは、コンパイル時に何かメッセージが必要な場合に使われるわけであるが、デバッグのような特別な状況でない限り、滅多に使わないように思われる。使用法としては、
: Comp-mess   .( Message in compiling) ;
のようにすると、このワード定義を入力するとすぐに、Message in compilingと印字される。このワードComp-messの中身は空っぽである。

文字列のスタック上での形式


既に述べたように、ForthおよびMopsでの、文字列のスタック上での表現形式は、文字列格納メモリー域のはじめのアドレスと文字列の長さの二つからなる、addr(ess) len(gth)形式である。しかし、これ以外にも、いわゆるパスカル文字列あるいはカウンテド文字列という表現形式も用いられる。これは、メモリー域の最初の1バイトに文字列の長さを表す数値を格納し、その直後から文字列を連続的に格納する形式で、スタック上では、メモリー域の冒頭のアドレス値一つで表現される。

Forth表現の文字列をPascal形式に変換する必要は、通常のプログラムではでてこないが、逆はありうる。

COUNT

上のような場合に、Pascal形式の文字列について、addr len形式に変換するワードがCOUNTである。
COUNT ( addr1 --- addr2 len) \ スタック上のPascal文字列表現をForth文字列表現に変換する。

ASCII文字とその印字


CHAR [CHAR]

英文字のASCIIコードを数値として使いたい場合がある。Forth標準では、そのために2つのワードが定義されている。

1つは解釈実行環境用のCHARである。
CHAR  ( “<spaces>name” -- uc )  \ 空白以外の文字を取り、そのASCIIコードをスタックに残す。
CHARは、間の空白は全て無視する。その後に置くのは、形式上は文字列でかまわない。但し、その先頭の文字だけのコードが出力となる。
CHAR  about \ -- 97 ('a'のコード)
もちろん、最初の1文字以外は無駄であるので、通常は一文字しか書かない。
CHARがワード定義内で用いられたときには、直後の文字を入力として受け取ること無く、ただワードCHARの呼び出しがそこにコンパイルされるにとどまる。それを含むワードが実行され、CHARが呼び出されたとき初めて、入力ストリームから文字を獲得しようとするのである。例えば、あまり意味はないが、
: CHAR-POWER  ( 'c' -- n )  CHAR dup * ;
CHAR-POWER a \ -- 97*97 = 9409
のようになる。

もう1つは、コンパイル環境用の[CHAR]である。
[CHAR]  コンパイル時: ( “<spaces>name” -- )  実行時:( -- uc ) \ コンパイル時に文字を取り、実行時にそのコードをスタックに置く
例えば、
: Case-Test   ( uc -- b )    [CHAR] a <  ;
CHAR D Case-Test  \ -- true
CHAR d Case-Test  \ -- false
のようになる。つまり、具体的にいえば、[CHAR]は、Case-Testの定義内でaのコードを取って格納しておき、Case-Test実行時にはそれを取り出して入力と大小を比較するわけである。

&

Mopsでは、拡張として、解釈実行状態ではCHARとして動作し、コンパイル状態では[CHAR]として動作するワードとして& が定義されている。
: Case-Test ( uc -- b )   & a < ;
& D Case-Test  \ -- true
& d Case-Test  \ -- false

UNICODE拡張

Forth標準ではオプションとして、文字コードを拡張したCHARおよび[CHAR]が規定されている。ユニコードを想定したものと思われる。しかし、これはオプションであるので、環境によっては対応していない場合もある。おそらく商用のforthとGforthは大丈夫ではないかと思われる。他方、Mopsでは、CHAR、[CHAR]や&は、半角英数文字(ASCII文字)を想定しており、2バイト以上で表現される日本語文字には対応していない。しかし、日本語文字は1文字の文字列として扱うことで、ほとんど制限とはならない。

なお、ワードとは関係ないが、ユニコード拡張に関連し、古典的にCHARは1バイト(8ビット)長をも意味していたので、混同を避けるために、文字型であって必ずしも1バイトではない場合には、XCHARと表示することがある。


EMIT

一文字(ASCII)を印字するワードはEMITである。
EMIT ( uc -- ) \ 渡されたコードに該当する文字をスクリーンに印字
例えば、あまり意味はないが、
: TYPE-A   [CHAR] A EMIT  ;
TYPE-A
とすれば、大文字のAが印字されるわけである。

BL

なお、頻用される半角空白のASCII値は、BLという定数として定義されている。
BL  ( -- $20 )




最終更新:2020年01月02日 22:16