C++言語編

(windows 10版)

 

C++言語はB.ストラウストラップ(Bjarne Stroustrup)博士によって1980年代初頭に設計されたプログラム言語として有名です。C言語文法との上位交換性があり、抱負な機能が盛り込まれていることでも知られています。ここでは、そのすべてについて日本語を使ったC++言語の適用例を挙げることはとうていできません。従ってほんの一部ではありますが、今回のプログラミング言語で日本語多めにというテーマで、いくつかの実例を紹介してみたいと思います。

もちろんプログラム言語について何も知らない方が、いきなりC++言語を習得しようとしても大変難しいのではないかと思います。まずはC言語の流儀に、ある程度慣れてからC++言語に進むのがよいのではないでしょうか。今回もEmbarcadero Technologies社 (旧ボーランド社)の処理系であるBCC32.EXE(C++バージョン7.4、リンカーバージョン6.9)を使用して、日本語多めの検証をしてみました。この処理系は入力ファイルの拡張子が「.c」であれば、C言語として処理しますが、「.cpp」であれば自動的にC++言語として処理をしてくれます。

また、C言語編と同じように、日本語で書かれたC++言語ファイルの名前を仮にファイル1.jcppとすると

>jcpp10 ファイル1

というバッチコマンドによりファイル1.cppを生成します。

さらに翻訳(コンパイル)、結合(リンク)して、実行可能形式ファイルであるファイル1.exeを生成します。jcpp10という日本語カスタマイザー実行プログラムはC言語の場合とほとんど同じ仕様です。

Windows 7版はこちら

Windows XP版はこちら

ここで、日本語定義の領域がかなり大きくなってきましたので、C++言語編からは自分でよく使う予約語や演算子などの日本語定義部分をプログラムとは別に、「std_include10.jcpp」として外部である程度まとめて宣言できるようにしておきます。 そして処理するときにバッチコマンドの機能を利用し、宣言ファイルと日本語C++ファイルを結合します。このように日本語定義部分を通常のC++言語の文法に合うように置き換えるという処理をします。

以下は、別に定義した日本語C++宣言ファイルの内容です。

#日本語定義 整数型     "int"
#日本語定義 文字型     "char"
#日本語定義 実数型     "float"

#日本語定義 定数指定     "const"
#日本語定義 論理型        "bool"
#日本語定義 外部定義     "extern"
#日本語定義 汎用ひな型   "template"
#日本語定義 型定義        "typedef"
#日本語定義 汎用型名     "typename"

#日本語定義 型なし              "void"
#日本語定義 構造体             "struct"
#日本語定義 共用体             "union"
#日本語定義 仲間の集まり       "class"
#日本語定義 全域で使用可       "public"
#日本語定義 仲間内で使用可     "private"
#日本語定義 派生の仲間まで使用可 "protected"
#日本語定義 直接出入可         "friend"

#日本語定義 である、         "::"
#日本語定義 スコープ解決     "::"
#日本語定義 標準的な名前空間 "using namespace std"
#日本語定義 内部埋め込み       "inline"
#日本語定義 文字列の仲間       "string"

#日本語定義 もし      "if"
#日本語定義 それ以外  "else"
#日本語定義 戻る      "return"
#日本語定義 繰り返し  "while"
#日本語定義 反復      "for"

#日本語定義 読み込む "cin"
#日本語定義 表示する "cout" //例2─1はこちらを使う
#日本語定義 印字する "cout" //例2─2以降はこちらを使う

#日本語定義 改行     "endl"
#日本語定義 二行改行 "endl << endl"

#日本語定義 集合型    "Set"


#日本語定義 ←       "<<"
#日本語定義 →       ">>"
#日本語定義 番地|   "*"
#日本語定義 参照|   "&"
#日本語定義 中味|   "*"
#日本語定義 かつ      "&&"
#日本語定義 または   "||"


#日本語定義 真       "true"
#日本語定義 偽       "false"

#日本語定義 はじまり  "main"

#日本語定義 通常終了 "return 0"
#日本語定義 強制終了 "exit(1)"

#日本語定義 入出力に関する前処理       "#include <iostream>"
#日本語定義 文字列入出力に関する前処理 "#include <sstream>"
#日本語定義 数学に関する前処理         "#include <math.h>"
#日本語定義 文字列に関する前処理       "#include <string>"
#日本語定義 システムに関する前処理     "#include <system.hpp>"

これ以降の例題では、プログラム内に上記の日本語定義の部分はありません。

例題2-1 うるう年かどうかを判定するプログラム

例題2-2 クラスを使った簡単な計算

例題2-3 参照型、ポインタ、配列

例題2-4 応用演算

例題2-5 曜日を求めるプログラム

例題2-6 長いπの計算

GUIについて

例題2-7 集合計算

デバッグについて

例題2-1 うるう年かどうかを判定するプログラム

はじめに、例2-1として西暦年を入力し、その年がうるう年かどうかを判定するプログラムをcpp_ex1.jcppとして示します。 今度のC++コンパイラのバージョン7.4の大きな特徴として、変数や関数名などに日本語がそのまま使えるようになったことが挙げられます。 そこで、プログラム中でも今まで日本語カスタマイザーの自動変数として定義していた日本語定義の部分は、そのまま直接プログラム中で使うことにします。

// 西暦を入力し、その年がうるう年かどうかを判定する
入出力に関する前処理

標準的な名前空間;

#日本語定義 うるう年である        "true"
#日本語定義 うるう年ではない      "false"
#日本語定義 がうるう年である      "== true"
#日本語定義 剰余計算               "%"
#日本語定義 が割り切れる          "== 0"
#日本語定義 ならば                ""
#日本語定義 どちらか大きい方      "max"

論理型 うるう年の判定( 整数型 );

内部埋め込み 論理型 うるう年の判定( 整数型 その年 )
{
                 もし ( ( その年 剰余計算 400 ) が割り切れる ) ならば 戻る( うるう年である );
        それ以外 もし ( ( その年 剰余計算 100 ) が割り切れる ) ならば 戻る( うるう年ではない );
        それ以外 もし ( ( その年 剰余計算    4 ) が割り切れる ) ならば 戻る( うるう年である );
        それ以外 ならば 戻る( うるう年ではない );
}

型なし はじまり( 型なし )
{
        整数型 年のデータ = 1;

        印字する ← "年(西暦)を入力してください。(1以上の数字)" ← 改行;
        読み込む → 年のデータ;
        年のデータ = どちらか大きい方( 年のデータ, 1 ); // 必ず入力値を1以上とする。

        印字する ← "西暦" ← 年のデータ ← "年は";
        印字する ← ( うるう年の判定(年のデータ) ? "うるう年である。":"うるう年でない。" ) ← 改行 ;
}

例2-1

西暦2000年問題という、うるう年の判定について、以前随分と話題になったことを覚えていらっしゃる方も多いと思いますが、うるう年かどうかの判断もややこしく、判断の順番を間違えないようにする必要があるようです。

また、C++言語になって、今までの"/*...*/"に加えて、コメントに"//"が使用できますので、FLEXの日本語解析プログラムもC++言語のコメント対応に変更しました。さらに変数の宣言は、変数を使う場所で宣言することができるのもC++言語の特徴ではないでしょうか。これは次のようなC++言語のコードに展開されます。

// 西暦を入力し、その年がうるう年かどうかを判定する
#include <iostream>

using namespace std;


bool うるう年の判定( int );

inline bool うるう年の判定( int その年 )
{
              if ( ( その年 % 400 ) == 0 ) return( true );
        else if ( ( その年 % 100 ) == 0 ) return( false );
        else if ( ( その年 % 4 )    == 0 ) return( true );
        else return( false );
}

void main( void )
{
        int 年のデータ = 1;

        cout << "年(西暦)を入力してください。(1以上の数字)" << endl;
        cin >> 年のデータ;
        年のデータ = max( 年のデータ, 1 ); // 必ず入力値を1以上とする。

        cout << "西暦" << 年のデータ << "年は";
        cout << ( うるう年の判定(年のデータ) ? "うるう年である。":"うるう年でない。" ) << endl ;
}

例2-1を展開したもの

