(windows 7版)
C++言語はB.ストラウストラップ(Bjarne Stroustrup)博士によって1980年代初頭に設計されたプログラム言語として有名です。C言語文法との上位交換性があり、抱負な機能が盛り込まれていることでも知られています。ここでは、そのすべてについて日本語を使ったC++言語の適用例を挙げることはとうていできません。従ってほんの一部ではありますが、今回のプログラミング言語で日本語多めにというテーマで、いくつかの実例を紹介してみたいと思います。
もちろんプログラム言語について何も知らない方が、いきなりC++言語を習得しようとしても大変難しいのではないかと思います。まずはC言語の流儀に、ある程度慣れてからC++言語に進むのがよいのではないでしょうか。今回もボーランド社 (2008年よりエンバカデロ・テクノロジーズ社に買収)の処理系であるBCC32.EXE(C++バージョン5.5.1、リンカーバージョン5.00)を使用して、日本語多めの検証をしてみました。この処理系は入力ファイルの拡張子が「.c」であれば、C言語として処理しますが、「.cpp」であれば自動的にC++言語として処理をしてくれます。
また、C言語編と同じように、日本語で書かれたC++言語ファイルの名前を仮にファイル1.jcppとすると
>jcpp ファイル1
というバッチコマンドによりファイル1.cppを生成し、さらに翻訳(コンパイル)、結合(リンク)して、実行可能形式ファイルであるファイル1.exeを生成します。jcppという日本語カスタマイザー実行プログラムはC言語の場合とほとんど同じ仕様です。
(注)以前は、Windows XP SP3上のコマンドプロンプトで動作することを前提にしていましたが、メーカーサポート期限の2014年4月9日以降は、Windows 7を使用した方が良いと思われます。サポート終了後も、XPパソコンを全く利用できないということはないと考えられますが、ネットに接続したときのトラブルに対処できない恐れがありますので、もしXPを使い続けるのであれば、オフラインでの使用が無難ではないでしょうか。また、Windows 7でのXPモードのサポート期限も、同様に2014年4月9日となっているようです。
ここで、日本語定義の領域がかなり大きくなってきましたので、C++言語編からは自分でよく使う予約語や演算子などの日本語定義部分をプログラムとは別に、外部である程度まとめて宣言できるようにしておきます。そして処理するときにバッチコマンドの機能を利用し、宣言ファイルと日本語C++ファイルを結合します。このように日本語定義部分を通常のC++言語の文法に合うように置き換えるという処理をします。
以下は、別に定義した日本語C++宣言ファイルの内容です。
#日本語定義 整数型 "int"
#日本語定義 文字型 "char"
#日本語定義 実数型 "float"
#日本語定義 定数指定 "const"
#日本語定義 論理型 "bool"
#日本語定義 外部定義 "extern"
#日本語定義 型なし "void"
#日本語定義 構造体 "struct"
#日本語定義 共用体 "union"
#日本語定義 仲間の集まり "class"
#日本語定義 全域で使用可 "public"
#日本語定義 仲間内で使用可 "private"
#日本語定義 文字列の仲間 "string"
#日本語定義 直接出入可 "friend"
#日本語定義 もし "if"
#日本語定義 それ以外 "else"
#日本語定義 戻る "return"
#日本語定義 繰り返し "while"
#日本語定義 反復 "for"
#日本語定義 読み込む "cin"
#日本語定義 印字する "cout" //例2─1はこちらを使う
#日本語定義 表示する "cout" //例2─2以降はこちらを使う
#日本語定義 改行 "endl"
#日本語定義 内部埋め込み "inline"
#日本語定義 ← "<<"
#日本語定義 → ">>"
#日本語定義 番地| "*"
#日本語定義 参照| "&"
#日本語定義 中味| "*"
#日本語定義 かつ "&&"
#日本語定義 または "||"
#日本語定義 真 "true"
#日本語定義 偽 "false"
#日本語定義 はじまり "main"
#日本語定義 入力反復子 "insert_iterator"
#日本語定義 出力反復子 "ostream_iterator"
#日本語定義 文字の特性 "char_traits"
#日本語定義 リストひな型 "list<int>"
#日本語定義 集合ひな型 "set<int, less<int> >"
#日本語定義 最初 "begin()"
#日本語定義 最後 "end()"
#日本語定義 複写 "copy"
#日本語定義 通常終了 "return 0"
これ以降の例題では、プログラム内に上記の日本語定義の部分はありません。
はじめに、例2-1として西暦年を入力し、その年がうるう年かどうかを判定するプログラムを示します。
// 西暦を入力し、その年がうるう年かどうかを判定する
#include <stdlib.h>
#include <iostream.h>
#日本語定義 その年
#日本語定義 年のデータ
#日本語定義 判定
#日本語定義 うるう年の判定
#日本語定義 うるう年である "1"
#日本語定義 うるう年ではない "0"
#日本語定義 がうるう年である "== 1"
#日本語定義 剰余計算 "%"
#日本語定義 が割り切れる "== 0"
#日本語定義 ならば ""
#日本語定義 どちらか大きい方 "max"
整数型 うるう年の判定( 整数型 );
型なし はじまり( 型なし )
{
整数型 年のデータ その年 = 1 ;
印字する << "年(西暦)を入力してください。(1以上の数字)" <<
改行;
読み込む >> 年のデータ;
年のデータ = どちらか大きい方( 年のデータ, 1 ); //
必ず入力値を1以上とする。
印字する << "西暦" << 年のデータ << "年は";
印字する << ( うるう年の判定(年のデータ) ?
"うるう年である。":"うるう年でない。" ) << 改行 ;
}
内部埋め込み 整数型 うるう年の判定( 整数型 その年 )
{
もし
( ( その年 剰余計算 400 ) が割り切れる ) ならば 戻る( うるう年である );
それ以外 もし ( ( その年 剰余計算 100 ) が割り切れる )
ならば 戻る( うるう年ではない );
それ以外 もし ( ( その年 剰余計算 4 )
が割り切れる ) ならば 戻る( うるう年である );
それ以外 ならば 戻る( うるう年ではない );
}
例2-1
西暦2000年問題という、うるう年の判定について、以前随分と話題になったことを覚えていらっしゃる方も多いと思いますが、うるう年かどうかの判断もややこしく、判断の順番を間違えないようにする必要があるようです。
また、C++言語になって、今までの"/*...*/"に加えて、コメントに"//"が使用できますので、FLEXの日本語解析プログラムもC++言語のコメント対応に変更しました。さらに変数の宣言は、変数を使う場所で宣言することができるのもC++言語の特徴ではないでしょうか。これは次のようなC++言語のコードに展開されます。
//
西暦を入力し、その年がうるう年かどうかを判定する
#include <stdlib.h>
#include <iostream.h>
int jp_str_3( int jp_str_0 );
void main( void )
{
int jp_str_1 = 1 ;
cout << "年(西暦)を入力してください。(1以上の数字)" <<
endl;
cin >> jp_str_1;
jp_str_1 = max( jp_str_1, 1 ); //
必ず入力値を1以上とする。
cout << "西暦" << jp_str_1 << "年は";
cout << ( jp_str_3(jp_str_1) ?
"うるう年である。":"うるう年でない。" ) << endl ;
}
inline int jp_str_3( int jp_str_0 )
{
if ( (
jp_str_0 % 400 ) == 0 ) return( 1 );
else if ( ( jp_str_0 % 100 ) == 0 )
return( 0 );
else if ( ( jp_str_0 % 4
) == 0 ) return( 1 );
else return( 0 );
}
例2-1を展開したもの
例2-1のプログラムを実行させ、西暦を2000年としたときの結果ですが、最小値は1(西暦1年)にしております。
次の例は、C++言語の目玉ともいうべき「クラス」を使ってみたものです。ここでは「クラス」を「仲間の集まり」という日本語で置き換えてみました。C言語の構造体が変数の集まりとして定義されていたのに対して、C++言語のクラスは変数だけではなく、関数も定義できるようにしてあるのが特徴といえるのではないでしょうか。
この例2─2では、簡単な計算を行う関数を「基本計算の仲間」として定義してあります。この関数はクラスのメンバー関数と呼ばれていますが、これを内部埋め込み(インライン)関数としました。
// 簡単な計算をする仲間の集まり(クラス)を作り、計算してみる
#include <iostream.h>
#日本語定義 基本計算の仲間
#日本語定義 足す
#日本語定義 引く
#日本語定義 掛ける
#日本語定義 割る
#日本語定義 余り
#日本語定義 論理積
#日本語定義 論理和
仲間の集まり 基本計算の仲間
{
全域で使用可:
基本計算の仲間(型なし) { 表示する ←
"基本計算の対象(オブジェクト)を組み立てる" ← 改行; };
実数型 足す ( 実数型, 実数型 );
実数型 引く ( 実数型, 実数型 );
実数型 掛ける( 実数型, 実数型 );
実数型 割る ( 実数型, 実数型 );
整数型 余り ( 整数型, 整数型 );
論理型 論理積( 論理型, 論理型 );
論理型 論理和( 論理型, 論理型 );
~基本計算の仲間(型なし) { 表示する ←
"基本計算の対象(オブジェクト)を消滅させる" ← 改行; };
};
#日本語定義 引数11
#日本語定義 引数12
内部埋め込み 実数型 基本計算の仲間::足す ( 実数型 引数11, 実数型 引数12 ) { 戻る( 引数11 + 引数12 ); };
内部埋め込み 実数型 基本計算の仲間::引く ( 実数型 引数11, 実数型 引数12 ) { 戻る( 引数11 - 引数12 ); };
内部埋め込み 実数型 基本計算の仲間::掛ける( 実数型 引数11, 実数型 引数12 ) { 戻る( 引数11 * 引数12 ); };
内部埋め込み 実数型 基本計算の仲間::割る ( 実数型 引数11, 実数型 引数12 ) { 戻る( 引数11 / 引数12 ); };
内部埋め込み 整数型 基本計算の仲間::余り ( 整数型 引数11, 整数型 引数12 ) { 戻る( 引数11 % 引数12 ); };
内部埋め込み 論理型 基本計算の仲間::論理積( 論理型 引数11, 論理型 引数12 ) { 戻る( 引数11 && 引数12 ); };
内部埋め込み 論理型 基本計算の仲間::論理和( 論理型 引数11, 論理型 引数12 ) { 戻る( 引数11 || 引数12 ); };
#日本語定義 計算くん
型なし はじまり(型なし)
{
基本計算の仲間 計算くん;
表示する ← 改行;
表示する ← "567 足す 2010 は " ← 計算くん.足す (
567, 2010 ) ← 改行;
表示する ← "80.76 引く 20 は " ← 計算くん.引く (
80.76, 20 ) ← 改行;
表示する ← "18 掛ける 369 は " ← 計算くん.掛ける(
18, 369 ) ← 改行;
表示する ← "75.3 割る 19 は " ← 計算くん.割る (
75.3, 19 ) ← 改行;
表示する ← "193を4で割った余りは " ← 計算くん.余り (
193, 4 ) ← 改行;
表示する ← "真と真の論理積は " ← ( 計算くん.論理積( 真, 真
) ? "真" : "偽" ) ← 改行;
表示する ← "真と偽の論理積は " ← ( 計算くん.論理積( 真, 偽
) ? "真" : "偽" ) ← 改行;
表示する ← "偽と真の論理積は " ← ( 計算くん.論理積( 偽, 真
) ? "真" : "偽" ) ← 改行;
表示する ← "偽と偽の論理積は " ← ( 計算くん.論理積( 偽, 偽
) ? "真" : "偽" ) ← 改行;
表示する ← "真と真の論理和は " ← ( 計算くん.論理和( 真, 真
) ? "真" : "偽" ) ← 改行;
表示する ← "真と偽の論理和は " ← ( 計算くん.論理和( 真, 偽
) ? "真" : "偽" ) ← 改行;
表示する ← "偽と真の論理和は " ← ( 計算くん.論理和( 偽, 真
) ? "真" : "偽" ) ← 改行;
表示する ← "偽と偽の論理和は " ← ( 計算くん.論理和( 偽, 偽
) ? "真" : "偽" ) ← 改行;
表示する ← 改行;
}
例2-2
さらに、C++言語では入出力ストリーム(iostreamクラスライブラリ)で使われる">>"と"<<"記号がオーバーロード演算子として使われていますが、これらはビットシフト演算子と兼用してあるかのように見えます。しかし、同じ記号を別の意味で使用すると混乱を招きやすいということもあり、これを「→」と「←」の2バイト記号で日本語定義として置き換えてみました。例2─2は次のようなC++言語のコードに展開されます。
// 簡単な計算をする仲間の集まり(クラス)を作り、計算してみる
#include <iostream.h>
class jp_str_0
{
public:
jp_str_0(void) { cout << "基本計算の対象(オブジェクト)を組み立てる" << endl; };
float jp_str_1 ( float, float );
float jp_str_2 ( float, float );
float jp_str_3( float, float );
float jp_str_4 ( float, float );
int jp_str_5 ( int, int );
bool jp_str_6( bool, bool );
bool jp_str_7( bool, bool );
~jp_str_0(void) { cout << "基本計算の対象(オブジェクト)を消滅させる" << endl; };
};
inline float jp_str_0::jp_str_1 ( float jp_str_8, float jp_str_9 ) { return(
jp_str_8 + jp_str_9 ); };
inline float jp_str_0::jp_str_2 ( float jp_str_8, float jp_str_9 ) { return(
jp_str_8 - jp_str_9 ); };
inline float jp_str_0::jp_str_3( float jp_str_8, float jp_str_9 ) { return(
jp_str_8 * jp_str_9 ); };
inline float jp_str_0::jp_str_4 ( float jp_str_8, float jp_str_9 ) { return(
jp_str_8 / jp_str_9 ); };
inline int jp_str_0::jp_str_5 ( int jp_str_8, int jp_str_9 ) { return( jp_str_8
% jp_str_9 ); };
inline bool jp_str_0::jp_str_6( bool jp_str_8, bool jp_str_9 ) { return(
jp_str_8 && jp_str_9 ); };
inline bool jp_str_0::jp_str_7( bool jp_str_8, bool jp_str_9 ) { return(
jp_str_8 || jp_str_9 ); };
void main(void)
{
jp_str_0 jp_str_10;
cout << endl;
cout << "567 足す 2010 は " << jp_str_10.jp_str_1 ( 567, 2010 ) << endl;
cout << "80.76 引く 20 は " << jp_str_10.jp_str_2 ( 80.76, 20 ) << endl;
cout << "18 掛ける 369 は " << jp_str_10.jp_str_3( 18, 369 ) << endl;
cout << "75.3 割る 19 は " << jp_str_10.jp_str_4 ( 75.3, 19 ) << endl;
cout << "193を4で割った余りは " << jp_str_10.jp_str_5 ( 193, 4 ) << endl;
cout << "真と真の論理積は " << ( jp_str_10.jp_str_6( true, true ) ? "真" : "偽" ) << endl;
cout << "真と偽の論理積は " << ( jp_str_10.jp_str_6( true, false ) ? "真" : "偽" ) << endl;
cout << "偽と真の論理積は " << ( jp_str_10.jp_str_6( false, true ) ? "真" : "偽" ) << endl;
cout << "偽と偽の論理積は " << ( jp_str_10.jp_str_6( false, false ) ? "真" : "偽" ) <<
endl;
cout << "真と真の論理和は " << ( jp_str_10.jp_str_7( true, true ) ? "真" : "偽" ) << endl;
cout << "真と偽の論理和は " << ( jp_str_10.jp_str_7( true, false ) ? "真" : "偽" ) << endl;
cout << "偽と真の論理和は " << ( jp_str_10.jp_str_7( false, true ) ? "真" : "偽" ) << endl;
cout << "偽と偽の論理和は " << ( jp_str_10.jp_str_7( false, false ) ? "真" : "偽" ) <<
endl;
cout << endl;
}
例2-2を展開したもの
例2─2を実行した結果を以下に示します。「計算くん」という「基本計算の仲間」クラスの対象(オブジェクト)が生成されると、最初にコンストラクタと呼ばれる部分が起動します。あとは対象(オブジェクト)のメンバー関数を呼ぶだけで、簡単な計算を行うことができます。最後にデストラクタと呼ばれる部分が起動し、対象(オブジェクト)が消滅します。
C言語編でも少し述べましたが、C++言語では参照型という新しい型が定義されています。これには「&」というアドレスを表す記号が使われております。次の例2─3では、その参照型で宣言した変数がどのような振る舞いをするのかを見てみます。また、ポインタを表す「*」記号について、やはりC言語編と同じように「番地|」、「中味|」という日本語定義で表記したときに、どのように記述されるのかも併せて見ていきます。
// 参照型とポインタ、配列についての例
#include <iostream.h>
#日本語定義 実際の数
#日本語定義 その分身
#日本語定義 もう一つの数
#日本語定義 他の数
整数型 はじまり(型なし)
{
整数型 実際の数;
整数型 参照|その分身 = 実際の数; // 宣言文で参照型を定義します
実際の数 = 324; //
実際の数に定数を代入すると、その分身にも代入されます。
表示する ← "参照型の例" ← 改行;
表示する ← 実際の数 ← " " ← その分身 ← 改行;
--その分身; // その分身を減少させると、実際の数も減ります。
表示する ← 実際の数 ← " " ← その分身 ← 改行;
++実際の数; // 実際の数を増加させると、その分身も増えます。
表示する ← 実際の数 ← " " ← その分身 ← 改行;
整数型 番地|もう一つの数; //
もう一つの数は整数型の番地であると定義します。
表示する ← "ポインタの例" ← 改行;
中味|もう一つの数 = その分身; //
もう一つの数が示す番地の中味に、数を代入します。
表示する ← 中味|もう一つの数 ← 改行;
整数型 他の数[2] = {18,36}; //
他の数は整数型の初期値を持った配列を表します。
表示する ← "配列の例" ← 改行;
もう一つの数 = 参照|他の数[0]; //
配列の要素に対応する番地をもう一つの数に代入します。
表示する ← 中味|もう一つの数 ← " ";
もう一つの数 = 参照|他の数[1];
表示する ← 中味|もう一つの数 ← 改行;
通常終了;
}
例2-3
このように実際の数(通常の変数)と、その分身(参照型変数)のどちらを変えても同じように操作されることが分かります。これは次のようなC++言語のコードに展開されます。
// 参照型とポインタ、配列についての例
#include <iostream.h>
int main(void)
{
int jp_str_0;
int &jp_str_1 = jp_str_0; //
宣言文で参照型を定義します
jp_str_0 = 324; //
実際の数に定数を代入すると、その分身にも代入されます。
cout << "参照型の例" << endl;
cout << jp_str_0 << " " << jp_str_1
<< endl;
--jp_str_1; // その分身を減少させると、実際の数も減ります。
cout << jp_str_0 << " " << jp_str_1
<< endl;
++jp_str_0; // 実際の数を増加させると、その分身も増えます。
cout << jp_str_0 << " " << jp_str_1
<< endl;
int *jp_str_2; //
もう一つの数は整数型の番地であると定義します。
cout << "ポインタの例" << endl;
*jp_str_2 = jp_str_1; //
もう一つの数が示す番地の中味に、数を代入します。
cout << *jp_str_2 << endl;
int jp_str_3[2] = {18,36}; //
他の数は整数型の初期値を持った配列を表します。
cout << "配列の例" << endl;
jp_str_2 = &jp_str_3[0]; //
配列の要素に対応する番地をもう一つの数に代入します。
cout << *jp_str_2 << " ";
jp_str_2 = &jp_str_3[1];
cout << *jp_str_2 << endl;
return 0;
}
例2-3を展開したもの
例2-3のプログラムを実行させた結果は以下のとおりです。
今回はC言語編と同じように「&」、「*」を日本語定義で「参照|」、「番地|」、「中味|」と置き換えて使ってみましたが、C++言語でも特に問題なく使えるのではないかと思っております。読者の皆さんはいかがでしょうか。
話は変わりますが、C++言語でのmain関数が特に値を返さない場合には、voidと書いておけば処理系から文句が出ることはありません。しかし、一応どのように書いた方が、読んだ方に納得してもらえるかのだろうかと思っておりました。これは今までの例題とは直接関係はありませんが、現在使用しておりますボーランド社のC++ビルダーでは、main関数の最後にreturn 0と自動的にコードが記述されるようですので、今回はmain関数をint型として宣言しておき、最後にreturn 0で終了しております。
次は例2─2で簡単な四則演算と論理演算程度の計算を行う仲間の集まり(クラス)を定義しましたが、これを少し発展させてみたいと思います。その機能拡張として、集合演算の機能を加えてみます。
C++言語ではPascal言語のように言語の標準仕様として集合演算の機能はないようですが、その代り豊富なクラスライブラリーの中に、それと同等の機能を備えた仲間の集まり(クラス)のメンバー関数が用意されているようです。また、以前ボーランド社のDelphi4で供給されているPascal処理系で試したところ、このPascal処理系の集合型では上限値が255という制限がありました。C++言語の集合演算は、標準クラスライブラリーを利用すれば特に上限値は制限なく使用できるようです。
新しくパワーアップした計算の仲間は、集合計算を基本計算から継承させる仲間の集まり(クラス)として定義しております。また今回は漢数字を多めに使用した場合に、日本語プログラム上ではどのように見えるかということも併せて試行しております。
// 集合計算を基本計算から継承させる仲間の集まり(クラス)を作る
#include <iostream.h>
#include <set>
#include <list>
#日本語定義 基本計算の仲間
#日本語定義 集合計算の仲間
#日本語定義 足す
#日本語定義 引く
#日本語定義 掛ける
#日本語定義 割る
#日本語定義 余り
#日本語定義 論理積
#日本語定義 論理和
#日本語定義 和集合を求める
#日本語定義 共通集合を求める
#日本語定義 部分集合かどうか
仲間の集まり 基本計算の仲間
{
全域で使用可:
基本計算の仲間(型なし) { 表示する ← "基本計算の対象を組み立てる"
← 改行; };
実数型 足す ( 実数型, 実数型 );
実数型 引く ( 実数型, 実数型 );
実数型 掛ける( 実数型, 実数型 );
実数型 割る ( 実数型, 実数型 );
整数型 余り ( 整数型, 整数型 );
論理型 論理積( 論理型, 論理型 );
論理型 論理和( 論理型, 論理型 );
~基本計算の仲間(型なし) { 表示する ←
"基本計算の対象を消滅させる" ← 改行; };
};
仲間の集まり 集合計算の仲間 : 全域で使用可 基本計算の仲間
{
全域で使用可:
集合計算の仲間(型なし) { 表示する ← "集合計算の対象を組み立てる"
← 改行; };
型なし 和集合を求める ( 整数型 番地|, 整数型, 整数型 番地|,
整数型 );
型なし 共通集合を求める( 整数型 番地|, 整数型, 整数型 番地|,
整数型 );
型なし 部分集合かどうか( 整数型 番地|, 整数型, 整数型 番地|,
整数型 );
~集合計算の仲間(型なし) { 表示する ←
"集合計算の対象を消滅させる" ← 改行; };
};
#日本語定義 引数一
#日本語定義 引数二
#日本語定義 引数三
#日本語定義 引数四
内部埋め込み 実数型 基本計算の仲間::足す ( 実数型 引数一, 実数型 引数二 ) { 戻る( 引数一 + 引数二 ); };
内部埋め込み 実数型 基本計算の仲間::引く ( 実数型 引数一, 実数型 引数二 ) { 戻る( 引数一 - 引数二 ); };
内部埋め込み 実数型 基本計算の仲間::掛ける( 実数型 引数一, 実数型 引数二 ) { 戻る( 引数一 * 引数二 ); };
内部埋め込み 実数型 基本計算の仲間::割る ( 実数型 引数一, 実数型 引数二 ) { 戻る( 引数一 / 引数二 ); };
内部埋め込み 整数型 基本計算の仲間::余り ( 整数型 引数一, 整数型 引数二 ) { 戻る( 引数一 % 引数二 ); };
内部埋め込み 論理型 基本計算の仲間::論理積( 論理型 引数一, 論理型 引数二 ) { 戻る( 引数一 && 引数二 ); };
内部埋め込み 論理型 基本計算の仲間::論理和( 論理型 引数一, 論理型 引数二 ) { 戻る( 引数一 || 引数二 ); };
#日本語定義 和集合の関数 "set_union"
#日本語定義 共通集合の関数 "set_intersection"
#日本語定義 部分集合の関数 "includes"
#日本語定義 書式付出力
#日本語定義 集合一
#日本語定義 集合二
#日本語定義 集合三
#日本語定義 集合四
#日本語定義 集合五
#日本語定義 集合六
出力反復子< 整数型, 文字型, 文字の特性< 文字型 > > 書式付出力(表示する, " ");
型なし 集合計算の仲間::和集合を求める( 整数型 番地|引数一, 整数型 引数二, 整数型 番地|引数三, 整数型 引数四 )
{
リストひな型 集合一(引数一, 引数一 + 引数二);
リストひな型 集合二(引数三, 引数三 + 引数四);
和集合の関数 ( 集合一.最初, 集合一.最後, 集合二.最初,
集合二.最後, 書式付出力 );
};
型なし 集合計算の仲間::共通集合を求める( 整数型 番地|引数一, 整数型 引数二, 整数型 番地|引数三, 整数型 引数四 )
{
リストひな型 集合一(引数一, 引数一 + 引数二);
リストひな型 集合二(引数三, 引数三 + 引数四);
共通集合の関数 ( 集合一.最初, 集合一.最後, 集合二.最初,
集合二.最後, 書式付出力 );
};
型なし 集合計算の仲間::部分集合かどうか( 整数型 番地|引数一, 整数型 引数二, 整数型 番地|引数三, 整数型 引数四 )
{
リストひな型 集合一(引数一, 引数一 + 引数二);
リストひな型 集合二(引数三, 引数三 + 引数四);
もし ( 部分集合の関数( 集合一.最初, 集合一.最後, 集合二.最初,
集合二.最後 ) )
表示する ← "部分集合である。";
それ以外 表示する ← "部分集合ではない。";
};
#日本語定義 計算くん
#日本語定義 要素数一 "5"
#日本語定義 要素数二 "5"
#日本語定義 要素数三 "5"
#日本語定義 要素数四 "6"
#日本語定義 要素数五 "5"
#日本語定義 要素数六 "3"
整数型 はじまり(型なし)
{
集合計算の仲間 計算くん;
表示する ← 改行;
表示する ← "567 足す 2010 は " ← 計算くん.足す(
567, 2010 ) ← 改行;
表示する ← "80.76 引く 20 は " ← 計算くん.引く(
80.76, 20 ) ← 改行;
表示する ← "18 掛ける 369 は " ← 計算くん.掛ける(
18, 369 ) ← 改行;
表示する ← "75.3 割る 19 は " ←
計算くん.割る( 75.3, 19 ) ← 改行;
表示する ← "193を4で割った余りは " ← 計算くん.余り(
193, 4 ) ← 改行;
表示する ← "真と真の論理積は " ← ( 計算くん.論理積( 真, 真
) ? "真" : "偽" ) ← 改行;
表示する ← "真と偽の論理積は " ← ( 計算くん.論理積( 真, 偽
) ? "真" : "偽" ) ← 改行;
表示する ← "偽と真の論理積は " ← ( 計算くん.論理積( 偽, 真
) ? "真" : "偽" ) ← 改行;
表示する ← "偽と偽の論理積は " ← ( 計算くん.論理積( 偽, 偽
) ? "真" : "偽" ) ← 改行;
表示する ← "真と真の論理和は " ← ( 計算くん.論理和( 真, 真
) ? "真" : "偽" ) ← 改行;
表示する ← "真と偽の論理和は " ← ( 計算くん.論理和( 真, 偽
) ? "真" : "偽" ) ← 改行;
表示する ← "偽と真の論理和は " ← ( 計算くん.論理和( 偽, 真
) ? "真" : "偽" ) ← 改行;
表示する ← "偽と偽の論理和は " ← ( 計算くん.論理和( 偽, 偽
) ? "真" : "偽" ) ← 改行;
整数型 集合一[5] = {100,300,500,700,900};
整数型 集合二[5] = {200,400,600,800,1000};
表示する ← "{ "; 複写( 集合一, 集合一 + 要素数一,
書式付出力 );
表示する ← "}と{ "; 複写( 集合二, 集合二 + 要素数二,
書式付出力 );
表示する ← "}" ← 改行;
表示する ← "の和集合は { ";
計算くん.和集合を求める( 集合一, 要素数一, 集合二, 要素数二 );
表示する ← "}" ← 改行;
整数型 集合三[5] = {200,400,600,800,1000};
整数型 集合四[6] =
{100,200,500,700,900,1000};
表示する ← "{ "; 複写( 集合三, 集合三 + 要素数三,
書式付出力 );
表示する ← "}と{ "; 複写( 集合四, 集合四 + 要素数四,
書式付出力 );
表示する ← "}" ← 改行;
表示する ← "の共通集合は { ";
計算くん.共通集合を求める( 集合三, 要素数三, 集合四, 要素数四
);
表示する ← "}" ← 改行;
整数型 集合五[5] = {200,400,600,800,1000};
整数型 集合六[3] = {200,400,600};
表示する ← "{ "; 複写( 集合五, 集合五 + 要素数五,
書式付出力 );
表示する ← "}と{ "; 複写( 集合六, 集合六 + 要素数六,
書式付出力 );
表示する ← "}" ← 改行;
表示する ← "の2番目の集合は1番目の集合の";
計算くん.部分集合かどうか( 集合五, 要素数五, 集合六, 要素数六
);
表示する ← 改行 ← 改行;
通常終了;
}
例2─4
このプログラムは汎用型、またはパラメータ型といわれるC++言語特有のテンプレートというものを使っております。これは例えばリスト(list<>)のテンプレートは日本語定義により、リストひな型というような名前を付けておりますが、詳しいテンプレートの使い方については、C++言語のリファレンスマニュアル等を参照願います。例2─4の日本語定義プログラムは次のようなC++言語のコードに展開されます。
// 集合計算を基本計算から継承させる仲間の集まり(クラス)を作る
#include <iostream.h>
#include <set>
#include <list>
class jp_str_0
{
public:
jp_str_0(void) { cout <<
"基本計算の対象を組み立てる" << endl; };
float jp_str_2 ( float, float );
float jp_str_3 ( float, float );
float jp_str_4( float, float );
float jp_str_5 ( float, float );
int jp_str_6 ( int, int );
bool jp_str_7( bool, bool );
bool jp_str_8( bool, bool );
~jp_str_0(void) { cout <<
"基本計算の対象を消滅させる" << endl; };
};
class jp_str_1 : public jp_str_0
{
public:
jp_str_1(void) { cout <<
"集合計算の対象を組み立てる" << endl; };
void jp_str_9 ( int *, int, int *,
int );
void jp_str_10( int *, int, int *,
int );
void jp_str_11( int *, int, int *,
int );
~jp_str_1(void) { cout <<
"集合計算の対象を消滅させる" << endl; };
};
inline float jp_str_0::jp_str_2 ( float jp_str_12, float jp_str_13 ) { return(
jp_str_12 + jp_str_13 ); };
inline float jp_str_0::jp_str_3 ( float jp_str_12, float jp_str_13 ) { return(
jp_str_12 - jp_str_13 ); };
inline float jp_str_0::jp_str_4( float jp_str_12, float jp_str_13 ) { return(
jp_str_12 * jp_str_13 ); };
inline float jp_str_0::jp_str_5 ( float jp_str_12, float jp_str_13 ) { return(
jp_str_12 / jp_str_13 ); };
inline int jp_str_0::jp_str_6 ( int jp_str_12, int jp_str_13 ) { return(
jp_str_12 % jp_str_13 ); };
inline bool jp_str_0::jp_str_7( bool jp_str_12, bool jp_str_13 ) { return(
jp_str_12 && jp_str_13 ); };
inline bool jp_str_0::jp_str_8( bool jp_str_12, bool jp_str_13 ) { return(
jp_str_12 || jp_str_13 ); };
ostream_iterator< int, char, char_traits< char > > jp_str_16(cout, " ");
void jp_str_1::jp_str_9( int *jp_str_12, int jp_str_13, int *jp_str_14, int
jp_str_15 )
{
list<int> jp_str_17(jp_str_12,
jp_str_12 + jp_str_13);
list<int> jp_str_18(jp_str_14,
jp_str_14 + jp_str_15);
set_union ( jp_str_17.begin(),
jp_str_17.end(), jp_str_18.begin(), jp_str_18.end(), jp_str_16 );
};
void jp_str_1::jp_str_10( int *jp_str_12, int jp_str_13, int *jp_str_14, int
jp_str_15 )
{
list<int> jp_str_17(jp_str_12,
jp_str_12 + jp_str_13);
list<int> jp_str_18(jp_str_14,
jp_str_14 + jp_str_15);
set_intersection ( jp_str_17.begin(),
jp_str_17.end(), jp_str_18.begin(), jp_str_18.end(), jp_str_16 );
};
void jp_str_1::jp_str_11( int *jp_str_12, int jp_str_13, int *jp_str_14, int
jp_str_15 )
{
list<int> jp_str_17(jp_str_12,
jp_str_12 + jp_str_13);
list<int> jp_str_18(jp_str_14,
jp_str_14 + jp_str_15);
if ( includes( jp_str_17.begin(),
jp_str_17.end(), jp_str_18.begin(), jp_str_18.end() ) )
cout << "部分集合である。";
else cout << "部分集合ではない。";
};
int main(void)
{
jp_str_1 jp_str_23;
cout << endl;
cout << "567 足す 2010 は " <<
jp_str_23.jp_str_2( 567, 2010 ) << endl;
cout << "80.76 引く 20 は " <<
jp_str_23.jp_str_3( 80.76, 20 ) << endl;
cout << "18 掛ける 369 は " <<
jp_str_23.jp_str_4( 18, 369 ) << endl;
cout << "75.3 割る 19 は " <<
jp_str_23.jp_str_5( 75.3, 19 ) << endl;
cout << "193を4で割った余りは " <<
jp_str_23.jp_str_6( 193, 4 ) << endl;
cout << "真と真の論理積は " << (
jp_str_23.jp_str_7( true, true ) ? "真" : "偽" ) << endl;
cout << "真と偽の論理積は " << (
jp_str_23.jp_str_7( true, false ) ? "真" : "偽" ) << endl;
cout << "偽と真の論理積は " << (
jp_str_23.jp_str_7( false, true ) ? "真" : "偽" ) << endl;
cout << "偽と偽の論理積は " << (
jp_str_23.jp_str_7( false, false ) ? "真" : "偽" ) << endl;
cout << "真と真の論理和は " << (
jp_str_23.jp_str_8( true, true ) ? "真" : "偽" ) << endl;
cout << "真と偽の論理和は " << (
jp_str_23.jp_str_8( true, false ) ? "真" : "偽" ) << endl;
cout << "偽と真の論理和は " << (
jp_str_23.jp_str_8( false, true ) ? "真" : "偽" ) << endl;
cout << "偽と偽の論理和は " << (
jp_str_23.jp_str_8( false, false ) ? "真" : "偽" ) << endl;
int jp_str_17[5] =
{100,300,500,700,900};
int jp_str_18[5] =
{200,400,600,800,1000};
cout << "{ "; copy( jp_str_17,
jp_str_17 + 5, jp_str_16 );
cout << "}と{ "; copy( jp_str_18,
jp_str_18 + 5, jp_str_16 );
cout << "}" << endl;
cout << "の和集合は { ";
jp_str_23.jp_str_9( jp_str_17, 5,
jp_str_18, 5 );
cout << "}" << endl;
int jp_str_19[5] =
{200,400,600,800,1000};
int jp_str_20[6] =
{100,200,500,700,900,1000};
cout << "{ "; copy( jp_str_19,
jp_str_19 + 5, jp_str_16 );
cout << "}と{ "; copy( jp_str_20,
jp_str_20 + 6, jp_str_16 );
cout << "}" << endl;
cout << "の共通集合は { ";
jp_str_23.jp_str_10( jp_str_19, 5,
jp_str_20, 6 );
cout << "}" << endl;
int jp_str_21[5] =
{200,400,600,800,1000};
int jp_str_22[3] = {200,400,600};
cout << "{ "; copy( jp_str_21,
jp_str_21 + 5, jp_str_16 );
cout << "}と{ "; copy( jp_str_22,
jp_str_22 + 3, jp_str_16 );
cout << "}" << endl;
cout << "の2番目の集合は1番目の集合の";
jp_str_23.jp_str_11( jp_str_21, 5,
jp_str_22, 3 );
cout << endl << endl;
return 0;
}
例2-4を展開したもの
例2-4のプログラムを実行させた結果は以下のとおりです。
このようにオブジェクト指向言語といわれるC++言語では、仲間の集まり(クラス)を継承することで、今あるソフトウェア財産をそのまま利用して無駄なく機能拡張ができます。さらに対象(オブジェクト)の生成では、まず基本(親)となる仲間の集まり(クラス)が生成され、次に継承(子)された仲間の集まり(クラス)が生成されることが、この実行結果により分かります。また、対象(オブジェクト)の消滅では、逆に継承(子)された仲間の集まり(クラス)が消滅して、最後に基本(親)となる仲間の集まり(クラス)が消滅しますので、生成と消滅はちょうど入れ子のような格好になっていることも分かります。
次の例は、平成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日が日曜日であったことを利用して、指定した日の曜日を求める
#include <iostream.h>
#include<string>
#日本語定義 引数
#日本語定義 その年
#日本語定義 その月
#日本語定義 その日
#日本語定義 年のデータ "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.h>
#include<string>
class jp_str_9 {
private:
int jp_str_12;
int jp_str_13[12];
public:
jp_str_9(void) {
jp_str_12 = 0;
jp_str_13[0] = 31; jp_str_13[1] = 28; jp_str_13[2] = 31;
jp_str_13[3] = 30; jp_str_13[4] = 31; jp_str_13[5] = 30;
jp_str_13[6] = 31; jp_str_13[7] = 31; jp_str_13[8] = 30;
jp_str_13[9] = 31; jp_str_13[10] = 30; jp_str_13[11] = 31;
};
bool jp_str_10(int) ;
int jp_str_11( int, int );
bool jp_str_5( int, int, int );
};
bool jp_str_9::jp_str_10(int jp_str_1) {
if ( ( (jp_str_1 % 4)== 0 ) &&
(
(jp_str_1 % 100)!= 0) ||
( (jp_str_1 %
400)== 0 ) )
{ return true; }
else return false;
};
int jp_str_9::jp_str_11( int y_val, int m_val ) {
if ( ( jp_str_10( y_val ) == true )
&& ( m_val <= 2 ) )
jp_str_12 = -1;
for ( int jp_str_1 = 1989; jp_str_1
<= ( y_val ); ++jp_str_1 ) {
if ( jp_str_10( jp_str_1 ) == true ) ++jp_str_12;
}
return(jp_str_12);
};
bool jp_str_9::jp_str_5( 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 ) && jp_str_10( y_val ) )
{ return true; }
else if ( d_val <= jp_str_13[m_val-1] )
{ return true; }
}
return false;
};
class jp_str_15 {
private:
string jp_str_16[7];
public:
jp_str_15(void) {
jp_str_16[0] = "日"; jp_str_16[1] = "月"; jp_str_16[2] = "火";
jp_str_16[3] = "水"; jp_str_16[4] = "木"; jp_str_16[5] = "金";
jp_str_16[6] = "土";
};
string jp_str_21(int jp_str_3) {
return jp_str_16[jp_str_3];
};
};
class jp_str_17 {
private:
int jp_str_18[12];
int jp_str_19;
public:
jp_str_17(void) {
jp_str_18[0] = 0; jp_str_18[1] = 3; jp_str_18[2] = 3;
jp_str_18[3] = 6; jp_str_18[4] = 1; jp_str_18[5] = 4;
jp_str_18[6] = 6; jp_str_18[7] = 2; jp_str_18[8] = 5;
jp_str_18[9] = 0; jp_str_18[10] = 3; jp_str_18[11] = 5;
}
int jp_str_20( int jp_str_1, int
jp_str_2, int jp_str_3 ) {
jp_str_9 jp_str_6;
jp_str_19 = (jp_str_1-1) + jp_str_18[jp_str_2-1]
+ jp_str_3 - 1 + jp_str_6.jp_str_11(jp_str_1,jp_str_2);
return jp_str_19;
}
};
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;
jp_str_9 jp_str_6;
if ( jp_str_6.jp_str_5( y_val, m_val,
d_val ) )
{
cout << "西暦" << y_val << "年" << m_val << "月" << d_val << "日は";
jp_str_17 jp_str_7;
int jp_str_19 =
jp_str_7.jp_str_20( y_val, m_val, d_val );
jp_str_15 jp_str_8;
cout << jp_str_8.jp_str_21(jp_str_19 % 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++言語用に書き換えることができたのではないかと感じました。
π
の計算
次の例はC言語編で使用した例題をC++言語版として移植したものです。これは任意の
桁数、π
の値を計算するプログラムです。
今回のプログラムはC言語編と同様に二つに分かれており、一つははじまり(main)のメイン関数の部分です。もう一つのファイルは、マチンの逆正接演算、長い配列の加減除算などから成っております。それぞれ別々に翻訳(コンパイル)しておき、後から次のようなコマンドで結合します。
>bcc32 ファイル1.obj ファイル2.obj
この場合、C言語編と同様に以下のように入力します。
この結果、ファイル1とファイル2が結合されて、ファイル1.exeという実行可能ファイルが生成されます。外部宣言を行う場合には、呼び出す側と呼び出される側で、同じ名前として定義する必要があります。(正確には関数の呼び出し規約 を合わせることも必要です)
/* マチンの公式を使用して、任意の桁数πの値を計算するプログラム */
#include <iostream.h>
#include <my_now_time.h>
#日本語定義 配列の最大値 "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] = "九";
};
文字列の仲間 漢数字の表示データ( 整数型 その数字 ) {
戻る 十の漢数字[その数字];
};
};
#日本語定義 剰余計算 "%"
#日本語定義 作業用配列一
#日本語定義 作業用配列二
#日本語定義 πの結果
#日本語定義 計算前の現在時間 "NOW_TIME1"
#日本語定義 計算後の現在時間 "NOW_TIME2"
整数型 はじまり(型なし)
{
整数型 作業用配列一[配列の最大値+1],
作業用配列二[配列の最大値+1],
πの結果[配列の最大値+1];
整数型 反復数;
計算前の現在時間;
マチンの逆正接演算( 作業用配列一, 16, 5 );
マチンの逆正接演算( 作業用配列二, 4, 239 );
長い配列の減算( πの結果, 作業用配列一, 作業用配列二 );
計算後の現在時間;
漢数字の仲間 漢太郎;
表示する ← 漢太郎.漢数字の表示データ(πの結果[0]) ← ".\n";
反復 ( 反復数 = 1; 反復数 <= 求める桁数; 反復数++ ) {
表示する ← 漢太郎.漢数字の表示データ(πの結果[反復数]);
もし ( 反復数 剰余計算 10 == 0 ) 表示する ← " " ;
もし ( 反復数 剰余計算 50 == 0 ) 表示する ← "\n";
}
通常終了;
}
例2─6
外部宣言の本体であるもう一つのファイルの方は、C言語編でも一応お断りしましたが、掲載は差し控えさせていただきます。また、逆正接演算というのはアークタンジェント演算の意味です。例2─6で時間計測のためのインクルード部分は以下のようになります。
#include <sstream>
#include <time.h>
ostream& operator<< (ostream& out_strm, const struct tm& t_block )
{
out_strm << t_block.tm_min << "分" << t_block.tm_sec << "秒";
return out_strm;
}
#define NOW_TIME1 time_t tm = time(NULL);\
struct tm* tm1 = localtime(&tm);\
struct tm time_block;\
memcpy( &time_block, tm1, sizeof(struct tm) );\
cout << time_block << endl
#define NOW_TIME2 tm = time(NULL);\
tm1 = localtime(&tm);\
memcpy( &time_block, tm1, sizeof(struct tm) );\
cout << time_block << endl
例2─6 インクールドファイル部
例2─6のプログラムは、あらかじめ現在の時間を表示するためのインクルード部を別に定義してあります。このインクルード部の中ではマクロ定義とともに、オーバーロード演算子というものを使い、時間を表示するところが簡潔に表現してあります。この場合、「<<」という出力ストリームの演算子をユーザー側で後から再定義できるような機能です。
また、仲間の集まり(クラス)のメンバーはデフォルトで仲間内で使用可(プライベート、private)となります。今回のプログラムでは、最初に この仲間内で使用可というアクセスを指定するように記述してありますが、特にこれはなくても構いません。分かりやすくするために一応書いています。例2─6のプログラムを展開すると以下のようになります。
/* マチンの公式を使用して、任意の桁数πの値を計算するプログラム */
#include <iostream.h>
#include <my_now_time.h>
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 jp_str_1 {
private:
string jp_str_2[10];
public:
jp_str_1(void) {
jp_str_2[0] = "〇"; jp_str_2[1] = "一"; jp_str_2[2] = "二";
jp_str_2[3] = "三"; jp_str_2[4] = "四"; jp_str_2[5] = "五";
jp_str_2[6] = "六"; jp_str_2[7] = "七"; jp_str_2[8] = "八";
jp_str_2[9] = "九";
};
string jp_str_3( int jp_str_4 ) {
return jp_str_2[jp_str_4];
};
};
int main(void)
{
int jp_str_6[10010+1],
jp_str_7[10010+1],
jp_str_8[10010+1];
int jp_str_0;
NOW_TIME1;
machins_arctan( jp_str_6, 16, 5 );
machins_arctan( jp_str_7, 4, 239 );
lng_dim_sub( jp_str_8, jp_str_6, jp_str_7 );
NOW_TIME2;
jp_str_1 jp_str_5;
cout << jp_str_5.jp_str_3(jp_str_8[0]) << ".\n";
for ( jp_str_0 = 1; jp_str_0 <= 10000; jp_str_0++ ) {
cout << jp_str_5.jp_str_3(jp_str_8[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桁で試してみました。また、日本語を多めにというテーマですので、出力は漢数字で行ってみました。実行時間はIntel dual core CPU(2.3GHz)での時間計測結果によると約4秒程度で、結果の数値についてはほかのサイトで公開している数値と比べてみたところ間違いはないようです。
今回、あまりにも結果が長くなり過ぎることと、表示のフォントの大きさの問題もあって、出力結果をWindowsのクリップボードにコピーしておき、それをワープロのクリップボード貼り付け機能で表示させることにします。また、出力結果については、最初の部分と最後の部分だけにしてあります。
(補則)これまでC++言語編での例題はすべてCUI(コマンド・ユーザー・インターフェース)ばかりでした。Java編ではGUI(グラフィック・ユーザー・インターフェース)の例題もありますので、このC++言語編を読まれた方の中にはどうもC++言語はGUIができないのではないかと思われる方があるといけないと思い、誤解のないように一応説明いたします。
これまでの例題とは違い、有償のBuilderというものを別途入手されますと、VCL(ビジュアル・コンポーネント・ライブラリー)というものを使うことができます。これを使えば、Windows7上のマウスで選択するアプリケーションなどを楽に作ることができます。以下はVCLを使って作った「簡単なワードプロセッサー」の例です。このようにBuilderのVCLとC++言語を併用することで、Java編と同様にさまざまなGUIアプリケーションを比較的短時間で作ることが可能になります。
今回は各言語で日本語を多めにということがテーマですので、このC++言語編ではやはりCUIを使った例題が主体になります。
次は例2─4で集合計算を行うクラスを定義しましたが、これを使用する場合Pascalの集合型のようなあたかも算術演算をするように記述する方法があります。C++では演算子のオーバーロードというものが用意されております ので、これを利用して集合の和(union)、積(intersection)、差(difference)の演算を通常の算術演算の「+」、「*」、「-」という演算子を使って 集合計算を行うプログラムを書いてみます。
// 例2─4の集合計算を別定義の演算子(オーバーロード演算子)を用いて計算する
#include <iostream.h>
#include <set>
#日本語定義 集合計算の仲間
#日本語定義 和集合を求める
#日本語定義 共通集合を求める
#日本語定義 部分集合かどうか
#日本語定義 引数一
#日本語定義 引数二
#日本語定義 内部引数一
#日本語定義 内部引数二
#日本語定義 対象引数一
#日本語定義 対象引数二
#日本語定義 結果の引数一
#日本語定義 演算結果
#日本語定義 結果の挿入
#日本語定義 出力の値
#日本語定義 出力ストリーム "ostream"
#日本語定義 別定義の演算子+ "operator+"
#日本語定義 別定義の演算子* "operator*"
#日本語定義 別定義の演算子- "operator-"
#日本語定義 別定義の演算子<< "operator<<"
#日本語定義 和集合の関数 "set_union"
#日本語定義 共通集合の関数 "set_intersection"
#日本語定義 部分集合の関数 "includes"
#日本語定義 差集合の関数 "set_difference"
#日本語定義 書式付出力
出力反復子< 整数型, 文字型, 文字の特性< 文字型 > > 書式付出力(表示する, " ");
仲間の集まり 集合計算の仲間
{
仲間内で使用可:
集合ひな型 演算結果;
整数型 番地|内部引数一, 内部引数二;
全域で使用可:
集合計算の仲間(型なし) { };
集合計算の仲間( 整数型 番地|引数一, 整数型 引数二 ) {
内部引数一 = 引数一; 内部引数二 = 引数二;
集合ひな型 集合一( 内部引数一, 内部引数一 + 内部引数二 );
入力反復子< 集合ひな型 > 結果の挿入( 演算結果, 演算結果.最初 );
表示する ← "{ ";
複写( 集合一.最初, 集合一.最後, 書式付出力 );
表示する ← "}";
};
直接出入可 集合計算の仲間 別定義の演算子+( 集合計算の仲間&
対象引数一, 集合計算の仲間& 対象引数二 ) {
集合ひな型 集合一( 対象引数一.内部引数一, 対象引数一.内部引数一 + 対象引数一.内部引数二 ),
集合二( 対象引数二.内部引数一, 対象引数二.内部引数一 + 対象引数二.内部引数二 );
入力反復子< 集合ひな型 > 結果の挿入( 対象引数一.演算結果, 対象引数一.演算結果.最初 );
和集合の関数 ( 集合一.最初, 集合一.最後, 集合二.最初, 集合二.最後, 結果の挿入 );
戻る 対象引数一;
};
直接出入可 集合計算の仲間 別定義の演算子*( 集合計算の仲間&
対象引数一, 集合計算の仲間& 対象引数二 ) {
集合ひな型 集合一( 対象引数一.内部引数一, 対象引数一.内部引数一 + 対象引数一.内部引数二 ),
集合二( 対象引数二.内部引数一, 対象引数二.内部引数一 + 対象引数二.内部引数二 );
入力反復子< 集合ひな型 > 結果の挿入( 対象引数一.演算結果, 対象引数一.演算結果.最初 );
共通集合の関数 ( 集合一.最初, 集合一.最後, 集合二.最初, 集合二.最後, 結果の挿入 );
戻る 対象引数一;
};
直接出入可 集合計算の仲間 別定義の演算子-( 集合計算の仲間&
対象引数一, 集合計算の仲間& 対象引数二 ) {
集合ひな型 集合一( 対象引数一.内部引数一, 対象引数一.内部引数一 + 対象引数一.内部引数二 ),
集合二( 対象引数二.内部引数一, 対象引数二.内部引数一 + 対象引数二.内部引数二 );
入力反復子< 集合ひな型 > 結果の挿入( 対象引数一.演算結果, 対象引数一.演算結果.最初 );
差集合の関数 ( 集合一.最初, 集合一.最後, 集合二.最初, 集合二.最後, 結果の挿入 );
戻る 対象引数一;
};
直接出入可 出力ストリーム& 別定義の演算子<<( 出力ストリーム&
出力の値, 集合計算の仲間& 対象引数一 ) {
出力の値 << "{ ";
複写( 対象引数一.演算結果.最初, 対象引数一.演算結果.最後, 書式付出力 );
出力の値 << "}";
戻る 出力の値;
};
~集合計算の仲間(型なし) { };
};
#日本語定義 要素数一 "5"
#日本語定義 要素数二 "5"
#日本語定義 要素数三 "5"
#日本語定義 要素数四 "6"
#日本語定義 要素数五 "10"
#日本語定義 要素数六 "6"
整数型 はじまり(型なし)
{
整数型 集合一[5] = {100,300,500,700,900};
整数型 集合二[5] = {200,400,600,800,1000};
整数型 集合三[5] = {200,400,600,800,1000};
整数型 集合四[6] =
{100,200,500,700,900,1000};
整数型 集合五[10] =
{100,200,300,400,500,600,700,800,900,1000};
整数型 集合六[6] =
{100,200,500,700,900,1000};
集合計算の仲間 S1( 集合一, 要素数一 ), S2( 集合二,
要素数二 ), S0;
S0 = S1 + S2;
表示する ← 改行 ← "の和集合は" ← S0 ← 改行 ← 改行;
集合計算の仲間 S3( 集合三, 要素数三 ), S4( 集合四,
要素数四 );
S0 = S3 * S4;
表示する ← 改行 ← "の積集合は" ← S0 ← 改行 ← 改行;
集合計算の仲間 S5( 集合五, 要素数五 ), S6( 集合六,
要素数六 );
S0 = S5 - S6;
表示する ← 改行 ← "の差集合は" ← S0 ← 改行 ← 改行;
通常終了;
}
#日本語定義 集合一
#日本語定義 集合二
#日本語定義 集合三
#日本語定義 集合四
#日本語定義 集合五
#日本語定義 集合六
#日本語定義 S0
#日本語定義 S1
#日本語定義 S2
#日本語定義 S3
#日本語定義 S4
#日本語定義 S5
#日本語定義 S6
例2─7
オーバーロード演算子を使う場合、例2─7では「S0=S1+S2」という部分は「S0=operator+(S1,S2)」と書くことと同じことになります。operator演算子で引数を2つ使用する場合には関数をメンバー関数ではなく、直接出入可の(friend)関数として定義する必要があるということです。例2─7のプログラムを展開すると以下のようになります。
// 例2─4の集合計算を別定義の演算子(オーバーロード演算子)を用いて計算する
#include <iostream.h>
#include <set>
ostream_iterator< int, char, char_traits< char > > jp_str_14(cout, " ");
class jp_str_0
{
private:
set<int, less<int> > jp_str_11;
int *jp_str_6, jp_str_7;
public:
jp_str_0(void) { };
jp_str_0( int *jp_str_4, int jp_str_5
) {
jp_str_6 = jp_str_4; jp_str_7 = jp_str_5;
set<int, less<int> > jp_str_15( jp_str_6, jp_str_6 + jp_str_7 );
insert_iterator< set<int, less<int> > > jp_str_12( jp_str_11, jp_str_11.begin()
);
cout << "{ ";
copy( jp_str_15.begin(), jp_str_15.end(), jp_str_14 );
cout << "}";
};
friend jp_str_0 operator+( jp_str_0&
jp_str_8, jp_str_0& jp_str_9 ) {
set<int, less<int> > jp_str_15( jp_str_8.jp_str_6, jp_str_8.jp_str_6 +
jp_str_8.jp_str_7 ),
jp_str_16( jp_str_9.jp_str_6, jp_str_9.jp_str_6 + jp_str_9.jp_str_7 );
insert_iterator< set<int, less<int> > > jp_str_12( jp_str_8.jp_str_11,
jp_str_8.jp_str_11.begin() );
set_union ( jp_str_15.begin(), jp_str_15.end(), jp_str_16.begin(),
jp_str_16.end(), jp_str_12 );
return jp_str_8;
};
friend jp_str_0 operator*( jp_str_0&
jp_str_8, jp_str_0& jp_str_9 ) {
set<int, less<int> > jp_str_15( jp_str_8.jp_str_6, jp_str_8.jp_str_6 +
jp_str_8.jp_str_7 ),
jp_str_16( jp_str_9.jp_str_6, jp_str_9.jp_str_6 + jp_str_9.jp_str_7 );
insert_iterator< set<int, less<int> > > jp_str_12( jp_str_8.jp_str_11,
jp_str_8.jp_str_11.begin() );
set_intersection ( jp_str_15.begin(), jp_str_15.end(), jp_str_16.begin(),
jp_str_16.end(), jp_str_12 );
return jp_str_8;
};
friend jp_str_0 operator-( jp_str_0&
jp_str_8, jp_str_0& jp_str_9 ) {
set<int, less<int> > jp_str_15( jp_str_8.jp_str_6, jp_str_8.jp_str_6 +
jp_str_8.jp_str_7 ),
jp_str_16( jp_str_9.jp_str_6, jp_str_9.jp_str_6 + jp_str_9.jp_str_7 );
insert_iterator< set<int, less<int> > > jp_str_12( jp_str_8.jp_str_11,
jp_str_8.jp_str_11.begin() );
set_difference ( jp_str_15.begin(), jp_str_15.end(), jp_str_16.begin(),
jp_str_16.end(), jp_str_12 );
return jp_str_8;
};
friend ostream& operator<<( ostream&
jp_str_13, jp_str_0& jp_str_8 ) {
jp_str_13 << "{ ";
copy( jp_str_8.jp_str_11.begin(), jp_str_8.jp_str_11.end(), jp_str_14 );
jp_str_13 << "}";
return jp_str_13;
};
~jp_str_0(void) { };
};
int main(void)
{
int jp_str_15[5] =
{100,300,500,700,900};
int jp_str_16[5] =
{200,400,600,800,1000};
int jp_str_17[5] =
{200,400,600,800,1000};
int jp_str_18[6] =
{100,200,500,700,900,1000};
int jp_str_19[10] =
{100,200,300,400,500,600,700,800,900,1000};
int jp_str_20[6] =
{100,200,500,700,900,1000};
jp_str_0 jp_str_22( jp_str_15, 5 ),
jp_str_23( jp_str_16, 5 ), jp_str_21;
jp_str_21 = jp_str_22 + jp_str_23;
cout << endl << "の和集合は" << jp_str_21
<< endl << endl;
jp_str_0 jp_str_24( jp_str_17, 5 ),
jp_str_25( jp_str_18, 6 );
jp_str_21 = jp_str_24 * jp_str_25;
cout << endl << "の積集合は" << jp_str_21
<< endl << endl;
jp_str_0 jp_str_26( jp_str_19, 10 ),
jp_str_27( jp_str_20, 6 );
jp_str_21 = jp_str_26 - jp_str_27;
cout << endl << "の差集合は" << jp_str_21
<< endl << endl;
return 0;
}
例2-7を展開したもの
これをコパイルさせるとoperator演算子の使用に伴うreturn文の注意について一応警告が出ますが、今回は 以下のように特定の警告メッセージを無効にするようにしております。また、例2─7を実行させた結果も以下に示します。
C言語編でも述べたように、日本語カスタマイザーを利用してC++言語を翻訳(コンパイル)するときに、プログラム中に文法的な問題があると警告メッセージやエラーメッセージが表示されます。このときにjp_str_*(*は数字)についてのエラー情報が出た場合、このjp_str_*とソースコード中の日本語文字列がどのように対応するのかが分からないとデバッグしにくいことが挙げられます。もっともエラーが出た行番号の情報は出力されますので、ソースコード上でどの行に問題があるのかは分かります。
このようなデバッグ状況に対処する方法としては、日本語カスタマイザーが出力する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