例題として、逆ポーランド電卓を作ります。
逆ポーランド電卓とは、「1 2 +」は「3」、「15 10 -」は「5」、「15 10 - 3 *」は「15」
というような感じで、数字が入力されたらスタックにプッシュし、+のような演算子が来たら
スタックから数字を2つポップして計算結果をスタックにプッシュするような計算機です。
※ただし、javaで引数を「*」と入力すると、ディレクトリ内にあるファイル名と解釈されるため、
かけ算の「*」は「@」で代用することにする。
逆ポーランド電卓を作るためにはスタックを作る必要があります。
コンストラクタにスタックサイズを指定します。デフォルトは100です。デフォルトでは
int 100個分の配列をnewします。また、pushで値をこの配列に格納します。popでは、pushした値を
配列から取り出して返します。getLengthでは現在のスタックに溜められている個数を返します。peekでは
popと同じですが、スタックの位置を変更しないよう、スタック要素を覗き見るようにします。
メンバー変数は、配列を格納するint型ポインタと、現在のスタック位置を示す
スタックポインタを持ちます。
なお、コマンドラインからの引数は、mainメソッドのStringの配列argsに配列で渡されます。
すなわちコマンドラインの引数が、半角スペースが区切りになって分けられ、配列になります。
個数は、配列.lengthで求めることができます。
以上をクラスにすると以下のようになるはずです。
public class IntStack{
////////////////////////////////////////////////////
// 管理情報
int m_sp; // スタックポインタ:次にプッシュする位置
int m_size; // スタックサイズ
int m_stack[]; // スタック
// コンストラクタ:引数あり
// IntStack オブジェクトが定義された時に自動的に呼び出される
public IntStack( int sz ){
// スタックサイズを保存する
m_size = sz ;
// スタックの実体(size個のint配列)を自由記憶上に割り当てる
m_stack = new int[m_size] ;
// スタックポインタを初期化する
m_sp = 0 ;
}
// コンストラクタ:引数なし
// IntStack オブジェクトが定義された時に自動的に呼び出される
public IntStack( ){
// スタックサイズを保存する
m_size = 100;
// スタックの実体(size個のint配列)を自由記憶上に割り当てる
m_stack = new int[m_size];
// スタックポインタを初期化する
m_sp = 0;
}
// プッシュ
public void push( int value ){
if( m_sp >= m_size ){
System.err.println("stack overflow");
System.exit(1) ; // 異常終了する
}
m_stack[m_sp] = value ;
m_sp = m_sp + 1 ;
}
// ポップ
public int pop(){
// アンダーフローのチェック
if( m_sp <= 0 ){
System.err.println("stack underflow");
System.exit(1) ; // 異常終了する
}
m_sp = m_sp - 1;
return m_stack[m_sp];
}
// 現在のスタック長を獲得する
public int getLength(){
return m_sp;
}
// 指定位置(スタックトップからのオフセット)のスタック要素を覗き見る
public int peek(){
// オフセットのチェック
if( 0 >= m_sp ){
System.err.println("stack underflow");
System.exit(1) ; // 異常終了する
}
// 指定位置の要素をコピーする
return m_stack[m_sp] - 1;
}
}
|
import java.io.*;
class Calc{
public static void main(String args[]){
int value, operand1, operand2 ;
// スタックテンプレートクラスから、実数スタックを具体化する
IntStack ostack = new IntStack( args.length );
// コマンド行引数からリバーシュポーリッシュ記法で記述された式を
// 読み込み計算する
for( int i = 0 ; i < args.length ; i = i + 1 ){
// 加法
if( args[i].equals("+") ){
operand2 = ostack.pop();
operand1 = ostack.pop();
ostack.push( operand1 + operand2 );
}
// 減法
else if( args[i].equals("-") ){
operand2 = ostack.pop();
operand1 = ostack.pop();
ostack.push( operand1 - operand2 );
}
// 乗法
else if( args[i].equals("@") ){
operand2 = ostack.pop();
operand1 = ostack.pop();
ostack.push( operand1 * operand2 );
}
// 除法
else if( args[i].equals("/") ){
operand2 = ostack.pop();
operand1 = ostack.pop();
// 0除算のチェック
if( operand2 == 0 ){
System.err.println("divid by 0");
System.exit(1); // 異常終了する
}
ostack.push( operand1 / operand2 );
}
// 上記以外は整数を仮定する
else{
value = (new Integer(args[i])).intValue();
ostack.push( value );
}
}
// スタックの整合性の確認
if( ostack.getLength( ) != 1 ){
System.err.println("illegal expression");
System.exit(1); // 異常終了する
}
// 答を出力する
value = ostack.pop();
System.out.println(value);
// 正常終了
}
}
|
C:\java>java clac 15 10 - 3 @
15
C:\java>
|
文字列を比較するときは、「==」ではなく、Stringクラスの「equals」メソッドを使用します。
この理由については後述します。