このcpp_ex1.jcppを日本語カスタマイザーで処理し、コンパイル、リンクします。

下記は例2-1のプログラムを実行させ、西暦を2019年、2020年としたときの結果ですが、最小値は1(西暦1年)にしています。

例題2-2 クラスを使った簡単な計算

次の例は、C++言語の目玉ともいうべき「クラス」を使ってみたものです。ここでは「クラス」を「仲間の集まり」という日本語で置き換えてみました。C言語の構造体が変数の集まりとして定義されていたのに対して、C++言語のクラスは変数だけではなく、関数も定義できるようにしてあるのが特徴といえるのではないでしょうか。 ここでもプログラム中のクラス名にそのまま日本語を使ってみます。

この例2─2では、簡単な計算を行う関数を「基本計算の仲間」として定義してあります。この関数はクラスのメンバー関数と呼ばれていますが、これを内部埋め込み(インライン)関数としました。

// 簡単な計算をする仲間の集まり(クラス)を作り、計算してみる
入出力に関する前処理

標準的な名前空間;

仲間の集まり 基本計算の仲間
{

全域で使用可:

        基本計算の仲間(型なし) { 表示する ← "基本計算の対象(オブジェクト)を組み立てる" ← 改行; };

        実数型 足す ( 実数型, 実数型 );
        実数型 引く ( 実数型, 実数型 );
        実数型 掛ける( 実数型, 実数型 );
        実数型 割る ( 実数型, 実数型 );
        整数型 余り ( 整数型, 整数型 );

        論理型 論理積( 論理型, 論理型 );
        論理型 論理和( 論理型, 論理型 );

        ~基本計算の仲間(型なし) { 表示する ← "基本計算の対象(オブジェクト)を消滅させる" ← 改行; };

};


内部埋め込み 実数型 基本計算の仲間である、足す ( 実数型 引数1, 実数型 引数2 ) { 戻る( 引数1 + 引数2 ); };
内部埋め込み 実数型 基本計算の仲間である、引く ( 実数型 引数1, 実数型 引数2 ) { 戻る( 引数1 - 引数2 ); };
内部埋め込み 実数型 基本計算の仲間である、掛ける( 実数型 引数1, 実数型 引数2 ) { 戻る( 引数1 * 引数2 ); };
内部埋め込み 実数型 基本計算の仲間である、割る ( 実数型 引数1, 実数型 引数2 ) { 戻る( 引数1 / 引数2 ); };
内部埋め込み 整数型 基本計算の仲間である、余り ( 整数型 引数1, 整数型 引数2 ) { 戻る( 引数1 % 引数2 ); };
内部埋め込み 論理型 基本計算の仲間である、論理積( 論理型 引数1, 論理型 引数2 ) { 戻る( 引数1 && 引数2 ); };
内部埋め込み 論理型 基本計算の仲間である、論理和( 論理型 引数1, 論理型 引数2 ) { 戻る( 引数1 || 引数2 ); };

型なし はじまり(型なし)
{

        基本計算の仲間 計算くん;

        表示する ← "567 足す 2019 は " ← 計算くん.足す ( 567, 2019 ) ← 改行;
        表示する ← "109.73 引く 18 は " ← 計算くん.引く ( 109.73, 18 ) ← 改行;
        表示する ← "369 掛ける 36.1 は " ← 計算くん.掛ける( 369, 36.1 ) ← 改行;
        表示する ← "123.4 割る 9.6 は " ← 計算くん.割る ( 123.4, 9.6 ) ← 改行;
        表示する ← "365を7で割った余りは " ← 計算くん.余り ( 365, 7 ) ← 改行;

        表示する ← "真と真の論理積は " ← ( 計算くん.論理積( 真, 真 ) ? "真" : "偽" ) ← 改行;
        表示する ← "真と偽の論理積は " ← ( 計算くん.論理積( 真, 偽 ) ? "真" : "偽" ) ← 改行;
        表示する ← "偽と真の論理積は " ← ( 計算くん.論理積( 偽, 真 ) ? "真" : "偽" ) ← 改行;
        表示する ← "偽と偽の論理積は " ← ( 計算くん.論理積( 偽, 偽 ) ? "真" : "偽" ) ← 改行;

        表示する ← "真と真の論理和は " ← ( 計算くん.論理和( 真, 真 ) ? "真" : "偽" ) ← 改行;
        表示する ← "真と偽の論理和は " ← ( 計算くん.論理和( 真, 偽 ) ? "真" : "偽" ) ← 改行;
        表示する ← "偽と真の論理和は " ← ( 計算くん.論理和( 偽, 真 ) ? "真" : "偽" ) ← 改行;
        表示する ← "偽と偽の論理和は " ← ( 計算くん.論理和( 偽, 偽 ) ? "真" : "偽" ) ← 改行;
        表示する ← 改行;

}

例2-2

さらに、C++言語では入出力ストリーム(iostreamクラスライブラリ)で使われる">>"と"<<"記号が入出力ストリーム演算子として使われていますが、これらはビットシフト演算子と兼用してあるかのように見えます。しかし、同じ記号を別の意味で使用すると混乱を招きやすいということもあり、これを「→」と「←」の2バイト記号で日本語定義として置き換えてみました。例2─2は次のようなC++言語のコードに展開されます。

// 簡単な計算をする仲間の集まり(クラス)を作り、計算してみる
#include <iostream>

using namespace std;

class 基本計算の仲間
{

public:

        基本計算の仲間(void) { cout << "基本計算の対象(オブジェクト)を組み立てる" << endl; };

        float 足す ( float, float );
        float 引く ( float, float );
        float 掛ける( float, float );
        float 割る ( float, float );
        int 余り ( int, int );

        bool 論理積( bool, bool );
        bool 論理和( bool, bool );

        ~基本計算の仲間(void) { cout << "基本計算の対象(オブジェクト)を消滅させる" << endl; };

};


inline float 基本計算の仲間::足す ( float 引数1, float 引数2 ) { return( 引数1 + 引数2 ); };
inline float 基本計算の仲間::引く ( float 引数1, float 引数2 ) { return( 引数1 - 引数2 ); };
inline float 基本計算の仲間::掛ける( float 引数1, float 引数2 ) { return( 引数1 * 引数2 ); };
inline float 基本計算の仲間::割る ( float 引数1, float 引数2 ) { return( 引数1 / 引数2 ); };
inline int 基本計算の仲間::余り ( int 引数1, int 引数2 ) { return( 引数1 % 引数2 ); };
inline bool 基本計算の仲間::論理積( bool 引数1, bool 引数2 ) { return( 引数1 && 引数2 ); };
inline bool 基本計算の仲間::論理和( bool 引数1, bool 引数2 ) { return( 引数1 || 引数2 ); };

void main(void)
{

基本計算の仲間 計算くん;

        cout << "567 足す 2019 は " << 計算くん.足す ( 567, 2019 ) << endl;
        cout << "109.73 引く 18 は " << 計算くん.引く ( 109.73, 18 ) << endl;
        cout << "369 掛ける 36.1 は " << 計算くん.掛ける( 369, 36.1 ) << endl;
        cout << "123.4 割る 9.6 は " << 計算くん.割る ( 123.4, 9.6 ) << endl;
        cout << "365を7で割った余りは " << 計算くん.余り ( 365, 7 ) << endl;

        cout << "真と真の論理積は " << ( 計算くん.論理積( true, true ) ? "真" : "偽" ) << endl;
        cout << "真と偽の論理積は " << ( 計算くん.論理積( true, false ) ? "真" : "偽" ) << endl;
        cout << "偽と真の論理積は " << ( 計算くん.論理積( false, true ) ? "真" : "偽" ) << endl;
        cout << "偽と偽の論理積は " << ( 計算くん.論理積( false, false ) ? "真" : "偽" ) << endl;

        cout << "真と真の論理和は " << ( 計算くん.論理和( true, true ) ? "真" : "偽" ) << endl;
        cout << "真と偽の論理和は " << ( 計算くん.論理和( true, false ) ? "真" : "偽" ) << endl;
        cout << "偽と真の論理和は " << ( 計算くん.論理和( false, true ) ? "真" : "偽" ) << endl;
        cout << "偽と偽の論理和は " << ( 計算くん.論理和( false, false ) ? "真" : "偽" ) << endl;
        cout << endl;

}

