An English summary is available here.
ここではプログラミング言語\(\mathbb{Q}_p\)の仕様を自然言語ベースで曖昧に導入します。\(\mathbb{Q}_p\)は計算可能関数の定義を書きやすくかつ読みやすくするために
- 通常の数学の流儀に近い構文を採用すること
- C++コンパイラでコンパイルできること
- 定義文を自然言語に自動翻訳できること
のみを重視して開発したプログラミング言語です。なお現在サポートしている自動翻訳は日本語、英語、中国語、フランス語、ドイツ語です。またC++への翻訳を緩い制約下で可能にしているため、その制約の下でなら実際に実行することが可能です。ただしあくまで定義の書きやすさと読みやすさを目的としているので、実行速度にはこだわっていません。
また今後も開発を続けていく予定なので、こちらに記載された仕様は後に変更になる可能性が十分にあります。
0. コード例
構文の説明に入る前に、実際のコードを見てみましょう。
USE( int , x );
IMP( bool , r , x );
SPEC( S , x , r );
USE( S , y );
USE( bool , b1 );
USE( bool , b2 );
USE( string , s1 );
USE( string , s2 );
IMP( int , g , s1 , s2 );
SET( g , SYMBOL , STR( \\Gamma ) );
SET( g , SEPARATOR , SYMBOLISE( g ) + LBIG + LPAREN + LMAT(c) + SPACE , SPACE + YY + SPACE , SPACE + RMAT + RBIG + RPAREN );
IMP( int , f , s1 , s2 , b1 , y );
SET( f , SEPARATOR , SYMBOLISE( f ) + SUB + LBRACE , RBRACE + SUP + LBRACE , RBRACE + LPAREN , COMMA , RPAREN );
DEF( f ){
PUT( b2 , s1 == s2 ) ,
IF( b2 >> ( ( ! b1 && b2 ) || b2 ),
IF( ( b1 << ( b2 || b1 ) ) , PUT( x , 1 ) ) ,
// ここを通るのはバグ
IF( ! ( ( b1 && b1 ) ->* b1 ) , EXIT ) ,
IF( b2 , RETURN( x + y ) )
) ,
RETURN( y + g( s2 , s1 + s2 ) )
};
WRITE( S , JAPANESE , FANDOM , a.txt , TRUNC );
DISPLAY( f , JAPANESE );
WRITE( f , JAPANESE , FANDOM , a.txt , APP );
それぞれの行は以下のような意味を持ちます:
- USEで定義に用いる変数の型と記号を宣言している。
- IMPで定義に用いる演算子の戻り値の型と記号と引数を宣言している。
- SPECで型の部分型を定義している。
- SETとSYMBOLで翻訳時に表示される演算子記号の変更をしている。(オプション)
- SETとSEPARATORで翻訳時に表示される演算子記法(通常の関数記法、ポーランド記法、中置記法、逆ポーランド記法、添字による記法、等)やセパレータには何を用いるかを設定している。(オプション)
- DEFで定義を記述している。
- PUTで引数以外の変数の値を定義している。
- IFで場合分けを行っている。
- EXITで異常終了を記述している。
- RETURNで定義する演算子の戻り値を記述している。
- DISPLAYで宣言の木構造を標準出力している。
- WRITEで定義文の日本語訳をファイルに書き込んでいる。
下の画像は先程のコードを自動翻訳した日本語の文章です。上で説明した意味と整合的な翻訳になっていることが分かると思います。
1. 準備
細かい話に入る前に、導入方法や用語法や記法の説明をします。
1.1. 導入
無理に導入をしなくてもp進大好きbotにソースコードを送って下さればコンパイル結果や自動翻訳結果を返送しますが、以下が\(\mathbb{Q}_p\)の導入手順です。
- C++コンパイラ(なるべくバージョンが新しいGCC推奨)をインストールし、それを\(G\)と置く。
- 以下のurlからzipファイルをダウンロードし、それを解凍して得られるフォルダを\(r\)と置く。
- WindowsユーザーかつGCCユーザーの場合、\(\mathbb{Q}_p\)ライブラリからダウンロードする。
- 非Windowsユーザーまたは非GCCユーザーの場合、このレポジトリ全体を「Clone or Download」からダウンロードする。
以下が\(\mathbb{Q}_p\)のソースコードをコンパイルとリンクする手順です。
- \(\mathbb{Q}_p\)で許容される文字列を用意し、それを\(C\)と置く。
- 白紙のcppファイルを用意し、それを\(c\)と置く。\(c\)の存在するカレントディレクトリから\(r\)への相対パスからを
RP
と置く。例えば\(r\)がカレントディレクトリに存在する場合はRP
が.
であり、\(r\)がカレントディレクトリの親ディレクトリに存在する場合はRP
が..
であり、\(r\)がカレントディレクトリの子ディレクトリchild
に存在する場合はRP
が./child
である。\(c\)にライブラリヘッダを以下のファイルをインクルードし、適宜改行をする。- WindowsユーザーかつGCCユーザーの場合、\(c\)に
RP/libqp.hpp
をインクルードする。 - 非Windowsユーザーまたは非GCCユーザーの場合、\(c\)に
RP/cpp/Mathematics/Function/Computable/a.hpp
をインクルードする。
- WindowsユーザーかつGCCユーザーの場合、\(c\)に
- \(c\)の末尾に
int main(){ C; return 0; }
というテキストを追加し、保存する。ただし\(c\)が日本語を含む場合は文字コードをShift-JISに設定する。 - \(c\)を\(G\)で文字コード
-finput-charset=cp932 -fexec-charset=cp932
と十分新しい標準ライブラリ(例えば-std=gnu++1z
)を指定の下でコンパイルし、以下のようにライブラリをリンクすることで生成される実行ファイルを\(e\)と置く。- WindowsユーザーかつGCCユーザーの場合、
libqp.a
をリンクする。 - 非Windowsユーザーまたは非GCCユーザーの場合、
RP/cpp/Error/BreakPoint/a.cpp
RP/cpp/Error/FaultInCoding/a.cpp
RP/cpp/Error/IllegalAccess/a.cpp
RP/cpp/Error/IllegalCall/a.cpp
RP/cpp/Error/IllegalImput/a.cpp
RP/cpp/Error/MismatchType/a.cpp
RP/cpp/Error/Position/a.cpp
RP/cpp/Error/Warning/a.cpp
RP/cpp/Error/a.cpp
RP/cpp/Utility/String/a.cpp
RP/cpp/Mathematics/Arithmetic/Prime/a.cpp
RP/cpp/Mathematics/Function/Computable/Expression/Primitive/bool/a.cpp
RP/cpp/Mathematics/Function/Computable/Expression/Primitive/int/a.cpp
RP/cpp/Mathematics/Function/Computable/Expression/Primitive/string/a.cpp
RP/cpp/Mathematics/Function/Computable/Operator/Basic/bool/a.cpp
RP/cpp/Mathematics/Function/Computable/Syntax/Translation/a.cpp
RP/cpp/Mathematics/Function/Computable/Syntax/a.cpp
RP/cpp/Mathematics/Function/Computable/Type/a.cpp
を全てコンパイルし、全ての中間生成ファイルをリンクする。もしくはお近くのPCショップでWindowsを購入するかGCCをインストールする。
- WindowsユーザーかつGCCユーザーの場合、
\(C\)に記述するオプション次第で、\(e\)の実行時に\(C\)内の各種定義文を標準出力させたり自然言語に翻訳してファイルに書き出したりすることが出来ます。詳しくは「補助構文」の項を参照して下さい。
また上記の手順を少し変えることで、自然言語ではなくC++への翻訳も可能となります。詳しくはC++への翻訳の章をご覧下さい。
1.2. 変数の扱い
\(\mathbb{Q}_p\)における変数は、通常のプログラミング言語における変数と違って値の変更が不可能なものです。従って等号は全て「等しい」という関係でのみ使われ、代入による書き換えを表すことはありません。ポインタもありません。これらは通常の数学における変数に似せた仕様です。ただし通常の数学と違って量化子による変数の束縛を明示的に扱いません。とはいえ関数という概念の通常の数学における定義にはもちろん量化子による束縛が行われていますので、量化子が隠れているだけと思って下さい。
また演算子(関数や関係)の引数を含む全ての変数は、最初に宣言を必要とします。更に演算子の引数でない全ての変数は、その演算子の定義文内で最初に参照される際に値を定義される必要があります。これらの仕様は実用的でなく煩わしいかもしれませんが、「動く範囲の不明な変数を使う」とか「未定義の値を使う」といった巨大数の定義でありがちなミスを防ぐための厳格なものです。
1.3. 関数の扱い
\(\mathbb{Q}_p\)における関数は、通常のプログラミング言語における関数と違って変数の書き換えを行う機能はなく、また必ず戻り値を持つことが想定されています。これらは通常の数学における部分再帰的関数に似せた仕様です。ただし通常の数学と違って単に値を出力するだけでなく、標準出力を行うことも可能です。
また関数の宣言においては引数の指定を必要とします。引数自体も変数であり宣言が必要であるため、これにより関数は宣言の時点で定義域がどんな集合の部分集合であるかが確定します。これらの仕様は実用的でなく煩わしいかもしれませんが、「定義域の不明な関数を使う」とか「定義域外の値を関数に代入する」といった関数の定義でありがちなミスを防ぐための厳格なものです。
1.4. 階数
\(\mathbb{Q}_p\)で許容される文字列全体の集合は再帰的に定義されます。\(\mathbb{Q}_p\)で許容される文字列のいくつかには階数という自然数が定義されており、特定の階数を持つ\(\mathbb{Q}_p\)で許容される文字列の定義にはそれ以上の階数を持つ\(\mathbb{Q}_p\)で許容される文字列は使いません。
また\(\mathbb{Q}_p\)で許容される文字列の階数を集めて有限集合を考えることがありますが、その上限とは、その集合が空集合の時は\(0\)のことであり、空集合でない時はその最大値のことです。
1.5. 使用可能な文字
「文字列」と言った時に使用可能な文字を全て列挙するのは非常に面倒なのでしません。半角英数ならば問題がありませんが、部分的に漢字等を用いてもコンパイルは通ります。また例えば「コンマを含まない文字列」と言った時、コンマさえ含まなければヒエログリフでも架空の文字でも良いというわけではなく、本当はもっと色々と制約がありますが、面倒なので省略します。
1.6. 文字列の結合
文字列の列A
を,
で結合して得られる文字列をこの記事ではConcatenate(A)
と表します。例えばA
が空列であればConcatenate(A)
は空文字列となります。A
がただ\(1\)つの成分を持つならばConcatenate(A)
はA
の唯一の成分です。A
がちょうど\(2\)つの成分を持つならば、A
の第\(1\)成分をx
と置き第\(2\)成分をy
と置くと、Concatenate(A)
は文字列x,y
となります。
A
に一切制限がない場合はConcatenate(A)
からA
を復元することが出来ないので、「Concatenate(A)
に対し\(X\)という概念を(A
を用いて)\(Y\)と定める」のような文は\(X\)の定義を与えません。一方でA
に十分強い制約(例えばいずれの成分も,
を含まない等)を課している文脈ではConcatenate(A)
からA
を復元することが可能になることがあり、そういう場合には「Concatenate(A)
に対し\(X\)という概念を(A
を用いて)\(Y\)と定める」のような文で\(X\)を定義することができます。
一般に「\(Z\)に対し、\(B\)を用いて\(X\)を\(Y\)と定める」という文は\(Z\)と\(B\)から\(Y\)が一意に定まりかつ\(Z\)から\(B\)が一意に定まる場合にのみ、\(Z\)のみに依存する概念としての\(X\)の定義を与えます。この記事においてもそれらの条件が満たされている場合にのみ、そのような構文を用いて定義していきます。
1.7. 文字列の位置関係
構文の説明の中に「~以降の文字列」という言葉遣いが現れますが、これは何となく意味が分かる概念でありながらもかなり曖昧な表現です。何故なら、これは2つの文字列の「位置関係」に関する述語であって2つの文字列そのものに関する述語ではなく、厳密に書こうとすると文字列の分割に関する述語として定義する必要があります。それを(書くのは簡単ですが)読むのは大変になると思うので、ここでは「~以降の文字列」という言葉遣いの厳密な定義はしません。
2. 構文
\(\mathbb{Q}_p\)で許容される文字列として、以下の分類を説明していきます。
- 型(type)
- 原始型(primitive type)
- 特殊型(specialised type)
- 配列型(array type)
- ネスト配列型(nested array type)
- 直積型(direct product type)
- 宣言(declaration)
- USE宣言(USE declaration)
- SUGAR宣言(SUGAR declaration)
- IMP宣言(IMP declaration)
- 表現(expression)
- 定数(constant symbol)
- 変数(variable symbol)
- 糖衣表現(sugar expression)
- 演算子表現(operator expression)
- 演算子(operator symbol)
- 関数(function symbol)
- 関係(relation symbol)
- 可変長引数演算子(variadic operator symbol)
- 可変長引数関数(variadic function symbol)
- 可変長引数関係(variadic relation symbol)
- 命令行(line)
- PUT文(PUT sentence)
- PRINT文(PRINT sentence)
- IF文(IF sentence)
- EXIT文(EXIT sentence)
- RETURN文(RETURN sentence)
- 定義文(definition)
- SPEC構成(SPEC construction)
- ARRAY構成(ARRAY construction)
- NEST構成(NEST construction)
- PROD構成(PROD construction)
- DEF文(DEF sentence)
- 補助構文(guide syntax)
- シンボル設定(symbol setting)
- セパレータ設定(separator setting)
- 全域性設定(totality setting)
- 宣言標準出力(declaration stdout)
- 翻訳出力(translation output)
- ソースコード(souce code)
これらの分類には重複があり、例えば定数は表現となり、表現は表現リストとなります。ソースコードである文字列が必ずしも全域な計算可能関数を定めるということではなく、ソースコードであることはあくまでコンパイルを通るための条件です。実行時に要請される追加の条件は「実行」の項で説明します。
2.1. 原始型
原始型またはprimitive typeとは、以下の4種類の文字列のことです;
- 文字列
void
- 文字列
int
- 文字列
string
- 文字列
bool
また基本型T
に対し、T
の階数という概念を\(0\)と定め、T
の基底型という概念をT
と定めます。
2.2. SPEC構成
自然数\(R\)に対し、階数\(R+1\)のSPEC構成またはSPEC constructionとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列T
と\(R = \max \{R_1,R_2\}\)を満たす自然数\(R_1\)と\(R_2\)と階数\(R_1\)の変数x
と階数\(R_2\)の関係r
を用いてSPEC(T,x,r)
と表される文字列のことです。SPEC(T,x,r);
以降の文字列において、T
はSPEC構成されていると言い、Tの階数という概念を\(R+1\)として定め、T
の基底型という概念をx
の型の基底型と定めます。SPEC構成されている文字列をSPEC構成、ARRAY構成、NEST構成、PROD構成、USE宣言、SUGAR宣言、IMP宣言することは出来ません。特にNEST構成された文字列T
をSPEC構成するSPEC構成は一意となるため、それをT
の定義文と呼びます。(T
をr
を満たすx
全体の型としてspecialisationしたい時にSPEC(T,x,r);
して下さい)
2.3. 特殊型
特殊型またはspecialised typeとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列であってSPEC構成されているもののことです。
2.4. ARRAY構成
自然数\(R\)に対し、階数\(R+1\)のARRAY構成またはARRAY constructionとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列T
と階数\(R\)の型S
を用いてARRAY(T,S)
と表される文字列のことです。ARRAY(T,S);
以降の文字列において、T
はARRAY構成されていると言い、Tの階数という概念を\(R+1\)として定めます。ARRAY構成されている文字列をSPEC構成、ARRAY構成、NEST構成、PROD構成、USE宣言、SUGAR宣言、IMP宣言することは出来ません。特にARRAY構成された文字列T
をARRAY構成するARRAY構成は一意となるため、それをT
の定義文と呼びます。(T
をS
の元を成分に持つ配列全体の型として定義したい時にARRAY(T,S)
して下さい。なお型の異なる空配列同士は区別されます)
ARRAY構成された文字列T1
とT2
の定義文がそれぞれARRAY(T1,S)
とARRAY(T2,S)
である時、「型の定義される文字列の型がT1
であることを要請する場合に型T2
でも良いものとする」という意味で型T1
と型T2
を同一視します。同一視される型のいずれか1つをこの記事内で表したい場合、それをS^ω
と書きます。S^ω`はこの記事内だけの記法であり、それ自体は\(\mathbb{Q}_p\)で許容される文字列ではないことに注意して下さい。
T
の基底型という概念をS
の基底型S0
を用いてS0^ω
と定めます。ただしこの定義ではT
の基底型が文字列として一意ではありませんが、T
の基底型の曖昧さが問題を起こさないように構文が定義されます。
2.5. 配列型
配列型またはarray typeとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列であってARRAY構成されているもののことです。
2.6. NEST構成
自然数\(R\)に対し、階数\(R+1\)のNEST構成またはNEST constructionとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列T
と階数\(R\)の型S
を用いてNEST(T,S)
と表される文字列のことです。NEST(T,S);
以降の文字列において、T
はNEST構成されていると言い、Tの階数という概念を\(R+1\)として定めます。ARRAY構成されている文字列をSPEC構成、ARRAY構成、NEST構成、PROD構成、USE宣言、SUGAR宣言、IMP宣言することは出来ません。特にNEST構成された文字列T
をNEST構成するNEST構成は一意となるため、それをT
の定義文と呼びます。(T
はT
自身の元を成分に持つ配列とS
の元全体の型として定義したい時にNEST(T,S)
して下さい)
NEST構成された文字列T1
とT2
の定義文がそれぞれNEST(T1,S)
とNEST(T2,S)
である時、「型の定義される文字列の型がT1
であることを要請する場合に型T2
でも良いものとする」という意味で型T1
と型T2
を同一視します。同一視される型のいずれか1つをこの記事内で表したい場合、それをS^{ω^ω}
と書きます。S^{ω^ω}
はこの記事内だけの記法であり、それ自体は\(\mathbb{Q}_p\)で許容される文字列ではないことに注意して下さい。
T
の基底型という概念をS
の基底型S0
を用いてS0^{ω^ω}
と定めます。ただしこの定義ではT
の基底型が文字列として一意ではありませんが、T
の基底型の曖昧さが問題を起こさないように構文が定義されます。
2.7. ネスト配列型
ネスト配列型またはnested array typeとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列であってNEST構成されているもののことです。
ただし型S
はネスト構成NEST(T,S)
を持つネスト配列型T
を特殊化して得られる型ではないことに注意して下さい。特にS
の基底型はT
の基底型となりません。(特殊化は部分集合のような型を与えますが、逆に部分集合のような関係のある型同士は必ずしも特殊化で得られません)
2.8. PROD構成
自然数\(R\)に対し、階数\(R+1\)のPROD構成またはPROD constructionとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列T
と階数\(R\)を上限に持つ空文字列でも型でもない型リストTs
を用いてPROD(Ts)
と表される文字列のことです。PROD(T,Ts);
以降の文字列において、T
はPROD構成されていると言い、Tの階数という概念を\(R+1\)として定めます。PROD構成されている文字列をSPEC構成、ARRAY構成、NEST構成、PROD構成、USE宣言、SUGAR宣言、IMP宣言することは出来ません。特に特殊型T
をPROD構成するPROD構成は一意となるため、それをT
の定義文と呼びます。(T
をTs
のdirect productの元全体の型として構成したい時にPROD(T,Ts);
して下さい)
PROD構成された文字列T1
とT2
の定義文がそれぞれPROD(T1,Ts)
とPROD(T2,Ts)
である時、「型の定義される文字列の型がT1
であることを要請する場合に型T2
でも良いものとする」という意味で型T1
と型T2
を同一視します。同一視される型のいずれか1つをこの記事内で表したい場合、それをΠ Ts
と書きます。Π Ts`はこの記事内だけの記法であり、それ自体は\(\mathbb{Q}_p\)で許容される文字列ではないことに注意して下さい。
T
の基底型という概念をTs
の各成分の基底型からなる型リストTs0
を用いてΠ Ts0
と定めます。ただしこの定義ではT
の基底型が文字列として一意ではありませんが、T
の基底型の曖昧さが問題を起こさないように構文が定義されます。
2.9. 直積型
自然数\(R\)に対し、階数\(R+1\)の直積型またはdirect product typeとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列であってPROD構成されているもののことです。
2.10. 型
型またはtypeとは、以下の5種類の文字列のことです:
- 原始型
- 特殊型
- 直積型
- 配列型
- ネスト配列型
大雑把には型は命令文や表現の属性の1つです。例えばint
は表現が整数に関係するという属性を表し、string
は表現が文字列に関係するという属性を表し、bool
は表現が条件に関係するという属性を表すと考えてください。
2.11. 型リスト
自然数\(R\)に対し、階数\(R\)を上限に持つ型リストまたはtype listとは、\(0\)個以上の型の列AT
であって階数の上限が\(R\)であるものを用いてConcatenate(AT)
と表せる文字列のことです。
2.12. 定数
定数またはconstant symbolとは、以下の3種類の文字列のことです;
- 整数の符号付き十進法表記
- 文字列
s
であってエスケープされていない"
や'
を含まないものを用いてSTR( s )
と表される文字列 - 文字列
true
および文字列false
また定数c
に対し、c
の型という概念を上記の場合分けにおいてそれぞれint
、string
、bool
と定め、cの階数という概念を\(0\)と定めます。
2.13. USE宣言
自然数\(R\)に対し、階数\(R+1\)のUSE宣言またはUSE declarationとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列x
とvoid
でない階数\(R\)の型T
を用いてUSE(T,x);
と表される文字列のことです。USE(T,x);
以降の文字列において、x
はUSE宣言されていると言い、またx
の型という概念をT
と定め、x
の階数という概念を\(R+1\)と定めます。USE宣言されている文字列をSPEC構成、ARRAY構成、NEST構成、PROD構成、USE宣言、SUGAR宣言、IMP宣言することは出来ません。(x
をT
型の変数としてuseしたい時にUSE(T,x);
して下さい)
2.14. 変数
変数またはvariable symbolとは、USE宣言されている文字列のことです。
2.15. SUGAR宣言
自然数\(R\)に対し、階数\(R+1\)のSUGAR宣言またはSUGAR declarationとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列x
と階数\(R\)の表現c
を用いてSUGAR(x,c);
と表される文字列のことです。SUGAR(x,c);
以降の文字列において、x
はSUGAR宣言されていると言い、またx
の型という概念をc
の型と定め、x
の階数という概念を\(R+1\)と定め、x
の実体という概念をc
と定めます。SUGAR宣言されている文字列をSPEC構成、ARRAY構成、NEST構成、PROD構成、USE宣言、SUGAR宣言、IMP宣言することは出来ません。(x
をc
糖衣構文として使いたい時にSUGAR(x,c);
して下さい)
2.16. 糖衣表現
糖衣表現またはsugar expressionとは、SUGAR宣言されている文字列のことです。(糖衣表現はその実体と同じ値を持ちますが、実体が変数であるとしても変数とはなりません)
2.17. 変数リスト
自然数\(R\)に対し、階数\(R\)を上限に持つ変数リストまたはvariable symbol listとは、\(0\)個以上の変数の列Ax
であって階数の上限が\(R\)であるものを用いてConcatenate(Ax)
と表せる文字列のことです。Concatenate(Ax)
の成分という概念をAx
の成分として定めます。
2.18. 可変長変数リスト
自然数\(R\)に対し、階数\(R+1\)の可変長変数リストまたはvariadic variable symbol listとは、\(R = \max \{R_1,R_2\}\)を満たす自然数\(R_1\)と\(R_2\)と階数\(R_1\)を上限に持つ変数リストx
と階数\(R_2\)の型\(V\)を用いてx,LDOTS(V)
と表される文字列のことです。
2.19. IMP宣言
自然数\(R\)に対し、階数\(R+1\)のIMP宣言またはIMP declarationとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列f
と\(R = \max \{R_1,R_2\}\)を満たす自然数\(R_1\)と\(R_2\)とvoid
でない階数\(R_1\)の型T
と階数\(R_2\)を上限に持つ変数リストまたは可変長変数リストである文字列x
を用いてIMP(T,f,x);
と表される文字列のことです。IMP(T,f,x);
以降の文字列において、f
はIMP宣言されていると言い、f
の戻り値の型という概念をT
として定め、f
の引数という概念をx
として定め、f
の階数という概念を\(R+1\)として定めます。IMP宣言されている文字列をSPEC構成、ARRAY構成、NEST構成、PROD構成、USE宣言、SUGAR宣言、IMP宣言することは出来ません。(f
を戻り値がT
型で引数がx
である演算子としてimportしたい時にIMP(T,f,x);
して下さい)
2.20. 宣言
宣言またはdeclarationとは、以下の2種類の文字列のことです;
- USE宣言
- SUGAR宣言
- IMP宣言
2.21. 演算子
演算子またはoperator symbolとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列であってIMP宣言されているもののうち、引数が変数リストであるもののことです。
演算子f
に対し、f
の引数の型リストがA
でf
の戻り値の型がR
の時、f
の高階型という概念を(A)->R
と定めます。
2.22. 関数
関数またはfunction symbolとは、戻り値の型がbool
でない演算子のことです。
2.23. 関係
関係またはrelation symbolとは、戻り値の型がbool
である演算子のことです。
2.24. 可変長引数演算子
可変長引数演算子またはvariadic operator symbolとは、アルファベットと_
のみからなる長さ\(1\)以上の文字列であってIMP宣言されているもののうち、引数が可変長変数リストであるもののことです。
可変長引数演算子f
に対し、f
の引数の型リストがA
でf
の引数の可変長部の型がV
でf
の戻り値の型がR
の時、f
の高階型という概念を(A,LDOTS(V))->R
と定めます。
2.25. 可変長引数関数
可変長引数関数またはvariadic function symbolとは、戻り値の型がbool
でない可変長引数演算子のことです。
2.26. 可変長引数関係
可変長引数関係またはvariadic relation symbolとは、戻り値の型がbool
である可変長引数演算子のことです。
2.27. 演算子表現
自然数\(R\)に対し、階数\(R+1\)の演算子表現またはoperator expressionとは、\(R = \max \{R_1,R_2\}\)を満たす自然数\(R_1\)と\(R_2\)と以下の3条件のいずれかを満たす文字列f
とa
を用いてf(a)
と表される文字列のことで、f(a)
の型という概念をf
の戻り値の型として定め、f(a)
の階数という概念を\(R+1\)と定めます:
f
は階数\(R_1\)の演算子であり、a
は階数\(R_2\)を上限に持つ表現リストであり、かつa
の型リストとf
の引数の型リストが等しい。f
は階数\(R_1\)の可変長引数演算子であり、a
は階数\(R_2\)を上限に持つ表現リストa
であり、a
の型リストがx
の型リストTs
と全ての成分がf
の引数の可変長部の型である列Vs
を用いてTs,Concatenate(Vs)
と表せる。f
は階数\(R_1\)の可変長引数演算子であり、a
は階数\(R_2\)を上限に持つ可変長表現リストa
であり、a
の型リストがx
の型リストTs
と全ての成分がf
の引数の可変長部の型である列Vs
を用いてTs,Concatenate(Vs)
と表せ、a
の可変長部の型がf
の引数の可変長部の型と等しい。
2.28. 表現
表現またはexpressionとは、定数または変数または糖衣表現または演算子表現である文字列のことです。
2.29. 表現リスト
自然数\(R\)に対し、階数\(R\)を上限に持つ表現リストまたはexpression listとは、\(0\)個以上の表現の列Ae
であって階数の上限が\(R\)であるものを用いてConcatenate(Ae)
と表せる文字列のことです。Concatenate(Ae)
の型リストという概念をs
の成分である表現の型の列AT
を用いてConcatenate(AT)
と表せる文字列として定めます。
2.30. 可変長表現リスト
自然数\(R\)に対し、階数\(R+1\)を上限に持つ可変長表現リストまたはvariadic expression listとは、\(R = \max \{R_1,R_2\}\)を満たす自然数\(R_1\)と\(R_2\)と階数\(R_1\)を上限に持つ表現リストa
と階数\(R_2\)の型V
を用いてa,LDOTS(V)
と表せる文字列のことです。a,LDOTS(V)
の型リストという概念をa
の型リストとして定め、a,LDOTS(V)の可変長部の型という概念をV
と定めます。
2.31. PUT文
PUT文またはPUT sentenceとは、変数x
と以下の2条件のいずれかを満たす文字列e
を用いてPUT(x,e)
と表される文字列のことです:
e
はx
と基底型T
の型の等しい表現である。(変数x
をe
の糖衣構文としてputしたい時にPUT(x,e)
して下さい)x
の型T
はnat
かstring
のいずれかであり、e
は基底型T
の型の変数n
と基底型T
の型の表現m
と基底型bool
の型の表現b
を用いてPUT(x,UNIQUE(n,m,b))
と表せる。(変数x
を「n <= m
とb
を満たす唯一のn
の糖衣構文としてputしたい時にPUT(x,UNIQUE(n,m,b))
して下さい)x
の型はnat
であり、e
はnat
型の変数n
と基底型bool
の型のb
を用いてPUT(x,MIN(n,m,b))
と表せる。(変数x
を「b
を満たす最小のn
の糖衣構文としてputしたい時にPUT(x,MIN(n,b))
して下さい)x
の型はnat
であり、e
はnat
型の変数n
と基底型int
の型の表現m
と基底型bool
の型のb
を用いてPUT(x,MAX(n,m,b))
と表せる。(変数x
を「n <= m
とb
を満たす最大のn
の糖衣構文としてputしたい時にPUT(x,MAX(n,m,b))
して下さい)
PUT文PUT(x,e)
をx
のPUT文と呼び、PUT(x,e)
の型という概念をvoid
として定めます。
2.32. PRINT文
PRINT文またはPRINT sentenceとは、表現リストae
を用いてPRINT(ae)
と表される文字列のことで、PRINT(ae)
の型という概念をvoid
として定めます。(ae
の成分を標準出力したい時にPRINT(ae)
して下さい)
2.33. EXIT文
EXIT文またはEXIT sentenceとは、文字列EXIT
のことで、EXIT
の型という概念をvoid
として定めます。(DEF文において処理を異常終了させたい時にEXIT
して下さい)
2.34. RETURN文
RETURN文またはRETURN sentenceとは、表現e
を用いてRETURN(e)
と表される文字列のことで、RETURN(e)
の型という概念をe
の型として定めます。(DEF文においてe
を出力したい時にRETURN(e)
して下さい)
2.35. 命令行
命令行、命令行リストおよびIF文という概念をという概念を以下のように同時に再帰的に定義します:
- 命令行またはlineとは、以下の5種類の文字列のことです:
- PUT文
- PRINT文
- IF文
- EXIT文
- RETURN文
- 命令行リストまたはline listとは、以下の2条件を満たす\(1\)個以上の命令行の列
AL
と型T
を用いてConcatenate(AL)
と表せる文字列のことで、Concatenate(AL)
の型という概念をT
として定めます。AL
の成分である命令行の型はT
またはvoid
である。AL
はT
型の命令行を少なくとも\(1\)個成分に持つ。
- IF文またはIF sentenceとは、以下の2条件を満たす
bool
型の表現b
と命令行リストaL
を用いてIF(b,aL)
と表される文字列のことで、IF(b,aL)
の型という概念をaL
の型として定めます。
2.36. DEF文
DEF文またはDEF sentenceとは、演算子f
と命令行リストaL
であってf
の戻り値の型とaL
の型は等しいものを用いてDEF(f){aL}
と表される文字列のことで、DEF(f){aL}
の型という概念をaL
の型として定め、DEF(f){aL}
の定める演算子という概念をf
として定めます。DEF(f){aL};
以降の文字列において、f
は実装されていると言います。実装されている演算子を実装することはできません。特に演算子f
を実装するDEF文は一意となるため、それをf
の定義文と呼びます。
ちなみにDEF文内に;
が現れるうるのはstring
型の定数内のみです。どこかに;
を書くべきかどうか迷ったら、それがDEF文の内側か外側かで判断しましょう。
2.37. 定義文
定義文またはdefinitionとは、以下の5種類の文字列のことです:
- SPEC構成
- ARRAY構成
- NEST構成
- PROD構成
- DEF文
2.38. シンボル設定
シンボル設定またはsymbol settingとは、以下の2種類の文字列のことです:
- 変数または演算子である文字列
x
とコンマ記号を含まない文字列s
を用いてSET(x,SYMBOL,s);
と表される文字列。(翻訳時にx
をエスケープされたs
のMathJaxでの表示に変更して出力したい時にSET(x,SYMBOL,s);
して下さい) - 文字列
s
を用いてSET(x,SYMBOL,PLAIN(s));
と表される文字列。(翻訳時に変数または演算子である文字列x
をエスケープされたs
のプレインテキスト化に変更して出力したい時にSET(x,SYMBOL,PLAIN(s));
して下さい) - 変数または演算子である文字列
x
を用いてROMANISE(x);
と表される文字列。(翻訳時にx
をプレインテキスト化して出力したい時にDEF文より前にROMANISE(x);
して下さい)
2.39. セパレータ設定
セパレータ設定またはseparator settingとは、演算子f
とセパレータリストas
を用いてSET(f,SEPARATOR,as);
と表せる文字列のことです。(演算子のセパレータを設定したい時にDEF文より前にSET(f,SEPARATOR,as);
して下さい。)
セパレータリストまたはseparator listとは、\(1\)個以上のセパレータの列As
を用いてConcatenate(As)
と表せる文字列のことです。
セパレータまたはseparatorとは、以下のように再帰的に定義される文字列の属性です:
- 表現または演算子
x
に対し、文字列SYMBOLISE(x)
(記号x
)はセパレータである。 - 文字列
EMPTY
(空文字列)はセパレータである。 - 文字列
ESCAPE
(直後の文字のエスケープ)はセパレータである。 - 文字列
SPACE
(半角スペース)はセパレータである。 - 文字列
SUB
(下付き指示)はセパレータである。 - 文字列
SUP
(上付き指示)はセパレータである。 - 文字列
LPAREN
(左カッコ)はセパレータである。 - 文字列
RPAREN
(右カッコ)はセパレータである。 - 文字列
LBRACE
(左中カッコ)はセパレータである。 - 文字列
RBRACE
(右中カッコ)はセパレータである。 - 文字列
LBRACK
(左四角カッコ)はセパレータである。 - 文字列
RBRACK
(右四角カッコ)はセパレータである。 - 文字列
LANGLE
(左三角カッコ)はセパレータである。 - 文字列
RANGLE
(右三角カッコ)はセパレータである。 - 文字列
VERT
(縦線)はセパレータである。 - 文字列
VVERT
(二重縦線)はセパレータである。 - 文字列
FRAC
(分数用の横線)はセパレータである。 - 文字列
LBIG
(左カッコ等の拡大指示)はセパレータである。 - 文字列
MBIG
(カッコ内の縦線等の拡大指示)はセパレータである。 - 文字列
RBIG
(右カッコ等の拡大指示)はセパレータである。 - 文字列
PERIOD
(ピリオド)はセパレータである。 - 文字列
COMMA
(コンマ)はセパレータである。 - 文字列
COLON
(コロン)はセパレータである。 - 文字列
SCOLON
(セミコロン)はセパレータである。 - 文字列
ATMARK
(アットマーク)はセパレータである。 - \(1\)個以上の文字
l
,c
,r
,|
のみからなる文字列s
に対し、文字列LMAT(s)
(行列開始位置)はセパレータである。 - 文字列
RMAT
(行列終端位置)はセパレータである。 - 文字列
ET
(列区切り位置)はセパレータである。 - 文字列
YY
(行区切り位置)はセパレータである。 - 文字列
HLINE
(行区切り用の横線)はセパレータである。 - 文字列
LVEC
(ベクトル開始位置)はセパレータである。 - 文字列
RVEC
(ベクトル終端位置)はセパレータである。 - 文字列
X
とY
がセパレータならば、それらを+
で結合した文字列はセパレータである。
2.40. 全域性設定
全域性設定またはtotality settingとは、演算子f
と全域性c
を用いてSET(f,TOTALITY,c);
と表せる文字列のことです。
全域性またはtotalityとは、以下の3種類の文字列のことです。
PARTIAL
TOTAL
PRIMITIVE
演算子f
の定義文を翻訳出力する際に、部分関数であることを明記したければSET(f,TOTALITY,PARTIAL);
をし、全域関数であることを明記したければSET(f,TOTALITY,TOTAL);
をし、原始再帰関数であることを明記したければSET(f,TOTALITY,PRIMITIVE);
をして下さい。
この機能はあくまで翻訳出力時の出力内容に影響を与えるためだけのもので、計算可能性設定に反するような定義文を書いてしまったとしてもコンパイルエラーにはなりません。特に、全域性を主張させることは出来ても実際に全域かどうか(無限ループを生じさせないかどうか)はコンパイルによって判定することは出来ません。
2.41. 宣言標準出力
宣言標準出力またはdeclaration stdoutとは、型または表現または演算子である文字列s
と対応言語L
を用いてDISPLAY(s,L);
と表せる文字列のことです。(s
の宣言の木構造をL
語の説明付きで標準出力したい時にDISPLAY(s,L);
して下さい)
対応言語またはsupported languageとは、以下の5種類の文字列のことです::
JAPANESE
ENGLISH
CHINESE
FRENCH
GERMAN
2.42. 翻訳出力
翻訳出力またはtranslation outputとは、型または糖衣表現または定義文を持つ演算子である文字列s
と対応言語L
と出力形式S
とコンマを含まない文字列F
とオープンモードM
を用いてWRITE(s,L,S,F,M);
と表せる文字列のことです。(s
の定義文のL
語訳をS
仕様でカレントディレクトリからの相対パスがF
のファイルにオープンモードM
で書き込みたい時にWRITE(s,L,S,F,M);
して下さい)
出力形式またはoutput styleとは、以下の2種類の文字列のことです:
FANDOM
JEKYLL
オープンモードまたはopen modeとは、以下の2種類の文字列のことです:
APP
TRUNC
2.43. 補助構文
補助構文またはguide syntaxとは、以下の5種類の文字列のことです:
- シンボル設定
- セパレータ設定
- 全域性設定
- 宣言標準出力
- 翻訳出力
これらは\(\mathbb{Q}_p\)の演算子の計算結果に一切影響を与えず、定義文の出力にのみ影響を与えます。
2.44. ソースコード
\(\mathbb{Q}_p\)のソースコードとは、宣言と定義文と補助構文のみからなる文字列の列の結合で表せる文字列のことです。
3.コンパイル
\(\mathbb{Q}_p\)のソースコードC
に対して、「導入」の項で紹介した方法でint main(){C; return 0;}
というコードをC++コンパイラでコンパイルすることが出来ます。このコードが正常にコンパイルできることはC
自身が\(\mathbb{Q}_p\)のソースコードであるための必要条件に近い条件であって十分条件ではありません。例えばC
がint x = 0
という文字列の場合、C
は\(\mathbb{Q}_p\)のソースコードではありませんがint main(){C; return 0;}
というコードは正常にコンパイルすることができます。
\(\mathbb{Q}_p\)のソースコードでない文字列の多くがコンパイルエラーとなりますので、\(\mathbb{Q}_p\)のソースコードのつもりで書いた文字列が実際に\(\mathbb{Q}_p\)のソースコードであるか否かの判定には十分機能すると思います。
ライブラリを導入するのが面倒という人はp進大好きbotにコードを渡して下さればコンパイルエラーの有無を返答致します。
3.1. 予約語
USE等の上記で説明されている文字列以外に、予め機能が定義されている文字列が存在します。例えばC++の予約語は\(\mathbb{Q}_p\)でも予約語となります。そうしないと、C++コンパイラで正常にコンパイル出来ないからです。C++の予約語以外の\(\mathbb{Q}_p\)の予約語は列挙するのが面倒なので、気にしないで下さい。
3.2. 糖衣構文
C++コンパイラに内蔵されているプリプロセッサを使うので、#define
により糖衣構文を導入することが出来ます。従ってC++の定義済みマクロは定義せずに用いることが出来ますが、一方でC++の定義済みマクロと衝突するマクロを定義することは出来ません。
3.3 定義済み型
\(\mathbb{Q}_p\)にはいくつかの型が最初から定義されています。
int
型の変数n
を用いてSPEC(nat,n,NONNEG)
と定義される、非負整数全体の集合に対応する型nat
。
ただしNONNEG
は後で説明する定義済み演算子です。
3.4 定義済み演算子
\(\mathbb{Q}_p\)にはいくつかの演算子が最初から定義されており、IMP宣言せずに用いることが出来ます。
3.4.1 糖衣構文付き演算子
加法や不等号や論理和などの基本的な以下の演算子はユーザー定義の演算子と異なり、中置記法等の特殊な記法を糖衣構文に持ちます:
演算子 | 意味 | 高階型 | 特殊記法 | 備考 |
---|---|---|---|---|
PLUS | 加算演算子 | 原始型または配列型またはネスト配列型である型 T に対する (T,LDOTS(T))->T | 中置記法 + | ただし string 型に対する + は文字列の結合に対応し、配列型やネスト配列型にする + は配列の結合に対応します。 |
TIMES | 乗算演算子 | int または bool である型 T に対する (T,LDOTS(T))->T | 中置記法 * | |
MINUS | 減算演算子 | (int,int)->int | 中置記法 - | |
SLASH | 除算演算子 | (int,int)->int | 中置記法 / | |
MOD | 剰余演算子 | (int,int)->int | 中置記法 % | |
POWER | 冪演算子 | (int,int)->int | 中置記法 ^ | |
NEG | 否定演算子 | (bool)->bool | ポーランド記法 ! | |
LAND | 論理積演算子 | (bool,LDOTS(bool))->bool | 中置記法 && と * | |
LOR | 論理和演算子 | (bool,LDOTS(bool))->bool | 中置記法 || と + | |
TO | 含意演算子 | (bool,bool)->bool | 中置記法 >> と ->* | 本当は TO の中置記法に -> を採用したいのですが、 -> はC++においてオーバーロードが制限されているため、C++コンパイラでコンパイルできるようにするために断念しました。 |
OT | 逆含意演算子 | (bool,bool)->bool | 中置記法 << | |
EQUIV | 同値演算子 | (bool,bool)->bool | 中置記法 == | 本当は EQUIV の中置記法に <=> を採用したいのですが、 <=> はC++においてオーバーロードが制限されているため、C++コンパイラでコンパイルできるようにするために断念しました。 |
EQ | 等号演算子 | 型 T に対する (T,T)->bool | 中置記法 == | 中置記法 == は通常の等号のように a == b == c と並列することが出来ません。 |
NEQ | 不等号演算子 (ノットイコール) | 型 T に対する (T,T)->bool | 中置記法 != | |
LEQ | 不等号演算子 (小なりイコール) | (int,int)->bool | 中置記法 <= | |
GEQ | 不等号演算子 (大なりイコール) | (int,int)->bool | 中置記法 >= | |
LNEQ | 不等号演算子 (小なり) | (int,int)->bool | 中置記法 < | |
GNEQ | 不等号演算子 (大なり) | (int,int)->bool | 中置記法 > |
3.4.2 その他の基本演算子
特殊な糖衣構文は持たないですが基本的な以下の演算子を使うことが出来ます:
演算子 | 高階型 | 説明 |
---|---|---|
NONNEG | (int)->bool | n に対し n >= 0 を返します。 |
LENGTH | (string)->nat (T^ω)->nat (T^{ω^ω})->nat | s に対し s の長さを返します。ただし DENESTABLE( a ) を満たす T^{ω^ω} 型の表現 a に対する LENGTH(a) は 0 と定義されるのではなく未定義です。 |
ENTRY | (string,nat)->nat (T^ω,nat)->T (T^{ω^ω},nat)->T^{ω^ω} | (a,n) に対し a の第 1+n 番目の成分を返します。 |
INISEG | (string,nat)->string (T^ω,nat)->T^ω (T^{ω^ω},nat)->T^{ω^ω} | (s,n) に対し s の始切片であって長さが n であるものを返します。 |
FINSEG | (string,nat)->string (T^ω,nat)->T^ω (T^{ω^ω},nat)->T^{ω^ω} | (s,n) に対し s の終切片であって長さが n であるものを返します。 |
WRAP | (T)->T^ω | x に対し (x) を返します。 |
NWRAP | (T^{ω^ω})->T^{ω^ω} | a に対し (a) を返します。 |
ENNEST | (T)->T^{ω^ω} | x に対し x 自身を返します。 |
DENEST | (T^{ω^ω})->T | a に対し a 自身を返します。 |
DENESTABLE | (T^{ω^ω})->bool | a に対し a が T^{ω^ω} 型の元の配列でないか否かを返します。例えば T 型の表現 x を用いて ENNEST(x) や ENTRY(WRAP(x),0) と表せる時は DENESTABLE( a ) は true と評価されます。 |
NESTIFY | (T^ω)->T^{ω^ω} T^{ω^ω}->T^{ω^ω} | a に対し a 自身を返します。 |
PROJ < i > | 非負整数 i に対する Π(T0,…,Tn)->Ti | a に対し a の第 1+i 成分を返します。 |
TUPLE | (T0,…,Tn)->Π(T0,…,Tn) | a に対し a 自身を返します。 |
3.4.3 メタ演算子
既存の演算子から別の演算子を定義するために以下のマクロを使うことが出来ます:
マクロ | 説明 |
---|---|
^ | 高階型 (T)->T の演算子 f と nat 型の表現 n に対し、 f の n 回合成に対応する高階型 (T)->T の関数 f^n を生成します。 |
CURRY | 高階型 (T1,T2)->T3 の演算子 f と T1 型の表現 x に対し、 y に対し f(x,y) を返す高階型 (T2)->T3 の関数 CURRY(f,x) を生成します。デフォルトの CURRY(f,x)(y) は翻訳時にCurry(f,x)(y)と表示され、オプション引数付きの CURRY(f,x,EMPTY)(y) では翻訳時にf(x,y)と表示され、オプション引数付きの CURRY(f,x,SUB)(y) では翻訳時にfx(y)と表示されます。 CURRY(f,x,EMPTY)(y) は自然言語への翻訳時に関数としては引数1の関数にyを代入したものとして表示されないことになるので、引数1の関数に対するマクロである ^ と組み合わせて (CURRY(f,x)^2)(y) とすると自然言語への翻訳が意図しないものとなります。自然言語への翻訳を意図している場合は代わりに (CURRY(f,x)^2)(y) や (CURRY(f,x,SUB)^2)(y) を使って下さい。 |
ARRAYISE | 高階型 (nat)->T の演算子 f と nat 型の変数 n に対し、 n に対し (f(0),…,f(n-1)) を返す高階型 (nat)->T^ω の関数 ARRAYISE(f,n) を生成します。ちなみに f の引数である変数が他の変数と被っている場合、自然言語への翻訳時に (f(n))_{n=0}^{n-1}} のような不適切な訳が出力されます。自然言語訳をしたい場合は f の引数が ARRAYISE(f) を用いるDEF文内の他の表現と衝突しないように注意しましょう。 |
EXISTS | nat または string である型 T0 に対し、 T0 型の変数 x と高階型 (T0,T1,…,Tn)->bool の関係 r に対し、 (x1,…,xn) に対し x0 <= x && r(x0,x1,…,xn) を満たす x0 の存在性を返す高階型 (T1,…,Tn)->bool の関係 EXISTS(N,r) を生成します。 |
UEXISTS | nat または string である型 T0 に対し、 T0 型の変数 x と高階型 (T0,T1,…,Tn)->bool の関係 r に対し、 (x1,…,xn) に対し x0 <= x && r(x0,x1,…,xn) を満たす x0 の一意存在性を返す高階型 (T1,…,T_n)->bool の関係 UEXISTS(n,r) を生成します。 |
3.4.4 ライブラリQpBasic
BASIC;
と書くことで、追加のライブラリであるQpBasic
に登録されている以下の演算子を使うことが出来ます:
演算子 | 高階型 | 説明 |
---|---|---|
Arrow | (nat,nat,nat)->nat | (a,c,b) に対しハイパー演算子で a ↑^c b を返します。 |
Chain | (nat,LDOTS(nat))->nat | (a,…) に対しチェーン表記で a → … を返します。 |
N | (string,nat,LDOTS(nat))->nat | (v,n,…) に対しその時点で定義されているN原始 Nv(…)[n] を返します。 |
3.5. 無視される文字列
半角空白か改行が(
か)
か,
か;
の前後どちらかに\(1\)個以上ある場合、それらは全て無視されます。連続した;
は1つの;
と同等に処理されます。コメントアウトされた文字列も無視されます。C++のコンパイラを使うので当然ですね。
3.6. C++による拡張
C++コンパイラがC++のコードも許容することを逆に利用して、C++を部分的に組み込むように\(\mathbb{Q}_p\)を拡張することが出来ます。例えば\(\mathbb{Q}_p\)におけるint
型の定数は1230
のような整数の符号付き十進法表記しか許されていませんでしたが、C++のコードで定義された関数int f(int n)
を用いてf(567)
などとしたものも\(\mathbb{Q}_p\)の定数として使うことができます。例えば変数x
に対するPUT(x,f(f(32)))
等は問題なくコンパイル出来ます。
3.7. 自然言語への翻訳
\(\mathbb{Q}_p\)のソースコードC
とC
に含まれるvoid
でない型またはDEF文の定める演算子である文字列s
と対応言語L
と出力形式S
とコンマを含まない文字列F
とオープンモードM
に対し、導入の章で述べた手順でint main(){C; WRITE(s,L,S,F,M); return 0;}
というコードをコンパイルして実行すると、s
が糖衣表現または演算子の時は標準出力にs
の定義文の構文木を出力し、いずれの場合もカレントディレクトリからの相対パスがF
のファイルに定義文の自然言語訳を書き込みます。
- パラメータ
L
により自然言語を指定します。L
がJAPANESE
の時、対応する言語は日本語です。L
がENGLISH
の時、対応する言語は英語です。L
がCHINESE
の時、対応する言語は中国です。ただしエディタが簡体字に対応していなかったため、繁体字となります。L
がFRENCH
の時、対応する言語はフランス語です。L
がGERMAN
の時、対応する言語はドイツ語です。
- パラメータ
S
はどの出力形式と整合的な文字列を書き出すかを指定します。S
がFANDOM
の時、ファイルに書き込まれた文字列をFANDOMで出力させるとDEF文の適切な数式が表示されます。S
がJEKYLL
の時、Jekyllで出力させるとDEF文の適切な数式が表示されるようにする予定です。(未対応)
- パラメータ
M
によりファイルのオープンモードを指定します。M
がAPP
の時、ファイルの末尾に文字列を書き加えます。M
がTRUNC
の時、ファイルを白紙化してから文字列を書き加えます。
ライブラリを導入するのが面倒という人はp進大好きbotにコードを渡して下さればファイルに書き込まれた文字列をお返しします。
3.8. C++への翻訳
\(\mathbb{Q}_p\)のソースコードC
がEXISTS
とUEXISTS
とUNIQUE
とMIN
とMAX
を含まない時、C
をC++のコードに翻訳することも可能です。
まず導入の章で述べた通常のコンパイル&リンク手順において、以下のようにインクルード対象を変更します。
- WindowsユーザーかつGCCユーザーは
RP/libqp.hpp
の代わりにRP/libqpc++.hpp
をインクルードする。 - 非Windowsユーザーまたは非GCCユーザーは
RP/cpp/Mathematics/Function/Computable/a.hpp
の代わりにRP/cpp/Mathematics/Function/Computable/Library/cpp/a.hpp
をインクルードする。
そしてコンマを含まない文字列F
とオープンモードM
に対しCONVERT_CPP(F,M); int main(){C; return 0;}
というコードをコンパイル&リンクをして生成された実行ファイルを実行すると、カレントディレクトリからの相対パスがF
のファイルにM
に対応するオープンモードでC
のC++への翻訳が書き込まれます。
書き込まれた翻訳をc
として、c
内のコメントアウトされた指示(インクルードするライブラリの相対パスの変更および関数の定義文内の変数の宣言)に従ってc
を 手動で変更し、c
の後に改行を入れてそれより後にint main()
の定義を記述することで、int main()
内でC
で定義された演算子や糖衣表現を使うことが出来ます。それを通常のC++のソースコードとしてコンパイル&リンクし、実行ファイルを実行することでint main()
内に現れるC
で定義された演算子や糖衣表現を実際に計算することができます。
ただし通常の手順でコンパイルできないような文字列でもこの操作でコンパイルできることがあります。そのためまずは通常の手順でコンパイルすることをおすすめします。またこの手順で実行する場合の挙動は、\(\mathbb{Q}_p\)に課されている実行時の制約のうち異常終了に関する制約が変更となります。詳しくは実行の章をご覧下さい。
4.実行
冒頭で述べましたように、\(\mathbb{Q}_p\)における演算子のDEF文を直接実行する機能を実装する予定は今のところないのですが、ひとまず「どのように実行されるか」は許容される文字列の分類名とそれらの定義においてカッコ書きで説明した部分を参考にして推測して下さい。C++へ翻訳した場合の実行はその規格に沿っていますので、C++へ翻訳することができるソースコードに関しては実質実行をすることが可能となります。
演算子のDEF文を実行する際の大雑把な流れは、実行中に何らかの演算子が呼ばれる時、その演算子のDEF文を更に実行する、というものです。C++による拡張を行った\(\mathbb{Q}_p\)の実行方法は、C++の関数が呼び出された箇所はC++の規格に従って実行し、そうでない箇所は通常の\(\mathbb{Q}_p\)の規格に従って実行します。
\(\mathbb{Q}_p\)における演算子のDEF文に対応する部分再帰関数の定義域は、そのDEF文の実行が正常終了するような入力全体の集合です。すなわち、それは無限ループを起こしてしまう入力および異常終了を起こしてしまう入力全体の集合の補集合となります。無限ループは演算子の自己再帰または相互再帰が停止しなかったり、無限ループする演算子の呼び出しを行ったりすることで生じます。
ここで注意が必要なのは、\(\mathbb{Q}_p\)における関係は(他のプログラミング言語における戻り値の型がbool
の関数と同様)必ずしも全域とは限らないことです。関係r
が全域でない時、見た目には完全な場合分けである「r(x)
ならば~である。r(x)
でないならば~である。」という場合分けが完全ではなくなります。何故なら「r(x)
である」という条件は「r(x)
の計算が停止しその出力が\(1\)である」ということに他ならない一方、「r(x)
でない」という文の実装は「! r(x)
である」となりすなわち「r(x)
の計算が停止しその出力が\(0\)である」ということになるからです。両者は同時に成立しない場合分けではありますが、r(x)
の計算が停止しない場合はどちらにも判定されません。それどころか、停止性すら未知であれば「いずれどちらかに判定されるのかこのまま計算し続けるのか」すら分かりません。これは「r(x)
ならば~である。r(x)
でないならば~である。」という場合分けに限った話ではなく、「r(x)
ならば~である」という場合分けを含むだけでも起こることですので、場合分けにはなるべく全域性の分かっている関係を使うようにしましょう。
さて、実行において表現e
がある型T
に属するとは、e
の型の基底型がT
の基底型T0
であり、かつT
をT0
から特殊化するまでに用いた全ての関係R
を満たすということですが、その確認にはR(e)
を呼び出す必要があるため、表向きは演算子を呼び出していない型の整合性の確認においても無限ループが生じる可能性があります。上記の事情から、SPEC構成をする場合もなるべく全域性の分かっている関係を使うようにしましょう。全域でない関係を用いてSPEC構成をした場合、対応する集合は再帰的集合である保証がありません。ちなみに再帰的集合\(X\)の再帰的とは限らない部分集合\(S\)上の計算可能関数とは、\(X\)上の計算可能関数であって計算規則が\(S\)の元にしか定義されていないもののこととします。\(S\)に対応する型を引数の型に持つ演算子はこの意味で、\(S\)上の計算可能関数に対応します。\(S\)が再帰的であろうとなかろうと、いわゆる計算不可能関数のようなものは作れないわけです。
なお、無限ループを起こしてしまう場合は実行時にエラーを出せません。一方で異常終了を起こしてしまった時は適切なエラーを標準出力する仕様を想定しています。例えばあえて「もし実行されたら規格に反してしまう命令行」をDEF文に仕込んでおきエラーの有無を確認することで、意図通りの処理が行われているかのデバッグが可能になります。ただしC++における例外処理と違い、そのような規格違反の処理は実行次第異常終了となります。以下に異常終了の条件を説明します。
4.1. EXIT文
DEF文の実行においてEXIT文が実行された場合は異常終了となります。
例えばbool
型の表現b
に対するIF(b,EXIT)
という命令行が型int
のDEF文に含まれることは許容されますが、この文が実行される時にb
がtrue
として評価されることが想定されていないという意思表示になります。例えば万が一b
がtrue
になる場合はコードにミスがあるのでエラーを出したい時や、b
がtrue
になるような引数は演算子の定義域に含まないことを明示したい時にそのような文を使いましょう。
ただしC++への翻訳時はこの異常終了を無視し、代わりに例外をThrowします。
4.2. 自由変数の使用
変数x
がDEF文\(D\)の実行において最初に参照される時、実行中の命令行をL
と置くと、以下の2条件のいずれかを満たさなければ異常終了となります:
x
は\(D\)の定める演算子の引数の成分である。L
はx
のPUT文である。
要するに引数の成分であるかPUT文を一度実行した変数しか参照してはいけません。それは未定義な変数の使用となります。
ただしC++への翻訳時はこの異常終了を無視します。
4.3. 変数の書き換え
変数x
がDEF文\(D\)の実行において参照される時、実行中の命令行がx
のPUT文であるならば、以下の3条件の両方を満たさなければ異常終了となります:
x
は\(D\)の定める演算子の引数の成分でない。- これ以前に
x
は一度も参照されていない。 x
の型をT
と置くと、x
を定める表現がT
に属する。
要するにPUT文を実行できるのは、引数の成分でない変数を最初に参照した時に限り、かつx
を定める表現がx
の型と適合する場合です。例えば引数をPUT文で書き換えたり、既にPUT文を実行して束縛された変数を再度PUT文で書き換えたりしてはいけません。従って\(\mathbb{Q}_p\)における変数は一般のプログラミング言語における変数と違い、引数の成分でない限りは実質定数として振る舞うということになります。これは通常の数学における束縛変数の扱いと似せた仕様です。
ただしC++への翻訳時はこの異常終了を無視します。
4.4. 未定義な作用素の使用
\(\mathbb{Q}_p\)で許容される文字列C
内のDEF文の実行において作用素g
が参照される時、以下の2条件の両方を満たさなければ異常終了となります:
C
がg
のDEF文を含む。g
の引数の型をTs
と置くと、g
に代入された表現リストがそれぞれ対応するTs
の各成分に属する。
要するに未定義の作用素を参照してはいけません。実行中のDEF文自身がg
のDEF文であっても良いので、自分自身を呼び出す再帰や相互再帰は可能となります。ただし無限ループの有無は判定できません。
ただしC++への翻訳時は1.に該当する未定義な作用素の評価を含むソースコードに対しリンクエラーが起こるためそもそも実行できず、また2.に該当する未定義な作用素の評価を行う場合の異常終了を無視します。
4.5. DEF文末への到達
DEF文の実行においてDEF文末へ到達した場合は異常終了となります。
ただしC++への翻訳時はこの異常終了を無視するか、または未定義動作を起こします。