例2-2を展開したもの

例2─2を実行した結果を以下に示します。「計算くん」という「基本計算の仲間」クラスの対象(オブジェクト)が生成されると、最初にコンストラクタと呼ばれる部分が起動します。あとは対象(オブジェクト)のメンバー関数を呼ぶだけで、簡単な計算を行うことができます。最後にデストラクタと呼ばれる部分が起動し、対象(オブジェクト)が消滅します。

例題2-3 参照型、ポインタ、配列

C言語編でも少し述べましたが、C++言語では参照型という新しい型が定義されています。これには「&」というアドレスを表す記号が使われております。次の例2─3では、その参照型で宣言した変数がどのような振る舞いをするのかを見てみます。また、ポインタを表す「*」記号について、やはりC言語編と同じように「番地|」、「中味|」という日本語定義で表記したときに、どのように記述されるのかも併せて見ていきます。

// 参照型とポインタ、配列についての例
入出力に関する前処理
標準的な名前空間;

整数型 はじまり(型なし)
{
        整数型 実際の数;
        整数型 参照|その分身 = 実際の数; // 宣言文で参照型を定義します

        実際の数 = 2019; // 実際の数に定数を代入すると、その分身にも代入されます。
        表示する ← "参照型の例" ← 改行;
        表示する ← "実際の数="← 実際の数 ← " その分身=" ← その分身 ← 改行;

        --その分身; // その分身を減少させると、実際の数も減ります。
        表示する ← "分身を1減らす" ← 改行;
        表示する ← "実際の数="← 実際の数 ← " その分身=" ← その分身 ← 改行;

        実際の数+=2; // 実際の数を増加させると、その分身も増えます。
        表示する ← "実際を2増やす" ← 改行;
        表示する ← "実際の数="← 実際の数 ← " その分身=" ← その分身 ← 改行;

        整数型 番地|もう一つの数; // もう一つの数は整数型への番地であると定義します。

        表示する ← "ポインタの例" ← 改行;
        表示する ← "番地型変数「もう一つの数」が「その分身」を参照できるようにします。" ← 改行;
        もう一つの数 = 参照|その分身; // もう一つの数がその分身を参照できるようにします。
        表示する ←"もう一つの数="← 中味|もう一つの数 ← 改行;

        表示する ← "配列の例" ← 改行;
        整数型 他の数[2] = {88,360}; // 他の数は整数型の初期値を持った配列を表します。
        表示する ← "他の数=" ← 他の数[0] ← " "←他の数[1]← 改行;
        表示する ← "番地型変数「もう一つの数」が配列「他の数」を参照できるようにします。" ←改行;
        もう一つの数 = 参照|他の数[0]; // 配列の要素に対応する番地をもう一つの数に代入します。
        表示する ← "もう一つの数=" ← 中味|もう一つの数 ← " ";
        もう一つの数 = 参照|他の数[1];
        表示する ← 中味|もう一つの数 ← 改行;

        通常終了;

}

例2-3

このように実際の数(通常の変数)と、その分身(参照型変数)のどちらを変えても同じように操作されることが分かります。これは次のようなC++言語のコードに展開されます。

// 参照型とポインタ、配列についての例
#include <iostream>
using namespace std;

int main(void)
{
        int 実際の数;
        int &その分身 = 実際の数; // 宣言文で参照型を定義します

        実際の数 = 2019; // 実際の数に定数を代入すると、その分身にも代入されます。
        cout << "参照型の例" << endl;
        cout << "実際の数="<< 実際の数 << " その分身=" << その分身 << endl;

        --その分身; // その分身を減少させると、実際の数も減ります。
        cout << "分身を1減らす" << endl;
        cout << "実際の数="<< 実際の数 << " その分身=" << その分身 << endl;

        実際の数+=2; // 実際の数を増加させると、その分身も増えます。
        cout << "実際を2増やす" << endl;
        cout << "実際の数="<< 実際の数 << " その分身=" << その分身 << endl;

        int *もう一つの数; // もう一つの数は整数型への番地であると定義します。

        cout << "ポインタの例" << endl;
        cout << "番地型変数「もう一つの数」が「その分身」を参照できるようにします。" << endl;
        もう一つの数 = &その分身; // もう一つの数がその分身を参照できるようにします。
        cout <<"もう一つの数="<< *もう一つの数 << endl;

        cout << "配列の例" << endl;
        int 他の数[2] = {88,360}; // 他の数は整数型の初期値を持った配列を表します。
        cout << "他の数=" << 他の数[0] << " "<<他の数[1]<< endl;
        cout << "番地型変数「もう一つの数」が配列「他の数」を参照できるようにします。" <<endl;
        もう一つの数 = &他の数[0]; // 配列の要素に対応する番地をもう一つの数に代入します。
        cout << "もう一つの数=" << *もう一つの数 << " ";
        もう一つの数 = &他の数[1];
        cout << *もう一つの数 << endl;

        return 0;

}

例2-3を展開したもの

例2-3のプログラムを実行させた結果は以下のとおりです。

今回はC言語編と同じように「&」、「*」を日本語定義で「参照|」、「番地|」、「中味|」と置き換えて使ってみましたが、C++言語でも特に問題なく使えるのではないかと思っております。読者の皆さんはいかがでしょうか。

話は変わりますが、C++言語でのmain関数が特に値を返さない場合には、voidと書いておけば処理系から文句が出ることはありません。しかし、一応どのように書いた方が、読んだ方に納得してもらえるかのだろうかと思っておりました。これは今までの例題とは直接関係はありませんが、現在使用しておりますボーランド社のC++ビルダーでは、main関数の最後にreturn 0と自動的にコードが記述されるようですので、今回はmain関数をint型として宣言しておき、最後にreturn 0で終了しております。

例題2-4 応用演算

次は例2─2で簡単な四則演算と論理演算程度の計算を行う仲間の集まり(クラス)を定義しましたが、これを少し発展させてみたいと思います。その機能拡張として、平方根、累乗、消費税計算の機能を加えてみます。

新しい計算の仲間は、応用計算を基本計算から継承させる仲間の集まり(クラス)として定義しております。また今回はクラス名を日本語カスタマイザーの自動変数定義で行いました。

// 応用計算を基本計算から継承させる仲間の集まり(クラス)を作る
入出力に関する前処理
数学に関する前処理

標準的な名前空間;

#日本語定義 基本計算の仲間
#日本語定義 応用計算の仲間

仲間の集まり 基本計算の仲間
{

全域で使用可:

        基本計算の仲間(型なし) { 表示する ← "基本計算の対象を組み立てる" ← 改行; };

        実数型 足す ( 実数型, 実数型 );
        実数型 引く ( 実数型, 実数型 );
        実数型 掛ける( 実数型, 実数型 );
        実数型 割る ( 実数型, 実数型 );
        整数型 余り ( 整数型, 整数型 );

        論理型 論理積( 論理型, 論理型 );
        論理型 論理和( 論理型, 論理型 );

        ~基本計算の仲間(型なし) { 表示する ← "基本計算の対象を消滅させる" ← 改行; };

};

仲間の集まり 応用計算の仲間 : 全域で使用可 基本計算の仲間
{

全域で使用可:

        応用計算の仲間(型なし) { 表示する ← "応用計算の仲間の対象を組み立てる" ← 改行; };

        実数型 平方根を求める( 実数型 引数一 ) ;
        実数型 累乗を求める( 実数型 引数一, 実数型 引数二 ) ;
        整数型 税込み価格を求める( 整数型 引数一 ) ;

        ~応用計算の仲間(型なし) { 表示する ← "応用計算の仲間の対象を消滅させる" ← 改行; };

};

内部埋め込み 実数型 基本計算の仲間::足す ( 実数型 引数一, 実数型 引数二 ) { 戻る( 引数一 + 引数二 ); };
内部埋め込み 実数型 基本計算の仲間::引く ( 実数型 引数一, 実数型 引数二 ) { 戻る( 引数一 - 引数二 ); };
内部埋め込み 実数型 基本計算の仲間::掛ける( 実数型 引数一, 実数型 引数二 ) { 戻る( 引数一 * 引数二 ); };
内部埋め込み 実数型 基本計算の仲間::割る ( 実数型 引数一, 実数型 引数二 ) { 戻る( 引数一 / 引数二 ); };
内部埋め込み 整数型 基本計算の仲間::余り ( 整数型 引数一, 整数型 引数二 ) { 戻る( 引数一 % 引数二 ); };
内部埋め込み 論理型 基本計算の仲間::論理積( 論理型 引数一, 論理型 引数二 ) { 戻る( 引数一 && 引数二 ); };
内部埋め込み 論理型 基本計算の仲間::論理和( 論理型 引数一, 論理型 引数二 ) { 戻る( 引数一 || 引数二 ); };

#日本語定義 平方根 "sqrt"
#日本語定義 累乗 "pow"
#日本語定義 切り捨て "floor"
#日本語定義 消費税率 "1.08"


実数型 応用計算の仲間::平方根を求める( 実数型 引数一)
{
        戻る( 平方根(引数一) );
};

実数型 応用計算の仲間::累乗を求める( 実数型 引数一, 実数型 引数二 )
{
        戻る( 累乗(引数一,引数二) );
};

整数型 応用計算の仲間::税込み価格を求める( 整数型 引数一)
{
        戻る( 切り捨て(引数一 * 消費税率) );
};

整数型 はじまり(型なし)
{

        応用計算の仲間 計算くん;

        表示する ← 改行;
        表示する ← "567 足す 2019 は " ← 計算くん.足す( 567, 2019 ) ← 改行;
        表示する ← "109.73 引く 18 は " ← 計算くん.引く( 109.73, 18 ) ← 改行;
        表示する ← "36 掛ける 369 は " ← 計算くん.掛ける( 36, 369 ) ← 改行;
        表示する ← "123.4 割る 9.6 は " ← 計算くん.割る( 123.4, 9.6 ) ← 改行;
        表示する ← "365を7で割った余りは " ← 計算くん.余り( 365, 7 ) ← 改行;

        表示する ← "真と真の論理積は " ← ( 計算くん.論理積( 真, 真 ) ? "真" : "偽" ) ← 改行;
        表示する ← "真と偽の論理積は " ← ( 計算くん.論理積( 真, 偽 ) ? "真" : "偽" ) ← 改行;
        表示する ← "偽と真の論理積は " ← ( 計算くん.論理積( 偽, 真 ) ? "真" : "偽" ) ← 改行;
        表示する ← "偽と偽の論理積は " ← ( 計算くん.論理積( 偽, 偽 ) ? "真" : "偽" ) ← 改行;

        表示する ← "真と真の論理和は " ← ( 計算くん.論理和( 真, 真 ) ? "真" : "偽" ) ← 改行;
        表示する ← "真と偽の論理和は " ← ( 計算くん.論理和( 真, 偽 ) ? "真" : "偽" ) ← 改行;
        表示する ← "偽と真の論理和は " ← ( 計算くん.論理和( 偽, 真 ) ? "真" : "偽" ) ← 改行;
        表示する ← "偽と偽の論理和は " ← ( 計算くん.論理和( 偽, 偽 ) ? "真" : "偽" ) ← 改行;

        表示する ← "3の平方根は " ← 計算くん.平方根を求める( 3 ) ← 改行;
        表示する ← "7の4乗は " ← 計算くん.累乗を求める( 7, 4 ) ← 改行;
        表示する ← "税抜き865円の商品の消費税込み価格は" ←
        計算くん.税込み価格を求める(865) ← "円" ← 改行;
        表示する ← 改行;


        通常終了;
}

例2─4

例2─4の日本語定義プログラムは次のようなC++言語のコードに展開されます。

// 応用計算を基本計算から継承させる仲間の集まり(クラス)を作る
#include <iostream>
#include <math.h>

using namespace std;

class jp_str_0
{

public:

        jp_str_0(void) { cout << "基本計算の対象を組み立てる" << endl; };

        float 足す ( float, float );
        float 引く ( float, float );
        float 掛ける( float, float );
        float 割る ( float, float );
        int 余り ( int, int );

        bool 論理積( bool, bool );
        bool 論理和( bool, bool );

        ~jp_str_0(void) { cout << "基本計算の対象を消滅させる" << endl; };

};

class jp_str_1 : public jp_str_0
{

public:

        jp_str_1(void) { cout << "応用計算の仲間の対象を組み立てる" << endl; };

        float sqrtを求める( float 引数一 ) ;
        float powを求める( float 引数一, float 引数二 ) ;
        int 税込み価格を求める( int 引数一 ) ;

        ~jp_str_1(void) { cout << "応用計算の仲間の対象を消滅させる" << endl; };

};

inline float jp_str_0::足す ( float 引数一, float 引数二 ) { return( 引数一 + 引数二 ); };
inline float jp_str_0::引く ( float 引数一, float 引数二 ) { return( 引数一 - 引数二 ); };
inline float jp_str_0::掛ける( float 引数一, float 引数二 ) { return( 引数一 * 引数二 ); };
inline float jp_str_0::割る ( float 引数一, float 引数二 ) { return( 引数一 / 引数二 ); };
inline int jp_str_0::余り ( int 引数一, int 引数二 ) { return( 引数一 % 引数二 ); };
inline bool jp_str_0::論理積( bool 引数一, bool 引数二 ) { return( 引数一 && 引数二 ); };
inline bool jp_str_0::論理和( bool 引数一, bool 引数二 ) { return( 引数一 || 引数二 ); };



float jp_str_1::sqrtを求める( float 引数一)
{
        return( sqrt(引数一) );
};

        float jp_str_1::powを求める( float 引数一, float 引数二 )
{
        return( pow(引数一,引数二) );
};

int jp_str_1::税込み価格を求める( int 引数一)
{
        return( floor(引数一 * 1.08) );
};

int main(void)
{

        jp_str_1 計算くん;

        cout << endl;
        cout << "567 足す 2019 は " << 計算くん.足す( 567, 2019 ) << endl;
        cout << "109.73 引く 18 は " << 計算くん.引く( 109.73, 18 ) << endl;
        cout << "36 掛ける 369 は " << 計算くん.掛ける( 36, 369 ) << endl;
        cout << "123.4 割る 9.6 は " << 計算くん.割る( 123.4, 9.6 ) << endl;
        cout << "365を7で割った余りは " << 計算くん.余り( 365, 7 ) << endl;

        cout << "真と真の論理積は " << ( 計算くん.論理積( true, true ) ? "真" : "偽" ) << endl;
        cout << "真と偽の論理積は " << ( 計算くん.論理積( true, false ) ? "真" : "偽" ) << endl;
        cout << "偽と真の論理積は " << ( 計算くん.論理積( false, true ) ? "真" : "偽" ) << endl;
        cout << "偽と偽の論理積は " << ( 計算くん.論理積( false, false ) ? "真" : "偽" ) << endl;

        cout << "真と真の論理和は " << ( 計算くん.論理和( true, true ) ? "真" : "偽" ) << endl;
        cout << "真と偽の論理和は " << ( 計算くん.論理和( true, false ) ? "真" : "偽" ) << endl;
        cout << "偽と真の論理和は " << ( 計算くん.論理和( false, true ) ? "真" : "偽" ) << endl;
        cout << "偽と偽の論理和は " << ( 計算くん.論理和( false, false ) ? "真" : "偽" ) << endl;

        cout << "3の平方根は " << 計算くん.sqrtを求める( 3 ) << endl;
        cout << "7の4乗は " << 計算くん.powを求める( 7, 4 ) << endl;
        cout << "税抜き865円の商品の消費税込み価格は" <<
        計算くん.税込み価格を求める(865) << "円" << endl;
        cout << endl;


return 0;
}

例2-4を展開したもの

例2-4のプログラムを実行させた結果は以下のとおりです。

このようにオブジェクト指向言語といわれるC++言語では、仲間の集まり(クラス)を継承することで、今あるソフトウェア財産をそのまま利用して無駄なく機能拡張ができます。さらに対象(オブジェクト)の生成では、まず基本(親)となる仲間の集まり(クラス)が生成され、次に継承(子)された仲間の集まり(クラス)が生成されることが、この実行結果により分かります。また、対象(オブジェクト)の消滅では、逆に継承(子)された仲間の集まり(クラス)が消滅して、最後に基本(親)となる仲間の集まり(クラス)が消滅しますので、生成と消滅はちょうど入れ子のような格好になっていることも分かります。

例題2-5 曜日を求めるプログラム

次の例は、平成1年(西暦1989年)の1月1日が日曜日であったことを利用して、指定した日の曜日を求めるプログラムです。西暦と年月日を入力すると、曜日と元号を表示するプログラムです。 ただし、1989年は平成元年ではありますが昭和の最後の週の年でもありますので、元号は「昭和64年/平成元年」としています。また、2019年の元号は「平成31年/令和元年」としています。 なお、1989年より小さい西暦年や2099年より大きい西暦年を入力するとプログラムは終了するようにもしています。

Java言語編でも説明しておりますが、まず1年365日は7で割った余りを求めると1になります。つまり、うるう年がなければ1年たつと曜日が1日ずれることになります。さらに、各月についても、うるう年がなければ毎月それぞれ規則的にずれます。このうるう年のずれについては、また別に計算して最後にすべての補正値を合計します。そして、この補正値の合計を7で割った余りを求めれば、指定した年月日の曜日が求められます。

年の補正値は、毎年1を足すだけで求められますが、月の補正値は表を用いております。しかし、うるう年の計算だけは、平成12年が西暦2000年という特殊な年ですので、毎年うるう年かどうかの判定を行い補正値を加算させました。

今回は、Java言語編と同様に仲間の集まり(クラス)をいくつかに分けて作成することにしました。また、宣言されているクラス内からだけアクセスすることができる仲間内で使用可(private)というアクセス修飾子を使ってデータのカプセル化を行っております。

//平成1(1989)年の1月1日が日曜日であったことを利用して、指定した日の曜日を求める
入出力に関する前処理
文字列に関する前処理

標準的な名前空間;

#日本語定義 年のデータ "y_val"
#日本語定義 月のデータ "m_val"
#日本語定義 日のデータ "d_val"

#日本語定義 ならば ""
#日本語定義 の個数が正常 ".length == 3"
#日本語定義 年月日が正常範囲である

#日本語定義 うるう年である "true"
#日本語定義 うるう年ではない "false"
#日本語定義 がうるう年である "== true"
#日本語定義 剰余計算 "%"
#日本語定義 が割り切れる "== 0"
#日本語定義 が割り切れない "!= 0"

#日本語定義 年が正常範囲 "((1989 <= y_val)&&(y_val <= 2099))"
#日本語定義 月が正常範囲 "((1 <= m_val)&&(m_val <= 12))"
#日本語定義 年月の正常範囲 "(1989 <= y_val)&&((1 <= m_val)&&(m_val <= 12))"
#日本語定義 平成元年である "(y_val == 1989)"
#日本語定義 平成である "((1989 < y_val)&&(y_val < 2019))"
#日本語定義 令和元年である "(y_val == 2019)"


仲間の集まり うるう年計算の仲間 {
仲間内で使用可:
        整数型 補正日数;
        整数型 月の最大日数[12];
全域で使用可:
        うるう年計算の仲間(型なし) {
                補正日数 = 0;
                月の最大日数[0] = 31; 月の最大日数[1] = 28; 月の最大日数[2] = 31;
                月の最大日数[3] = 30; 月の最大日数[4] = 31; 月の最大日数[5] = 30;
                月の最大日数[6] = 31; 月の最大日数[7] = 31; 月の最大日数[8] = 30;
                月の最大日数[9] = 31; 月の最大日数[10] = 30; 月の最大日数[11] = 31;
        };
        論理型 うるう年の判定(整数型) ;
        整数型 うるう年の補正( 整数型, 整数型 );
        論理型 年月日が正常範囲である( 整数型, 整数型, 整数型 );

};

論理型 うるう年計算の仲間::うるう年の判定(整数型 その年) {
        もし ( ( (その年 剰余計算 4)が割り切れる ) かつ
                ( (その年 剰余計算 100)が割り切れない) または
                ( (その年 剰余計算 400)が割り切れる ) )
        ならば { 戻る うるう年である; }
        それ以外 ならば 戻る うるう年ではない;
};

整数型 うるう年計算の仲間::うるう年の補正( 整数型 年のデータ, 整数型 月のデータ ) {

        もし ( ( うるう年の判定( 年のデータ ) がうるう年である ) かつ ( 月のデータ <= 2 ) )
        ならば 補正日数 = -1;

        反復 ( 整数型 その年 = 1989; その年 <= ( 年のデータ ); ++その年 ) {
                もし ( うるう年の判定( その年 ) がうるう年である ) ならば ++補正日数;
        }
        戻る(補正日数);
};

論理型 うるう年計算の仲間::年月日が正常範囲である( 整数型 年のデータ, 整数型 月のデータ, 整数型 日のデータ ) {
        もし ( 年が正常範囲 かつ 月が正常範囲 かつ ( 1 <= 日のデータ ) )
        ならば {
                もし ( ( 日のデータ <= 29 ) かつ ( 月のデータ == 2 ) かつ うるう年の判定( 年のデータ ) )
                ならば { 戻る 真; }

                それ以外 もし ( 日のデータ <= 月の最大日数[月のデータ-1] )
                ならば { 戻る 真; }
        }

        戻る 偽;
};

仲間の集まり 曜日の仲間 {
仲間内で使用可:
        文字列の仲間 七つの曜日[7];
全域で使用可:
        曜日の仲間(型なし) {
                七つの曜日[0] = "日"; 七つの曜日[1] = "月"; 七つの曜日[2] = "火";
                七つの曜日[3] = "水"; 七つの曜日[4] = "木"; 七つの曜日[5] = "金";
                七つの曜日[6] = "土";
        };
        文字列の仲間 曜日の表示データ(整数型 その日) {
                戻る 七つの曜日[その日];
        };
};

仲間の集まり 日数計算の仲間 {
仲間内で使用可:
        整数型 月の補正日数[12];
        整数型 合計補正日数;
全域で使用可:
        日数計算の仲間(型なし) {
                月の補正日数[0] = 0; 月の補正日数[1] = 3; 月の補正日数[2] = 3;
                月の補正日数[3] = 6; 月の補正日数[4] = 1; 月の補正日数[5] = 4;
                月の補正日数[6] = 6; 月の補正日数[7] = 2; 月の補正日数[8] = 5;
                月の補正日数[9] = 0; 月の補正日数[10] = 3; 月の補正日数[11] = 5;
        }
整数型 補正日数を求める( 整数型 その年, 整数型 その月, 整数型 その日 ) {
        うるう年計算の仲間 うるうの介;
        合計補正日数 = (その年-1) + 月の補正日数[その月-1]
                    + その日 - 1 + うるうの介.うるう年の補正(その年,その月);
        戻る 合計補正日数;
        }
};

整数型 はじまり(型なし)
{
        整数型 年のデータ = 1, 月のデータ = 1, 日のデータ = 1;

        表示する ← "(西暦)年、月(1-12)、日(1-31)を入力してください。" ← 改行;
        読み込む >> 年のデータ >> 月のデータ >> 日のデータ;

        うるう年計算の仲間 うるうの介;

        もし ( うるうの介.年月日が正常範囲である( 年のデータ, 月のデータ, 日のデータ ) )
        ならば {

                表示する ← "西暦" ← 年のデータ ← "年" ← 月のデータ ← "月" ← 日のデータ ← "日は";

                日数計算の仲間 日数太郎;
                整数型 合計補正日数 =
                日数太郎.補正日数を求める( 年のデータ, 月のデータ, 日のデータ );

                曜日の仲間 曜日の丸;
                表示する ← 曜日の丸.曜日の表示データ(合計補正日数 剰余計算 7 ) ← "曜日です。" ← 改行;
                もし ( 平成元年である ) ならば {
                        表示する ← "また" ← 年のデータ ← "年は" ← "昭和64年/平成元年です" ← 改行;

                }
                それ以外 もし ( 平成である ) ならば {
                        表示する ← "また" ← 年のデータ ← "年は" ← "平成" ← (年のデータ - 1988) ← "年です" ← 改行;

                }
                それ以外 もし ( 令和元年である ) ならば {
                        表示する ← "また" ← 年のデータ ← "年は" ← "平成31年/令和元年です" ← 改行 ;
                }
                それ以外 ならば {
                        表示する ← "また" ← 年のデータ ← "年は" ← "令和" ← (年のデータ - 2018) ← "年です" ← 改行;

                }

        }

        通常終了;

}

例2─5

入力のチェックにより、年、月、日が範囲外のものを入力しても計算は行われません。これは次のようなC++言語のコードに展開されます。

//平成1(1989)年の1月1日が日曜日であったことを利用して、指定した日の曜日を求める
#include <iostream>
#include <string>

using namespace std;

class うるう年計算の仲間 {
private:
        int 補正日数;
        int 月の最大日数[12];
public:
        うるう年計算の仲間(void) {
                補正日数 = 0;
                月の最大日数[0] = 31; 月の最大日数[1] = 28; 月の最大日数[2] = 31;
                月の最大日数[3] = 30; 月の最大日数[4] = 31; 月の最大日数[5] = 30;
                月の最大日数[6] = 31; 月の最大日数[7] = 31; 月の最大日数[8] = 30;
                月の最大日数[9] = 31; 月の最大日数[10] = 30; 月の最大日数[11] = 31;
        };
        bool うるう年の判定(int) ;
        int うるう年の補正( int, int );
        bool jp_str_0( int, int, int );

};

bool うるう年計算の仲間::うるう年の判定(int その年) {
        if ( ( (その年 % 4)== 0 ) &&
            ( (その年 % 100)!= 0) ||
            ( (その年 % 400)== 0 ) )
        { return true; }
        else return false;
};

int うるう年計算の仲間::うるう年の補正( int y_val, int m_val ) {

        if ( ( うるう年の判定( y_val ) == true ) && ( m_val <= 2 ) )
        補正日数 = -1;

        for ( int その年 = 1989; その年 <= ( y_val ); ++その年 ) {
                if ( うるう年の判定( その年 ) == true ) ++補正日数;
        }
        return(補正日数);
};

bool うるう年計算の仲間::jp_str_0( int y_val, int m_val, int d_val ) {
        if ( ((1989 <= y_val)&&(y_val <= 2099)) && ((1 <= m_val)&&(m_val <= 12)) && ( 1 <= d_val ) )
        {
                if ( ( d_val <= 29 ) && ( m_val == 2 ) && うるう年の判定( y_val ) )
                { return true; }

                else if ( d_val <= 月の最大日数[m_val-1] )
                { return true; }
        }

        return false;
};

class 曜日の仲間 {
private:
        string 七つの曜日[7];
public:
        曜日の仲間(void) {
                七つの曜日[0] = "日"; 七つの曜日[1] = "月"; 七つの曜日[2] = "火";
                七つの曜日[3] = "水"; 七つの曜日[4] = "木"; 七つの曜日[5] = "金";
                七つの曜日[6] = "土";
        };
        string 曜日の表示データ(int その日) {
                return 七つの曜日[その日];
        };
};

class 日数計算の仲間 {
private:
        int 月の補正日数[12];
        int 合計補正日数;
public:
        日数計算の仲間(void) {
                月の補正日数[0] = 0; 月の補正日数[1] = 3; 月の補正日数[2] = 3;
                月の補正日数[3] = 6; 月の補正日数[4] = 1; 月の補正日数[5] = 4;
                月の補正日数[6] = 6; 月の補正日数[7] = 2; 月の補正日数[8] = 5;
                月の補正日数[9] = 0; 月の補正日数[10] = 3; 月の補正日数[11] = 5;
}
int 補正日数を求める( int その年, int その月, int その日 ) {
        うるう年計算の仲間 うるうの介;
        合計補正日数 = (その年-1) + 月の補正日数[その月-1]
                 + その日 - 1 + うるうの介.うるう年の補正(その年,その月);
        return 合計補正日数;
        }
};

int main(void)
{
int y_val = 1, m_val = 1, d_val = 1;

        cout << "(西暦)年、月(1-12)、日(1-31)を入力してください。" << endl;
        cin >> y_val >> m_val >> d_val;

        うるう年計算の仲間 うるうの介;

        if ( うるうの介.jp_str_0( y_val, m_val, d_val ) )
        {

                cout << "西暦" << y_val << "年" << m_val << "月" << d_val << "日は";

                日数計算の仲間 日数太郎;
                int 合計補正日数 =
                日数太郎.補正日数を求める( y_val, m_val, d_val );

                曜日の仲間 曜日の丸;
                cout << 曜日の丸.曜日の表示データ(合計補正日数 % 7 ) << "曜日です。" << endl;
                if ( (y_val == 1989) ) {
                        cout << "また" << y_val << "年は" << "昭和64年/平成元年です" << endl;

                }
                else if ( ((1989 < y_val)&&(y_val < 2019)) ) {
                        cout << "また" << y_val << "年は" << "平成" << (y_val - 1988) << "年です" << endl;

                }
                else if ( (y_val == 2019) ) {
                        cout << "また" << y_val << "年は" << "平成31年/令和元年です" << endl ;
                }
                else {
                        cout << "また" << y_val << "年は" << "令和" << (y_val - 2018) << "年です" << endl;

                }


        }

        return 0;

}

例2-5を展開したもの

例2─5のプログラムで平成1年1月8日、平成12年(西暦2000年)2月29日、平成18年(西暦2006年)2月3日、さらに天皇陛下の御退位により、 平成最後の日となる平成31年(西暦2019年)4月30日、令和2年(西暦2020年)6月15日としたときの実行結果です。

今回は日本語ベースでJava言語用のプログラムを移植してみました。意外とスムーズにC++言語用に書き換えることができたのではないかと感じました。

例題2-6 長いπの計算

次の例はC言語編で使用した例題をC++言語版として移植したものです。これは任意の 桁数、πの値を計算するプログラムです。

今回のプログラムはC言語編と同様に二つに分かれており、一つははじまり(main)のメイン関数の部分です。もう一つのファイルは、マチンの逆正接演算、長い配列の加減除算などから成っております。それぞれ別々に翻訳(コンパイル)しておき、後から次のようなコマンドで結合します。

>bcc32 -e実行ファイル名 ファイル1.obj ファイル2.obj

この場合、C言語編と同様に以下のように入力します。また、実行結果を「pi.txt」というファイルに書き出しています。

この結果、ファイル1とファイル2が結合されて、ファイル1.exeという実行可能ファイルが生成されます。外部宣言を行う場合には、呼び出す側と呼び出される側で、同じ名前として定義する必要があります。(正確には関数の呼び出し規約 を合わせることも必要です)

/* マチンの公式を使用して、任意の桁数πの値を計算するプログラム */
入出力に関する前処理
文字列入出力に関する前処理

標準的な名前空間;

#日本語定義 配列の最大値 "10010"
#日本語定義 求める桁数 "10000"

#日本語定義 マチンの逆正接演算 "machins_arctan"
#日本語定義 長い配列の割り算 "lng_dim_div"
#日本語定義 長い配列の加算 "lng_dim_add"
#日本語定義 長い配列の減算 "lng_dim_sub"
#日本語定義 非ゼロの位置を見つける "nzero_fnd"
#日本語定義 初期化 "lng_dim_init"
#日本語定義 反復数

外部定義 型なし マチンの逆正接演算( 整数型[], 整数型, 整数型 );
外部定義 型なし 長い配列の減算( 整数型[], 整数型[], 整数型[] );
外部定義 型なし 長い配列の加算( 整数型[], 整数型[], 整数型[] );
外部定義 型なし 長い配列の割り算( 整数型[], 整数型[], 整数型, 整数型 );
外部定義 型なし 初期化( 整数型[], 整数型 );
外部定義 整数型 非ゼロの位置を見つける( 整数型[], 整数型 );

仲間の集まり 漢数字の仲間 {
仲間内で使用可:
        文字列の仲間 十の漢数字[10];
全域で使用可:
        漢数字の仲間(型なし) {
                十の漢数字[0] = "〇"; 十の漢数字[1] = "一"; 十の漢数字[2] = "二";
                十の漢数字[3] = "三"; 十の漢数字[4] = "四"; 十の漢数字[5] = "五";
                十の漢数字[6] = "六"; 十の漢数字[7] = "七"; 十の漢数字[8] = "八";
                十の漢数字[9] = "九";
        };
        文字列の仲間 漢数字の表示データ( 整数型 その数字 ) {
                戻る 十の漢数字[その数字];
        };
};

#日本語定義 剰余計算 "%"
#日本語定義 が割り切れる "== 0"

整数型 はじまり(型なし)
{
        整数型 作業用一[配列の最大値+1],
        作業用二[配列の最大値+1],
        円周率の結果[配列の最大値+1];
        整数型 反復数;

        マチンの逆正接演算( 作業用一, 16, 5 );
        マチンの逆正接演算( 作業用二, 4, 239 );

        長い配列の減算( 円周率の結果, 作業用一, 作業用二 );

        漢数字の仲間 漢太郎;

        表示する ← 漢太郎.漢数字の表示データ(円周率の結果[0]) ← ".\n";
        反復 ( 反復数 = 1; 反復数 <= 求める桁数; 反復数++ ) {

                表示する ← 漢太郎.漢数字の表示データ(円周率の結果[反復数]);
                もし ( ( 反復数 剰余計算 10 ) が割り切れる ) 表示する ← " " ;
                もし ( ( 反復数 剰余計算 50 ) が割り切れる ) 表示する ← "\n";
        }

通常終了;
}

例2─6

外部宣言の本体であるもう一つのファイルの方は、C言語編でも一応お断りしましたが、掲載は差し控えさせていただきます。また、逆正接演算というのはアークタンジェント演算の意味です。

また、仲間の集まり(クラス)のメンバーはデフォルトで仲間内で使用可(プライベート、private)となります。今回のプログラムでは、最初に この仲間内で使用可というアクセスを指定するように記述してありますが、特にこれはなくても構いません。分かりやすくするために一応書いています。例2─6のプログラムを展開すると以下のようになります。

/* マチンの公式を使用して、任意の桁数πの値を計算するプログラム */
#include <iostream>
#include <sstream>

using namespace std;

extern void machins_arctan( int[], int, int );
extern void lng_dim_sub( int[], int[], int[] );
extern void lng_dim_add( int[], int[], int[] );
extern void lng_dim_div( int[], int[], int, int );
extern void lng_dim_init( int[], int );
extern int nzero_fnd( int[], int );

class 漢数字の仲間 {
private:
        string 十の漢数字[10];
public:
        漢数字の仲間(void) {
                十の漢数字[0] = "〇"; 十の漢数字[1] = "一"; 十の漢数字[2] = "二";
                十の漢数字[3] = "三"; 十の漢数字[4] = "四"; 十の漢数字[5] = "五";
                十の漢数字[6] = "六"; 十の漢数字[7] = "七"; 十の漢数字[8] = "八";
                十の漢数字[9] = "九";
        };
        string 漢数字の表示データ( int その数字 ) {
                return 十の漢数字[その数字];
        };
};


int main(void)
{
        int 作業用一[10010+1],
        作業用二[10010+1],
        円周率の結果[10010+1];
        int jp_str_0;

        machins_arctan( 作業用一, 16, 5 );
        machins_arctan( 作業用二, 4, 239 );

        lng_dim_sub( 円周率の結果, 作業用一, 作業用二 );

        漢数字の仲間 漢太郎;

        cout << 漢太郎.漢数字の表示データ(円周率の結果[0]) << ".\n";
        for ( jp_str_0 = 1; jp_str_0 <= 10000; jp_str_0++ ) {

                cout << 漢太郎.漢数字の表示データ(円周率の結果[jp_str_0]);
                if ( ( jp_str_0 % 10 ) == 0 ) cout << " " ;
                if ( ( jp_str_0 % 50 ) == 0 ) cout << "\n";
        }

        return 0;
}

例2-6を展開したもの

C言語編では3,000桁の計算を行ってみました。今回もギネス記録( 2011年)の10兆桁にはやはり遠く及びませんが、「C言語によるアルゴリズム辞典」という本によると、1958年にIBM704により10,000桁を約100分で求めたという記録があるそうです。今回はその10,000桁で試してみましたが、数秒で答えが出ました。また、日本語を多めにというテーマですので、出力は漢数字で行ってみました。結果の数値についてはほかのサイトで公開している数値と比べてみたところ間違いはないようです。

今回、あまりにも結果が長くなり過ぎることと、表示のフォントの大きさの問題もあって、出力結果を「pi.txt」というファイルに書き出し、それをワープロで読み込ませて表示させることにします。また、出力結果については、最初の部分と最後の部分だけにしてあります。

GUIについて

(補則)これまでC++言語編での例題はすべてCUI(コマンド・ユーザー・インターフェース)ばかりでした。Java編ではGUI(グラフィック・ユーザー・インターフェース)の例題もありますので、このC++言語編を読まれた方の中にはどうもC++言語はGUIができないのではないかと思われる方があるといけないと思い、誤解のないように一応説明いたします。

これまでの例題のようにコマンドプロンプト上のCUIで実行するのとは違い、RAD StudioやC++Builderというものを使うと、VCL(ビジュアル・コンポーネント・ライブラリー)というものを使うことができます。さらにRAD StudioにはマルチデバイスでGUIを開発できるFireMonkeyという環境があります。これらの機能を使えば、Windows10上のマウスで選択するアプリケーションなどを楽に作ることができます。 以下はVCLを使って作った「簡単なワードプロセッサー」の例です。このようにBuilderのVCLとC++言語を併用することで、Java編と同様にさまざまなGUIアプリケーションを比較的短時間で作ることが可能になります。

今回は各言語で日本語を多めにということがテーマですので、このC++言語編ではやはりCUIを使った例題が主体になります。

例題2-7 集合計算

次は集合計算をやってみます。集合に関する演算は集合の和、積、差の演算を通常の算術演算の「+」、「*」、「-」という演算子を使ってできますが、 日本語カスタマイザーを使ってプログラム上では「∪」、「∩」、「─」で表現しています。 この集合計算を行うプログラムを書いてみます。

//集合計算を行う
#include <iostream>
#include <system.hpp>

標準的な名前空間;

#日本語定義 最小値 "1"
#日本語定義 最大値 "20"
#日本語定義 に要素が含まれるか ".Contains"
#日本語定義 ∪ "+"
#日本語定義 ∩ "*"
#日本語定義 ─ "-"

#日本語定義 白菜 "1"
#日本語定義 にんじん "2"
#日本語定義 じゃがいも "3"
#日本語定義 玉ねぎ "4"
#日本語定義 キャベツ "5"
#日本語定義 小松菜 "6"
#日本語定義 いちご "11"
#日本語定義 みかん "12"
#日本語定義 柿 "13"
#日本語定義 りんご "14"
#日本語定義 メロン "15"
#日本語定義 すいか "16"

型定義 集合型<整数型,最小値,最大値> 青果型;

型なし 集合要素の表示( 青果型 それ )
{
        表示する ← "{";
        表示する ← (それに要素が含まれるか(白菜)?"白菜 ":"");
        表示する ← (それに要素が含まれるか(にんじん)?"にんじん ":"");
        表示する ← (それに要素が含まれるか(じゃがいも)?"じゃがいも ":"");
        表示する ← (それに要素が含まれるか(玉ねぎ)?"玉ねぎ ":"");
        表示する ← (それに要素が含まれるか(キャベツ)?"キャベツ ":"");
        表示する ← (それに要素が含まれるか(小松菜)?"小松菜 ":"");
        表示する ← (それに要素が含まれるか(いちご)?"いちご ":"");
        表示する ← (それに要素が含まれるか(みかん)?"みかん ":"");
        表示する ← (それに要素が含まれるか(柿)?"柿 ":"");
        表示する ← (それに要素が含まれるか(りんご)?"りんご ":"");
        表示する ← (それに要素が含まれるか(メロン)?"メロン ":"");
        表示する ← (それに要素が含まれるか(すいか)?"すいか ":"");
        表示する ← "}";

}



整数型 はじまり()
{

        青果型 野菜の集合,根野菜の集合,葉野菜の集合,
        果物の集合,木になる果物の集合,地になる果物の集合,
        野菜と果物の積集合 ;

        葉野菜の集合 ← 白菜← キャベツ← 小松菜← いちご ;
        根野菜の集合 ← にんじん← じゃがいも ← 玉ねぎ ;
        表示する ← "葉野菜の集合は";
        集合要素の表示(葉野菜の集合) ;
        表示する ← "です。" ← 改行;

        表示する ← "根野菜の集合は";
        集合要素の表示(根野菜の集合) ;
        表示する ← "です。" ← 二行改行 ;

        野菜の集合 = 葉野菜の集合∪根野菜の集合 ;
        表示する ← "野菜全部の集合は、葉野菜と根野菜の和集合で、" ← 改行;
        集合要素の表示(野菜の集合) ;
        表示する ← "です。" << 二行改行 ;

        果物の集合 ← みかん ← 柿 ← りんご ← いちご ← メロン ← すいか ;
        表示する ← "果物全部の集合は";
        集合要素の表示(果物の集合) ;
        表示する ← "です。" ← 二行改行 ;

        野菜と果物の積集合 = 野菜の集合∩果物の集合;
        表示する ← "野菜と果物の積集合は";
        集合要素の表示(野菜と果物の積集合) ;
        表示する ← "です。" ← 二行改行;

        地になる果物の集合 ← いちご ← メロン ← すいか ;
        表示する ← "地になる果物の集合は";
        集合要素の表示(地になる果物の集合) ;
        表示する ← "です。" ← 改行;

        木になる果物の集合 = 果物の集合 ─ 地になる果物の集合;
        表示する ← "木になる果物の集合は果物全部の集合と地になる果物の集合の差集合で、" ← 改行;
        集合要素の表示(木になる果物の集合) ;
        表示する ← "です。" ← 改行;

        通常終了;
}

例2─7

例2─7のプログラムを展開すると以下のようになります。

//集合計算を行う
#include <iostream>
#include <system.hpp>

using namespace std;

typedef Set<int,1,20> 青果型;

void 集合要素の表示( 青果型 それ )
{
        cout << "{";
        cout << (それ.Contains(1)?"白菜 ":"");
        cout << (それ.Contains(2)?"にんじん ":"");
        cout << (それ.Contains(3)?"じゃがいも ":"");
        cout << (それ.Contains(4)?"玉ねぎ ":"");
        cout << (それ.Contains(5)?"キャベツ ":"");
        cout << (それ.Contains(6)?"小松菜 ":"");
        cout << (それ.Contains(11)?"いちご ":"");
        cout << (それ.Contains(12)?"みかん ":"");
        cout << (それ.Contains(13)?"柿 ":"");
        cout << (それ.Contains(14)?"りんご ":"");
        cout << (それ.Contains(15)?"メロン ":"");
        cout << (それ.Contains(16)?"すいか ":"");
        cout << "}";

}

int main()
{

        青果型 野菜の集合,根野菜の集合,葉野菜の集合,
        果物の集合,木になる果物の集合,地になる果物の集合,
        野菜と果物の積集合 ;

        葉野菜の集合 << 1<< 5<< 6<< 11 ;
        根野菜の集合 << 2<< 3 << 4 ;
        cout << "葉野菜の集合は";
        集合要素の表示(葉野菜の集合) ;
        cout << "です。" << endl;

        cout << "根野菜の集合は";
        集合要素の表示(根野菜の集合) ;
        cout << "です。" << endl << endl ;

        野菜の集合 = 葉野菜の集合+根野菜の集合 ;
        cout << "野菜全部の集合は、葉野菜と根野菜の和集合で、" << endl;
        集合要素の表示(野菜の集合) ;
        cout << "です。" << endl << endl ;

        果物の集合 << 12 << 13 << 14 << 11 << 15 << 16 ;
        cout << "果物全部の集合は";
        集合要素の表示(果物の集合) ;
        cout << "です。" << endl << endl ;

        野菜と果物の積集合 = 野菜の集合*果物の集合;
        cout << "野菜と果物の積集合は";
        集合要素の表示(野菜と果物の積集合) ;
        cout << "です。" << endl << endl;

        地になる果物の集合 << 11 << 15 << 16 ;
        cout << "地になる果物の集合は";
        集合要素の表示(地になる果物の集合) ;
        cout << "です。" << endl;

        木になる果物の集合 = 果物の集合 - 地になる果物の集合;
        cout << "木になる果物の集合は果物全部の集合と地になる果物の集合の差集合で、" << endl;
        集合要素の表示(木になる果物の集合) ;
        cout << "です。" << endl;

        return 0;
}

例2-7を展開したもの

例2─7を実行させた結果も以下に示します。

デバッグについて

今回のC++処理系(コンパイラ)のバージョンでは変数名、関数名、クラス名などにそのまま日本語を使うことができました。 デバッグも機械語(アセンブラ)オプションを付けて、機械語ファイルを見てみると識別子(シンボル)にはちゃんと日本語が残っていましたので、デバッグには困らないのではないかと思います。

これ以降、プログラム言語の開発環境で日本語の利用が拡大していけば幸いですが、かつて10年以上前にWindowsで利用できるclispのバージョン2.XXでは関数名などに直接日本語が利用できていたにもかかわらず、 その後バージョン3.XXからは全く使えないという状況が続きました。こういうこともあるので、やはりいざというときのために日本語カスタマイザーの自動変数定義はあってしかるべきだと考えています。

C言語編でも述べたように、日本語カスタマイザーの自動変数定義を利用してC++言語を翻訳(コンパイル)するときに、プログラム中に文法的な問題があると警告メッセージやエラーメッセージが表示されます。このときにjp_str_*(*は数字)についてのエラー情報が出た場合、このjp_str_*とソースコード中の日本語文字列がどのように対応するのかが分からないとデバッグしにくいことが挙げられます。もっともエラーが出た行番号の情報は出力されますので、ソースコード上でどの行に問題があるのかは分かります。

このようなデバッグ状況に対処する方法としては、日本語カスタマイザーが出力する2パス目の規則を記述してあるjcpp_rule2.lexファイルが残っていますので、この中の規則記述部分を見ることによって、日本語文字列とそれに対応するjp_str_*がどれかということが分かります。多少手数はかかりますが、デバッグについて全く対処できないというわけではないと思います。

C言語同様に、C++言語でも「mapcpp」というFLEXを用いた簡単なデバッグ用のツールを作ってみました。中味はjcpp_rule2.lexの規則記述部分だけを取り出して、表示するというものです。ここでは例題2-1において、変数であるjp_str_*がソースリスト中のどの日本語文字列に対応するか、という情報だけを取り出したい場合を考えます。日本語カスタマイザーが出力したjcpp_rule2.lexをmapcppで読み込ませた後、UNIXツールとして有名なgrepのパターン検索フィルタ機能を利用してjp_str_*だけを抽出した結果を以下に示します。(Windowsのコマンドプロンプトで動作するフリーウェアのgrepは多数あると思いますが、ここではC++言語の処理系に同梱されていたgrepを使用しております)このような簡単なツールを作ることで、日本語と実際に使われている予約語や変数を対応させることができるのではないかと思います。

 

参考資料
Bjarne Stroustrup's homepage B.ストラウストラップ博士が公開しているホームページ
C++Builder3/C++Builder5 リファレンスマニュアル一式 旧インプライズ/ボーランド社
Visual C++ V1.0 リファレンスマニュアル一式 マイクロソフト社
Delphi4 リファレンスマニュアル一式 旧インプライズ/ボーランド社
C言語による最新アルゴリズム辞典 奥村晴彦著 技術評論社 1991
C++Builder3パワフルテクニック大全集 John Miano/Tom Cabanski/Harold Howe著 大野元久監訳 インプレス 1998