STL Samples : <queue>
キュー
queue
優先順位キュー
priority_queue
#include <iostream>
#include <queue>
using namespace std;
void std_queue() {
cout << "queue" << endl;
queue<int> que;
cout << "enqueue... ";
for ( int i = 0; i < 5; i++ ) {
que.push(i);
cout << i << ' ';
}
cout << endl << "deque... ";
while ( !que.empty() ) {
cout << que.front() << ' ';
que.pop();
}
cout << endl;
}
void std_priority_queue() {
cout << "priority_queue" << endl;
priority_queue<int> pque;
int input[] = { 1,7,6,4,3,2,5 };
cout << "enqueue... ";
for ( int i = 0; i < 7; i++ ) {
pque.push(input[i]);
cout << input[i] << ' ';
}
cout << endl << "deque... ";
while ( !pque.empty() ) {
cout << pque.top() << ' ';
pque.pop();
}
cout << endl;
}
STL Samples : <utility>
関係演算子
operator!=
operator<=
operator>
operator>=
namespace rel_ops {
template<class T> bool operator!=(const T&, const T&);
template<class T> bool operator> (const T&, const T&);
template<class T> bool operator<=(const T&, const T&);
template<class T> bool operator>=(const T&, const T&);
};
Tに比較演算==,<が定義されているとき、==から!=を、<から>,<=,>=を生成します。
#include <iostream> #include <utility> #include "foo.h" using namespace std; using namespace std::rel_ops; void std_rel_ops() { cout << "operator!=, <=, >, >=" << endl; Foo f1(1); Foo f2(2); cout << boolalpha; cout << f1 << " == " << f2 << "..." << (f1 == f2) << endl; cout << f1 << " != " << f2 << "..." << (f1 != f2) << endl; cout << f1 << " < " << f2 << "..." << (f1 < f2) << endl; cout << f1 << " <= " << f2 << "..." << (f1 <= f2) << endl; cout << f1 << " > " << f2 << "..." << (f1 > f2) << endl; cout << f1 << " >= " << f2 << "..." << (f1 >= f2) << endl; }
pair
pair
make_pair
template <class T1, class T2>
struct pair {
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair();
pair(const T1& x, const T2& y);
template<class U, class V> pair(const pair<U, V> &p);
};
型T1,T2をメンバとする"2つ組"です。2つのpairの大小関係はそれぞれのfirstの大小関係に従い、両方のfirstが等しければsecondの大小関係に従います。
template <class T1, class T2> pair<T1,T2> make_pair(const T1& x, const T2& y);
pair<T1,T2>(x,y)を返します。
#include <iostream> #include <utility> #include "foo.h" #include "bar.h" using namespace std; void std_pair() { cout << "pair, make_pair" << endl; pair<Foo,Bar> p(1,2); cout << "pair<Foo,Bar>: [" << p.first << "," << p.second << ']' << endl; p = make_pair(Foo(3), Bar(4)); cout << "pair<Foo,Bar>: [" << p.first << "," << p.second << ']' << endl; }
<stx/cstring>
DDJ-J 1998年5月号で紹介したSTL拡張コンポーネント”STX”の改版です。C Magazine Jan 2000で公開したtokenizerをreviseしました。
c_tokenizer<class charT>
Cで用いられる文字列(char*, unsigned char*, wchar_t*)をトークン分割するクラスです。テンプレート引数にはchar, unsigned char, wchar_tを与えることができ、それぞれSBCS(ascii), MBCS(Shift-JIS/EUC), DBCS(UNICODE)に対応したトークン分割を行ないます。
※ c_tokenizer はトークン分割対象および区切り文字集合のコピーを生成しません。したがって、c_tokenizer を利用している間にそれら2つの文字列の内容が変更されたときの動作は保証されません。
template<class charT>
class c_tokenizer {
public:
typedef token_traits<charT> traits;
typedef traits::value_type value_type;
typedef traits::pointer pointer;
typedef traits::const_pointer const_pointer;
typedef traits::range_type range_type;
typedef traits::size_type size_type;
explicit c_tokenizer(const_pointer str =0, const_pointer del=0, bool ret=false)
: str_(str), del_(del), cur_(str_), ret_(ret) {}
bool empty();
pointer next();
range_type next_range();
pointer substr(range_type range);
size_type count() const;
void set_str(const_pointer str);
void set_delimiter(const_pointer del, bool ret =false);
private:
const_pointer str_;
const_pointer del_;
const_pointer cur_;
bool ret_;
void skip();
};
// sample
// "apple,banana,cherry" を "," で区切る
stx::c_tokenizer<char> tok("apple,banana,cherry", ",");
while ( !tok.empty() ) {
char* token = tok.next();
cout << token << " ";
delete[] token;
}
cout << endl;
c_tokenizer (コンストラクタ)
template<class charT>
c_tokenizer<charT>::c_tokenizer(const_pointer str =0, const_pointer del=0, bool ret=false)
: str_(str), del_(del), cur_(str_), ret_(ret) {}
- str
- :トークン分割対象となる文字列
- del
- :トークンを区切る文字の集合
- ret
- :区切り文字をトークンとして扱うならtrue
ret == true のとき、区切り文字をトークンとして扱います。たとえば、
// sample
// "apple_banana__cherry_" を "_" で区切る
bool ret = false;
stx::c_tokenizer<char> tok("apple_banana__cherry_", "_", ret);
while ( !tok.empty() ) {
char* token = tok.next();
cout << token << " ";
delete[] token;
}
cout << endl;
では “apple”, “banana”, “cherry” が切り出されますが、ret = true とすると、”apple”, “_”, “banana”, “_”, “_”, “cherry”, “_” が切り出されます。
※ Shift-JIS, EUS などのマルチバイト文字列を分割するときは、stx::c_tokenizer<unsigned char> を用います。このとき、文字列はすべて unsigned char* となります。したがって必要に応じて char -> unsigned char のキャストが必要になります。
// sample
// "りんご、みかん、バナナ" を "、" で区切る
typedef unsigned char uchar;
stx::c_tokenizer<uchar> tok((uchar)"りんご、みかん、バナナ", (uchar)"、");
while ( !tok.empty() ) {
uchar* token = tok.next();
cout << token << " ";
delete[] token;
}
cout << endl;
skip (private)
template<class charT>
void c_tokenizer<charT>::skip() {
if ( !ret_ && cur_ )
cur_ += traits::span(cur_,del_);
}
empty
template<class charT>
bool c_tokenizer<charT>::empty() {
skip();
return !cur_ || *cur_ == charT(0);
}
切り出すトークンが見つからないとき true を返します。
next
template<class charT>
c_tokenizer<charT>::pointer c_tokenizer<charT>::next() {
return substr(next_range());
}
切り出されたトークンを返します。
※ 切り出されたトークンはヒープ領域から operator new によって取得した領域です。したがって、得られた文字列は明示的に delete[] しなければなりません。
next_range
template<class charT>
c_tokenizer<charT>::range_type c_tokenizer<charT>::next_range() {
skip();
const_pointer start = cur_;
cur_ += traits::cspan(cur_, del_);
if ( ret_ && start == cur_ && traits::span(cur_,del_) )
cur_ = traits::advance(cur_);
return range_type(start-str_, cur_-start);
}
トークンを切り出し、その位置と長さを表す std::pair<String::size_type,String::size_type> rangeを返します。range.first はトークンの開始位置、range.second はトークンの長さです。
substr
template<class charT>
c_tokenizer<charT>::pointer c_tokenizer<charT>::substr(range_type range) {
pointer p = new value_type[range.second+1];
for ( int i = 0; i < range.second; ++i )
p[i] = str_[range.first + i];
p[range.second] = value_type(0);
return p;
}
引数 range が表すトークン文字列を返します。
※ 切り出されたトークンはヒープ領域から operator new によって取得した領域です。したがって、得られた文字列は明示的に delete[] しなければなりません。
set_str
template<class charT>
void c_tokenizer<charT>::set_str(const_pointer str) {
str_ = str;
cur_ = str;
}
トークン分割対象となる文字列を設定します。トークン分割の開始位置を0に設定します。
set_delimiter
template<class charT>
void c_tokenizer<charT>::set_delimiter(const_pointer del, bool ret) {
del_ = del;
ret_ = ret;
}
区切り文字の集合を設定します。区切り文字をトークンとして扱うときは第2引数を true としてください。
count
template<class charT>
c_tokenizer<charT>::size_type c_tokenizer<charT>::count() const {
size_t count = 0;
const_pointer currpos = cur_;
while ( *currpos ) {
if ( !ret_ )
currpos += traits::span(currpos, del_);
if ( !*currpos )
break;
const_pointer start = currpos;
if ( *currpos )
currpos += traits::cspan(currpos, del_);
if ( ret_ && start == currpos && traits::span(currpos,del_) )
currpos = traits::advance(currpos);
++count;
}
return count;
}
現在の位置から切り出されるトークンの数を返します。
st_tokenizer / mbs_tokenizer / wcs_tokenizer
typedef c_tokenizer<char> str_tokenizer; typedef c_tokenizer<unsigned char> mbs_tokenizer; typedef c_tokenizer<wchar_t> wcs_tokenizer;
使用頻度の多い c_tokenizer<char>, c_tokenizer<unsigned char> および c_tokenizer<wchar_t> をそれぞれ str_tokenizer, mbs_tokenizer, wcs_tokenizerとtypedefしました。
(実装メモ : traitsについて)
異なる文字セット(SBCS,MBCS,DBCS)に対応したc_tokenizerをひとつの実装で実現するため、traitsと呼ばれるテクニックを用いました。
たとえば文字列の中から区切り文字を探し出すのに、 SBCSならstrspn / MSCSなら_mbsspn / DBCSならwcsspn を呼ばなければなりません。このとき、traits と呼ばれるヘルパ関数の集合を定義し、それらtraitsのヘルパ関数を呼び出すことで文字セットに応じた関数の呼び分けを実現しています。
template<class charT> struct token_traits {};
// traits for SBCS(ascii)
template<> struct token_traits<char> {
typedef char value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef size_t size_type;
typedef std::pair<size_type,size_type> range_type;
static size_type span(const_pointer x, const_pointer y)
{ return strspn(x,y); }
static size_type cspan(const_pointer x, const_pointer y)
{ return strcspn(x,y); }
static const_pointer advance(const_pointer x)
{ return x+1; }
};
// traits for MBCS(Shift-JIS, EUC)
template<> struct token_traits<unsigned char> {
typedef unsigned char value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef size_t size_type;
typedef std::pair<size_type,size_type> range_type;
static size_type span(const_pointer x, const_pointer y)
{ return _mbsspn(x,y); }
static size_type cspan(const_pointer x, const_pointer y)
{ return _mbscspn(x,y); }
static const_pointer advance(const_pointer x)
{ return _mbsinc(x); }
};
// traits for DBCS(UNICODE)
template<> struct token_traits<wchar_t> {
typedef wchar_t value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef size_t size_type;
typedef std::pair<size_type,size_type> range_type;
static size_type span(const_pointer x, const_pointer y)
{ return wcsspn(x,y); }
static size_type cspan(const_pointer x, const_pointer y)
{ return wcscspn(x,y); }
static const_pointer advance(const_pointer x)
{ return x+1; }
};
stx::basic_symbol<T>
より速い検索を行なうには…
STLが提供するset(multiset)/map(multimep)は要素の大小関係に基づいてコンテナ内の要素を2進木上に配置します。このとき、挿入/削除/検索に要する時間計算量は(コンテナ内の要素数をNとして)logNです。
以下に示すコードは、int/stringをキーとするmapの検索に要する時間を計測するものです。
#include <windows.h>
#include <iostream>
#include <string>
#include <map>
using namespace std;
const int N = 1000;
int int_table[N];
string string_table[N];
void init() {
char buf[32];
string key;
for ( int i = 0; i < N; ++i ) {
int_table[i] = i;
sprintf(buf,"string as comparison key:%d",i);
string_table[i] = buf;
}
}
template<class Container, class T>
long trial(Container& container, const T* table, int repeat) {
int i;
// table[0..N-1]をcontainerに挿入する。
for (i = 0; i < N; ++i)
container[table[i]] = i;
// table[0..N-1]をcontainerから検索し、その処理時間を求める。
long t = GetTickCount();
while ( repeat-- )
for ( i = 0; i < N; ++i)
container.find(table[i]);
return GetTickCount() - t;
}
int main() {
map<int,int> intmap;
map<string,int> strmap;
init();
cout << "intmap " << trial(intmap, int_table, 100) << " [ms]\n";
cout << "strmap " << trial(strmap, string_table, 100) << " [ms]\n";
return 0;
}
| intmap | 90 [ms] |
|---|---|
| strmap | 1212 [ms] |
このように、同じmapであっても、intとstringでは処理速度に大きな差があることがわかります。stringをキーとするmapはなぜこんなに遅いのでしょう。
それは、stringの比較に要する時間がintに比べ非常に大きいからです。
2つのstring a, bを比較するには、 (i = 0 を初期値として)a[i]とb[i]を比較します。このとき a[i] != b[i] であればその時点で大小の判断が完了しますが、a[i] == b[i]であったときは、さらにその次の文字a[i+1]とb[i+1]について比較しなければなりません。
すなわち、文字列の比較は、比較される文字列が長いほど時間がかかることになります。上の例のように末尾だけが異なるような文字列ではその傾向が顕著です。
このように、大小の比較に時間のかかる要素をキーとするsetやmapはかなり遅いものとなります。もし要素の大小比較が瞬時にして完了するなら、set/mapからの要素の挿入/削除/検索スピードを劇的に改善できそうです。
たとえば文字列に対しユニークな数値を与える、すなわち文字列から数値への変換が定義できれば、set<string> は set<int> と同程度のスピードが見込めることでしょう。ただしこの変換を行なうとset内ではもはや文字列の大小関係に基づいた要素管理は行なわれませんから、コンテナ内の要素の格納順は予測できないものとなります。
文字列から数値への変換の最も高速なものはポインタです。すなわち、set<string>の代わりにset<string*>を使えば、set<int>並みのスピードが期待できます。
set<string*> spset; string value = "apple"; spset.insert(&value); ... set<string*>::iterator it = spset.find(&value); ...
しかしこれでは問題が発生します。
set<string*> spset; string value = "apple"; spset.insert(&value); ... string key = "apple"; set<string*>::iterator it = spset.find(&key); ...
上記のコードにおいてkeyとvalueの値は等しい(“apple”)のですが、ポインタ値は異なるので、set<string*>からvalueを検索できません。等しい要素からは等しい数値(ポインタ値)を対応付けなければなりません。
それを実現するのがbasic_symbolなのです。
ソースコード
#include <set>
#include <string>
namespace stx {
template<class Key, class Comp =std::less<Key> >
class basic_symbol {
public:
typedef unsigned long hash_type;
typedef Key key_type;
basic_symbol()
{ k = &(*key_pool().insert(key_type()).first); }
basic_symbol(const key_type& key)
{ k = &(*key_pool().insert(key).first); }
basic_symbol(const basic_symbol& sym)
: k(sym.k) {}
basic_symbol& operator=(const basic_symbol& sym)
{ k = sym.k; return *this; }
basic_symbol& operator=(const key_type& key)
{ k = &(*key_pool().insert(key).first); return *this; }
const key_type& key() const { return *k; }
hash_type hash() const { return hash_type(k); }
private:
typedef std::set<key_type, Comp> pool_allocator;
static pool_allocator& key_pool();
const key_type* k;
};
template<class Key, class Comp>
basic_symbol<Key,Comp>::pool_allocator&
basic_symbol<Key,Comp>::key_pool() {
static pool_allocator pool;
return pool;
}
template<class K, class C>
inline bool operator==(const basic_symbol<K,C>& x, const basic_symbol<K,C>& y)
{ return x.hash() == y.hash(); }
template<class K, class C>
inline bool operator!=(const basic_symbol<K,C>& x, const basic_symbol<K,C>& y)
{ return x.hash() != y.hash(); }
template<class K, class C>
inline bool operator<(const basic_symbol<K,C>& x, const basic_symbol<K,C>& y)
{ return x.hash() < y.hash(); }
template<class K, class C>
inline bool operator<=(const basic_symbol<K,C>& x, const basic_symbol<K,C>& y)
{ return x.hash() <= y.hash(); }
template<class K, class C>
inline bool operator>(const basic_symbol<K,C>& x, const basic_symbol<K,C>& y)
{ return x.hash() > y.hash(); }
template<class K, class C>
inline bool operator>=(const basic_symbol<K,C>& x, const basic_symbol<K,C>& y)
{ return x.hash() >= y.hash(); }
typedef basic_symbol<std::string> string_symbol;
typedef basic_symbol<std::wstring> wstring_symbol;
}
basic_symbolのからくり
basic_symbol<T>はその内部にset<T>のインスタンス’pool’をstaticメンバとして持っており、これが生成されたすべてのsymbolを抱き込んでいます。
symbolが生成されるとき、同じsymbolが既にpool内にあればそのsymbolのポインタを返します。そうでなければ新たに生成してpoolに登録し、そのポインタを返します。
こうすることでstring_symbol x("apple") と string_symbol y("apple")は同じ値(ポインタ値)を持つわけです。
コンストラクタ
basic_symbol()
key_type()を基にsymbolを生成します(したがってkey_typeはデフォルトコンストラクタが定義されていなければなりません)。
basic_symbol(const key_type& key)
keyに基づいてユニークな値を持つsymbolを生成します。
basic_symbol(const basic_symbol& sym)
symの持つsymbol値(ポインタ値)がコピーされます。
コピー演算子
basic_symbol& operator=(const basic_symbol& sym)
symの持つsymbol値(ポインタ値)がコピーされます。
basic_symbol& operator=(const key_type& key)
keyに基づいてユニークな値を持つsymbolがセットされます。
key / hash
const key_type& key() const
symbol値に対応するkey_typeを返します。
hash_type hash() const
symbol値を返します。
サンプルコード
冒頭に示したコードをbasic_symbolで書き換えてみました。
#include <windows.h>
#include <iostream>
#include <string>
#include <stx/symbol>
#include <map>
using namespace std;
const int N = 1000;
string string_table[N];
stx::string_symbol symbol_table[N];
void init() {
char buf[32];
string key;
for ( int i = 0; i < N; ++i ) {
sprintf(buf,"string as comparison key:%d",i);
string_table[i] = buf;
symbol_table[i] = stx::string_symbol(buf);
}
}
template<class Container, class T>
long trial(Container& container, const T* table, int repeat) {
// ...省略...
}
int main() {
map<std::string,int> strmap;
map<stx::string_symbol,int> symmap;
init();
cout << "strmap " << trial(strmap, string_table, 100) << " [ms]\n";
cout << "symmap " << trial(symmap, symbol_table, 100) << " [ms]\n";
return 0;
}
| strmap | 1162 [ms] |
|---|---|
| symmap | 70 [ms] |
…効果は歴然ですね。
<stx/string>
DDJ-J 1998年5月号で紹介したSTL拡張コンポーネント”STX”の改版です。C Magazine Jan 2000で公開したtokenizerをreviseしました。
tokenizer<class String>
標準C++ライブラリが提供する文字列クラス basic_string<charT> をトークン分割するクラスです。テンプレート引数にはstd::string もしくは std::wstring を与えることができます。
※ マルチバイト文字(Shift-JIS, EUCなど)は正しく分割できません。一旦DBCS(UNICODE)に変換しstd::wstringで扱うか、もしくは stx::c_tokenizer<unsigned char> をお使いください。
template<class String>
class tokenizer {
private:
String::size_type cur_;
String str_;
String del_;
bool ret_;
void skip();
public:
typedef std::pair<String::size_type,String::size_type> range_type;
tokenizer(const String& str, const String& del, bool ret=false);
bool empty();
String next();
range_type next_range();
void set_str(const String& str);
void set_delimiter(const String& del, bool ret=false);
size_t count() const;
};
// sample
// "apple,banana,cherry" を "," で区切る
stx::tokenizer<string> tok("apple,banana,cherry", ",");
while ( !tok.empty() ) {
cout << tok.next() << " ";
}
cout << endl;
tokenizer (コンストラクタ)
template<class String>
tokenizer<String>::tokenizer(const String& str, const String& del, bool ret)
: cur_(0), str_(str), del_(del), ret_(ret) {}
- str
- :トークン分割対象となる文字列
- del
- :トークンを区切る文字の集合
- ret
- :区切り文字をトークンとして扱うならtrue
ret == true のとき、区切り文字をトークンとして扱います。たとえば、
// sample
// "apple_banana__cherry_" を "_" で区切る
bool ret = false;
stx::tokenizer<string> tok("apple_banana__cherry_", "_", ret);
while ( !tok.empty() ) {
cout << tok.next() << " ";
}
cout << endl;
では “apple”, “banana”, “cherry” が切り出されますが、ret = true とすると、”apple”, “_”, “banana”, “_”, “_”, “cherry”, “_” が切り出されます。
skip (private)
template<class String>
void tokenizer<String>::skip() {
if ( cur_ == str_.length() )
cur_ = String::npos;
if ( !ret_ && cur_ != String::npos ) {
String::size_type tmp = str_.find_first_not_of(del_, cur_);
if ( tmp != String::npos )
cur_ = tmp;
}
}
empty
template<class String>
bool tokenizer<String>::empty() {
skip();
return cur_ == String::npos;
}
切り出すトークンが見つからないとき true を返します。
next
template<class String>
String tokenizer<String>::next() {
range_type range = next_range();
return str_.substr(range.first, range.second);
}
切り出されたトークンを返します。
next_range
template<class String>
tokenizer<String>::range_type tokenizer<String>::next_range() {
skip();
String::size_type start = cur_;
String::size_type tmp = cur_;
if ( cur_ != String::npos ) cur_ = str_.find_first_of(del_,cur_);
if ( cur_ == String::npos ) return range_type(start,str_.length()-start);
if ( ret_ && start == cur_ && del_.find(str_[cur_]) != String::npos ) ++cur_;
return range_type(start,cur_-start);
}
トークンを切り出し、その位置と長さを表す std::pair<String::size_type,String::size_type> range を返します。range.first はトークンの開始位置、range.second はトークンの長さです。
set_str
template<class String>
void tokenizer<String>::set_str(const String& str) {
str_ = str;
cur_ = 0;
}
トークン分割対象となる文字列を設定します。トークン分割の開始位置を0に設定します。
set_delimiter
template<class String>
void tokenizer<String>::set_delimiter(const String& delim, bool ret) {
del_ = delim;
ret_ = ret;
}
区切り文字の集合を設定します。区切り文字をトークンとして扱うときは第2引数を true としてください。
count
template<class String>
size_t tokenizer<String>::count() const {
size_t count = 0;
String::size_type currpos = cur_;
while ( currpos != String::npos ) {
if ( !ret_ ) {
currpos = str_.find_first_not_of(del_,currpos);
if ( currpos == String::npos ) { ++count; break; }
} else if ( currpos == str_.length() ) {
break;
}
String::size_type start = currpos;
if ( currpos != String::npos ) currpos = str_.find_first_of(del_,currpos);
if ( currpos == String::npos ) { ++count; break; }
if ( ret_ && start == currpos && del_.find(str_[currpos]) != String::npos ) ++currpos;
++count;
}
return count;
}
現在の位置から切り出されるトークンの数を返します。
string_tokenizer / wstring_tokenizer
typedef tokenizer<std::string> string_tokenizer; typedef tokenizer<std::wstring> wstring_tokenizer;
使用頻度の多いtokenizer<std::string> および tokenizer<std::wstring>をそれぞれ string_tokenizer,wstring_tokenizerとtypedefしました。
stx::builtin_array<T>
‘builtin_array’について
STLが提供するアルゴリズムや関数オブジェクトはvectorやlistに対してだけでなく、char[]やint[]などのビルトイン配列に対しても適用できるように作られています。
しかしながらビルトイン配列をSTLに適用させるときは、たとえば、
// int data[N]の中から1を探す
int data[N];
int* iter = std::find(data, data+N, 1);
if ( iter != data+N ) {
// found!!
}
のように記述することになります。これをSTLコンテナと同様、
// vector<int> dataの中から1を探す
vector<int> data;
vector<int>::iterator iter = std::find(data.begin(), data,end(), 1);
if ( iter != data.end() ) {
// found!!
}
のように記述できるよう、ビルトイン配列をwrapする’builtin_array’を作りました。要素のアクセスに関する基本操作だけでなく、要素の反転アクセスのためのreverse_iterator/const_reverse_iteratorをサポートしています。
また、builtin_arrayの比較演算(==, !=, < <=, >, >=)を用意しました。したがってbuiltin_arrayはinsertやeraseなどの要素の挿入/削除に関する操作を持たないことを除いて、vectorと同じ操作ができます。
‘builtin_array’を使えば、前述のコードは以下のように記述できます。
// int data[N]の中から1を探す
int data[N];
stx::builtin_array<int> ba(data,N);
stx::builtin_array<int>::iterator iter = std::find(ba.begin(), ba.end(), 1);
if ( iter != ba.end() ) {
// found!!
}
ソースコード
/*
* builtin_array for VC++6
*/
#include <iterator>
#include <algorithm>
#include <stdexcept>
namespace stx {
template<class T>
class builtin_array {
T* first_;
T* last_;
size_t size_;
// no-definition to prevent copy
builtin_array();
builtin_array(const builtin_array&);
builtin_array& operator=(const builtin_array&);
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T* iterator;
typedef const T* const_iterator;
typedef std::reverse_iterator<iterator,T> reverse_iterator;
typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
builtin_array(T* first, T* last)
: first_(first), last_(last), size_(last-first) {}
builtin_array(T* first, size_t size)
: first_(first), last_(first+size), size_(size) {}
iterator begin()
{ return first_; }
const_iterator begin() const
{ return first_; }
iterator end()
{ return last_; }
const_iterator end() const
{ return last_; }
reverse_iterator rbegin()
{ return reverse_iterator(last_); }
const_reverse_iterator rbegin() const
{ return const_reverse_iterator(last_); }
reverse_iterator rend()
{ return reverse_iterator(first_); }
const_reverse_iterator rend() const
{ return const_reverse_iterator(first_); }
size_type size() const
{ return size_; }
size_type max_size() const
{ return size_; }
size_type capacity() const
{ return size_; }
bool empty() const
{ return size_ == 0; }
reference operator[](size_type n)
{ return *(first_+n); }
const_reference operator[](size_type n) const
{ return *(first_+n); }
reference at(size_type n)
{ if ( size_ <= n ) _Xran(); return *(first_+n); }
const_reference at(size_type n) const
{ if ( size_ <= n ) _Xran(); return *(first_+n); }
reference front()
{ return *first_; }
const_reference front() const
{ return *first_; }
reference back()
{ return *(last_-1); }
const_reference back() const
{ return *(last_-1); }
pointer body()
{ return first_; }
const_pointer body() const
{ return first_; }
std::pair<pointer,size_type> data()
{ return std::pair<pointer,size_type>(first_,size_); }
std::pair<const_pointer,size_type> data() const
{ return std::pair<const_pointer,size_type>(first_,size_); }
void swap(builtin_array& x) {
std::swap(first_, x.first_);
std::swap(last_, x.last_);
std::swap(size_, x.size_);
}
friend void swap(builtin_array& x, builtin_array& y)
{ x.swap(y); }
// internal use internary
bool _Eq(const builtin_array& x) const
{ return size() == x.size() && std::equal(begin(), end(), x.begin()); }
bool _Lt(const builtin_array& _X) const
{ return std::lexicographical_compare(begin(), end(), x.begin(), x.end()); }
private:
void _Xran () const
{ throw std::out_of_range("invalid builtin_array<T> subscript"); }
};
template<class T> inline
bool operator==(const builtin_array<T>& x, const builtin_array<T>& y)
{ return x._Eq(y); }
template<class T> inline
bool operator!=(const builtin_array<T>& x, const builtin_array<T>& y)
{ return !(_X == _Y); }
template<class T> inline
bool operator<(const builtin_array<T>& x, const builtin_array<T>& y)
{ return x._Lt(y); }
template<class T> inline
bool operator>(const builtin_array<T>& x, const builtin_array<T>& y)
{ return y < x; }
template<class T> inline
bool operator<=(const builtin_array<T>& x, const builtin_array<T>& y)
{ return !(y < x); }
template<class T> inline
bool operator>=(const builtin_array<T>& x, const builtin_array<T>& y)
{ return !(x < y); }
}
コンストラクタ
builtin_array(T* first, T* last) builtin_array(T* first, size_t size)
ビルトイン配列 first からbuiltin_arrayを構築します。
// sample int data[N]; builtin_array<int> ba(data,data+N); // または ba(data,N) int n = ba.size(); // n = N
※安全を期するため、デフォルトコンストラクタ/コピーコンストラクタ/コピー演算子はprivate部に宣言することで使えなくしてあります。
イテレータ
iterator begin(); const_iterator begin() const;
最初の要素を指すイテレータを返します。
iterator end(); const_iterator end() const;
最後の要素の直後を指すイテレータを返します。
// sample : 全要素をプリント
int data[N];
stx::builtin_array<int> ba(data,N);
stx::builtin_array<int>::iterator iter = ba.begin();
while ( iter != ba.end() ) {
cout << *iter << endl;
++iter;
}
reverse_iterator rbegin(); const_reverse_iterator rbegin() const;
最後の要素の直後を指す反転イテレータを返します。
reverse_iterator rend(); const_reverse_iterator rend() const;
最初の要素を指す反転イテレータを返します。
// sample : 全要素を逆順にプリント
int data[N];
stx::builtin_array<int> ba(data,N);
stx::builtin_array<int>::reverse_iterator iter = ba.rbegin();
while ( iter != ba.rend() ) {
cout << *iter << endl;
++iter;
}
empty / size / max_size
bool empty() const;
要素数が 0 なら true を返します。
size_type size() const; size_type max_size() const; size_type capacity() const;
いずれもコンストラクト時に設定した要素数を返します。
front / back / operator[] / at / data / swap
reference front() const_reference front() const
最初の要素を返します。
reference back() const_reference back() const
最後の要素を返します。
reference operator[](size_type n) const_reference operator[](size_type n) const
n番目の要素を返します。
reference at(size_type n) const_reference at(size_type n) const
n番目の要素を返します。n >= size() であるとき、out_of_range例外をthrowします。
std::pair<pointer,size_type> data() std::pair<const_pointer,size_type> data() const
配列のポインタと要素数(first()とsize())のpairを返します。
void swap(builtin_array& x)
*this と x の全要素を交換します。
[Java入門]3.5 判断と繰り返し
3.5.1 if と while
無事に実行できたのはいいのですが、お客が一組しか来ないのでは寂し過ぎますね。 またスタンドのタンクの用量が決まっているのにそれより多くのガソリンがあることにできてしまったり、タンクにあるガソリンの残量よりたくさん給油できてしまたりするのも困りものです。 ちゃんとタンクの容量と残量を考慮するようにプログラムを改良しましょう。 そしてお客さんが何台も来るようにします。 まず、コンストラクタも含めてそのメソッドでどんなことを考慮しなくてはいけないかを整理してみます。
| メソッド | どんなことを考慮するか |
|---|---|
| コンストラクタ | タンク容量より多くのガソリンを入れておくことはできない |
refuel |
ガソリンの残量より多く給油することはできない |
また、タンクが空になるまで給油を繰り返すことにしましょう。 タンクが空になったかどうかはガソリンスタンド自身に確認させます。 つまり、タンクが空かどうかを調べるisNotEmptyメソッドをGasStationクラスに追加することにします。
さて、問題は給油の繰り返しですが、これは GasStationSimulationクラスのdealCustomerを書き換えればよさそうです。 なにはともあれ、これらを考慮に入れるとそれぞれのメソッドがどうなるか見てみましょう。
- Java: GasStation.java の変更箇所
-
public GasStation(int initial) { if ( initial > tankCapacity_ ) { remain_ = tankCapacity_; } else { remain_ = initial; } } public int refuel(int amount) { int charged = amount; if ( remain_ < amount ) { charged = remain_; } remain_ = remain_ - charged; return charged; } // isNotEmpty メソッドを追加 public boolean isNotEmpty() { return remain_ > 0; } - Java: GasStationSimulation.java の変更箇所
-
public void dealCustomer() { while ( aGS_.isNotEmpty() ) { System.out.println("\nFor the cusomer:"); int amount = askInt(" refuel amount? "); int chargedAmount = aGS_.refuel(amount); int price = aGS_.price(chargedAmount); aGS_.addSales(price); System.out.println(" amount: " + chargedAmount + " [liter]"); System.out.println(" price: " + price + " [yen]"); } System.out.println("\nTotal sales: " + aGS_.totalSales() + " [yen]"); }
■
- C++: GasStation.h の変更箇所
-
class GasStation { private: static const int tankCapacity_; static int unitPrice_; int remain_; int totalSales_; public: explicit GasStation(int initial); int refuel(int amount); int price(int amount) const; void addSales(int price); bool isNotEmpty() const; // isNotEmpty メソッドを追加 static int tankCapacity(); int totalSales() const; static void setUnitPrice(int price); static int unitPrice(); }; - C++: GasStation.cpp の変更箇所
-
GasStation::GasStation(int initial) { if ( initial < tankCapacity_ ) { remain_ = tankCapacity_; } else { remain_ = initial; } } int GasStation::refuel(int amount) { int charged = amount; if ( remain_ < amount ) { charged = remain_; } remain_ = remain_ - charged; return charged; } bool GasStation::isNotEmpty() const { return remain_ > 0; } - C++: GasStationSimulation.cpp の変更箇所
-
void GasStationSimulation::dealCustomer() { while ( aGS_->isNotEmpty() ) { std::cout << "\nFor the customer:" << std::endl; int amount = askInt(" refuel amount? "); int chargedAmount = aGS_->refuel(amount); int price = aGS_->price(chargedAmount); aGS_->addSales(price); std::cout << " amount: " << chargedAmount << " [liter]" << std::endl; std::cout << " price: " << price << " [yen]" << std::endl; } std::cout << "\nTotal sales: " << aGS_->totalSales() << " [yen]" << std::endl; }
- C#: GasStation.cs の変更箇所
-
public GasStation(int initial) { if ( initial > tankCapacity_ ) { remain_ = tankCapacity_; } else { remain_ = initial; } } public int refuel(int amount) { int charged = amount; if ( remain_ < amount ) { charged = remain_; } remain_ = remain_ - charged; return charged; } // isNotEmpty メソッドを追加 public bool isNotEmpty() { return remain_ > 0; } - C#: GasStationSimulation.cs の変更箇所
-
public void dealCustomer() { while ( aGS_.isNotEmpty() ) { System.Console.WriteLine("\nFor the customer:"); int amount = askInt(" refuel amount? "); int chargedAmount = aGS_.refuel(amount); int price = aGS_.price(chargedAmount); aGS_.addSales(price); System.Console.WriteLine(" amount: " + chargedAmount + " [liter]"); System.Console.WriteLine(" price: " + price + " [yen]"); } System.Console.WriteLine("\nTotal sales: " + aGS_.totalSales + " [yen]"); }
- VB: GasStation.vb の変更箇所
-
Public Sub New(ByVal initial As Integer) If initial > tankCapacity_ Then remain_ = tankCapacity_ Else remain_ = initial End If End Sub Public Function refuel(ByVal amount As Integer) As Integer Dim charged As Integer = amount If remain_ < amount Then charged = remain_ End If remain_ = remain_ - charged Return charged End Function ' isNotEmpty メソッドの追加 Public Function isNotEmpty() As Boolean Return remain_ > 0 End Function - VB: GasStationSimulation.vb の変更箇所
-
Public Sub dealCustomer() While aGS_.isNotEmpty() System.Console.WriteLine(vbCrLf + "For the customer:") Dim amount As Integer = askInt(" refuel amount? ") Dim chargedAmount As Integer = aGS_.refuel(amount) Dim price As Integer = aGS_.price(chargedAmount) aGS_.addSales(price) System.Console.WriteLine(" amount: " + CStr(chargedAmount) + " [liter]") System.Console.WriteLine(" price: " + CStr(price) + " [yen]") End While System.Console.WriteLine(vbCrLf + "Total sales: " + CStr(aGS_.totalSales) + " [yen]") End Sub
□
if, else, while の3つの単語がキーワードです。 if は条件を判断し、その結果に応じて何をするか換えたいときに使います。refuelメソッドの中では、給油しようとした量がガソリンの残量を超えているときには、タンクが空になるまで、すなわち残量全部しか給油できないようにしています。 これに対してコンストラクタの方では if と else を組み合わせて使い、もし(if)最初に指定された量がタンク容量を越えていたらタンク容量ぎりぎりまで、そうでなければ(else)指定された量だけタンクに入れるということをしています。 最後に、dealCustomerメソッドのwhileですが、これはまず始めにガソリンスタンドのタンク残量があるかどうかを調べ、タンクが空でない間(while)次のお客さんを受け付けるという風になっています。 そしてタンクが空になったら繰り返しを終了して、売上の合計を表示します。 実際に変更してどのように動作するか試してみてください。
また、何度も繰り返すと画面が見にくくなってしまうので、繰り返すごとに一行あけるようにしています。 売上の合計を表示するときにも一行あけています。 それぞれ、ソース中のどこがそれにあたるかを調べてみてください。 このソースは$chapter3/ex3にあります。
これを反映したプログラムを実行した結果は以下のようになります。
For the gas station:
initial amount? 250
unit price? 108
For the customer:
refuel amount? 35
amount: 35 [liter]
price: 3780 [yen]
For the customer:
refuel amount? 40
amount: 40 [liter]
price: 4320 [yen]
For the customer:
refuel amount? 38
amount: 38 [liter]
price: 4104 [yen]
For the customer:
refuel amount? 42
amount: 42 [liter]
price: 4536 [yen]
For the customer:
refuel amount? 36
amount: 36 [liter]
price: 3888 [yen]
For the customer:
refuel amount? 45
amount: 45 [liter]
price: 4860 [yen]
For the customer:
refuel amount? 52
amount: 14 [liter]
price: 1512 [yen]
Total sales: 27000 [yen]
3.5.2 回数を決めて繰り返す 1: while
さて、この例では最初にタンクに用意した量を全部売り尽くすまで給油を繰り返すので、結局売上の合計はタンクに最初に用意した量とガソリンの単価だけで決まってしまいます。 これではシミュレーションらしくないので、給油する代数を決めて、それ以上は給油しないことにしましょう。 このための代表的な方法は、次の2つがあります。
whileを使って、変数を1ずつ増やし、一定数に達したら繰り返しをやめる。forを使い、1と同様にする。
それぞれの方法でやるとどうなるか、ソースを比べてみましょう。
とりあえず、5台だけ給油することにします。 もちろん、askIntメソッドを使えば、何台給油するかを最初に入力するようにもできます。 是非試してみてください。
まず、whileを使った場合のソースを次に示します。
このソースは$chapter3/ex4/GasStationSimulation-while.javaに格納されています。
- Java: GasStationSimulation-while.java
-
import java.io.BufferedReader; import java.io.InputStreamReader; class GasStationSimulation { private GasStation aGS_; private int askInt(String message) { try { System.out.print(message); System.out.flush(); String line = new BufferedReader(new InputStreamReader(System.in)).readLine(); return Integer.parseInt(line); } catch ( java.io.IOException e ) { System.out.println("could not get input. Sorry."); return 0; } } public void init() { System.out.println("For the gas station:"); int initial = askInt(" initial amount? "); int price = askInt(" unit price? "); GasStation.setUnitPrice (price); aGS_ = new GasStation(initial); } public void dealCustomer() { int i = 0; while ( aGS_.isNotEmpty() && i < 5 ) { System.out.println("\nFor the cusomer:"); int amount = askInt(" refuel amount? "); int chargedAmount = aGS_.refuel(amount); int price = aGS_.price(chargedAmount); aGS_.addSales(price); System.out.println(" amount: " + chargedAmount + " [liter]"); System.out.println(" price: " + price + " [yen]"); i = i + 1; } System.out.println("\nTotal sales: " + aGS_.totalSales() + " [yen]"); } public static void main(String[] args) { GasStationSimulation simulation = new GasStationSimulation(); simulation.init(); simulation.dealCustomer(); } }
■
- C++: GasStationSimulation-while.cpp
-
#include <iostream> #include "GasStationSimulation.h" #include "GasStation.h" GasStationSimulation::~GasStationSimulation() { delete aGS_; } int GasStationSimulation::askInt(const std::string& message) { std::cout << message << std::flush; int result; if ( std::cin >> result ) { return result; } std::cout << "could not get input. sorry." << std::endl; return 0; } void GasStationSimulation::init() { std::cout << "For the gas station:" << std::endl; int initial = askInt(" initial amount? "); aGS_ = new GasStation(initial); int unitPrice = askInt(" unit price? "); GasStation::setUnitPrice(unitPrice); } void GasStationSimulation::dealCustomer() { int i = 0; while ( aGS_->isNotEmpty() && i < 5 ) { std::cout << "\nFor the customer:" << std::endl; int amount = askInt(" refuel amount? "); int chargedAmount = aGS_->refuel(amount); int price = aGS_->price(chargedAmount); aGS_->addSales(price); std::cout << " amount: " << chargedAmount << " [liter]" << std::endl; std::cout << " price: " << price << " [yen]" << std::endl; i = i + 1; } std::cout << "\nTotal sales: " << aGS_->totalSales() << " [yen]" << std::endl; }
- C#: GasStationSimulation-while.cs
-
class GasStationSimulation { private GasStation aGS_; private int askInt(string message) { System.Console.Write(message); System.Console.Out.Flush(); try { return System.Int32.Parse(System.Console.ReadLine()); } catch ( System.Exception ) { System.Console.WriteLine("could not get input. sorry."); return 0; } } public void init() { System.Console.WriteLine("For the gas stations:"); int initial = askInt(" initial amount? "); aGS_ = new GasStation(initial); GasStation.unitPrice = askInt(" unit price? "); } public void dealCustomer() { int i = 0; while ( aGS_.isNotEmpty() && i < 5 ) { System.Console.WriteLine("\nFor the customer:"); int amount = askInt(" refuel amount? "); int chargedAmount = aGS_.refuel(amount); int price = aGS_.price(chargedAmount); aGS_.addSales(price); System.Console.WriteLine(" amount: " + chargedAmount + " [liter]"); System.Console.WriteLine(" price: " + price + " [yen]"); i = i + 1; } System.Console.WriteLine("\nTotal sales: " + aGS_.totalSales + " [yen]"); } [System.STAThread] static void Main(string[] args) { GasStationSimulation simulation = new GasStationSimulation(); simulation.init(); simulation.dealCustomer(); } }
- VB: GasStationSimulation-while.vb
-
Class GasStationSimulation Private aGS_ As GasStation Private Function askInt(ByVal message As String) System.Console.Write(message) System.Console.Out.Flush() Try Return System.Int32.Parse(System.Console.ReadLine()) Catch e As System.Exception System.Console.WriteLine("could not get input. Sorry.") Return 0 End Try End Function Public Sub init() System.Console.WriteLine("For the customer:") Dim initial As Integer = askInt(" initial amount? ") GasStation.unitPrice = askInt(" unit price? ") aGS_ = New GasStation(initial) End Sub Public Sub dealCustomer() Dim i As Integer = 0 While aGS_.isNotEmpty() And i < 5 System.Console.WriteLine(vbCrLf + "For the customer:") Dim amount As Integer = askInt(" refuel amount? ") Dim chargedAmount As Integer = aGS_.refuel(amount) Dim price As Integer = aGS_.price(chargedAmount) aGS_.addSales(price) System.Console.WriteLine(" amount: " + CStr(chargedAmount) + " [liter]") System.Console.WriteLine(" price: " + CStr(price) + " [yen]") i = i + 1 End While System.Console.WriteLine(vbCrLf + "Total sales: " + CStr(aGS_.totalSales) + " [yen]") End Sub Public Shared Sub Main() Dim simulation As GasStationSimulation = New GasStationSimulation() simulation.init() simulation.dealCustomer() End Sub End Class
□
このdealCustomerメソッドでは、ローカル変数 i を用意してその値を1ずつ増やしています。 そして、whileの条件に i < 5 を追加して、5台に給油したかどうかを調べています。
&& という演算子は、2つ以上の条件を並べて書き、両方が成立しているときだけ成立する、いわゆる論理演算子です。
このプログラムを実行すると以下のように出力されます。
For the gas station:
initial amount? 250
unit price? 108
For the customer:
refuel amount? 42
amount: 42 [liter]
price: 4536 [yen]
For the customer:
refuel amount? 38
amount: 38 [liter]
price: 4104 [yen]
For the customer:
refuel amount? 45
amount: 45 [liter]
price: 4860 [yen]
For the customer:
refuel amount? 41
amount: 41 [liter]
price: 4428 [yen]
For the customer:
refuel amount? 36
amount: 36 [liter]
price: 3888 [yen]
Total sales: 21816 [yen]
こんどはタンクが空にならなくても5回で実行を終了していることがわかります。
3.5.3 回数を決めて繰り返す 2: for
次に、for を使ったもうひとつのやり方を見てみましょう。
次のリストを参照してください。 ソースは$chapter3/ex4/GasStationSimulation-for.javaに格納されています。
- Java: GasStationSimulation-for.java
-
import java.io.BufferedReader; import java.io.InputStreamReader; class GasStationSimulation { private GasStation aGS_; private int askInt(String message) { try { System.out.print(message); System.out.flush(); String line = new BufferedReader(new InputStreamReader(System.in)).readLine(); return Integer.parseInt(line); } catch ( java.io.IOException e ) { System.out.println("could not get input. Sorry."); return 0; } } private void init() { System.out.println("For the gas station:"); int initial = askInt(" initial amount? "); int price = askInt(" unit price? "); GasStation.setUnitPrice (price); aGS_ = new GasStation(initial); } private void dealCustomer() { for ( int i = 0; aGS_.isNotEmpty() && i < 5; i = i + 1 ) { System.out.println("\nFor the cusomer:"); int amount = askInt(" refuel amount? "); int chargedAmount = aGS_.refuel(amount); int price = aGS_.price(chargedAmount); aGS_.addSales(price); System.out.println(" amount: " + chargedAmount + " [liter]"); System.out.println(" price: " + price + " [yen]"); } System.out.println("\nTotal sales: " + aGS_.totalSales() + " [yen]"); } public static void main(String[] args) { GasStationSimulation simulation = new GasStationSimulation(); simulation.init(); simulation.dealCustomer(); } }
■
- C++: GasStationSimulation-for.cpp
-
#include <iostream> #include "GasStationSimulation.h" #include "GasStation.h" GasStationSimulation::~GasStationSimulation() { delete aGS_; } int GasStationSimulation::askInt(const std::string& message) { std::cout << message << std::flush; int result; if ( std::cin >> result ) { return result; } std::cout << "could not get input. sorry." << std::endl; return 0; } void GasStationSimulation::init() { std::cout << "For the gas station:" << std::endl; int initial = askInt(" initial amount? "); aGS_ = new GasStation(initial); int unitPrice = askInt(" unit price? "); GasStation::setUnitPrice(unitPrice); } void GasStationSimulation::dealCustomer() { for ( int i = 0; aGS_->isNotEmpty() && i < 5; i = i + 1 ) { std::cout << "\nFor the customer:" << std::endl; int amount = askInt(" refuel amount? "); int chargedAmount = aGS_->refuel(amount); int price = aGS_->price(chargedAmount); aGS_->addSales(price); std::cout << " amount: " << chargedAmount << " [liter]" << std::endl; std::cout << " price: " << price << " [yen]" << std::endl; } std::cout << "\nTotal sales: " << aGS_->totalSales() << " [yen]" << std::endl; }
- C#: GasStationSimulation-for.cs>
-
class GasStationSimulation { private GasStation aGS_; private int askInt(string message) { System.Console.Write(message); System.Console.Out.Flush(); try { return System.Int32.Parse(System.Console.ReadLine()); } catch ( System.Exception ) { System.Console.WriteLine("could not get input. sorry."); return 0; } } public void init() { System.Console.WriteLine("For the gas stations:"); int initial = askInt(" initial amount? "); aGS_ = new GasStation(initial); GasStation.unitPrice = askInt(" unit price? "); } public void dealCustomer() { for ( int i = 0; aGS_.isNotEmpty() && i < 5; i = i + 1 ) { System.Console.WriteLine("\nFor the customer:"); int amount = askInt(" refuel amount? "); int chargedAmount = aGS_.refuel(amount); int price = aGS_.price(chargedAmount); aGS_.addSales(price); System.Console.WriteLine(" amount: " + chargedAmount + " [liter]"); System.Console.WriteLine(" price: " + price + " [yen]"); } System.Console.WriteLine("\nTotal sales: " + aGS_.totalSales + " [yen]"); } [System.STAThread] static void Main(string[] args) { GasStationSimulation simulation = new GasStationSimulation(); simulation.init(); simulation.dealCustomer(); } }
- VB: GasStationSimulation-for.vb
-
Class GasStationSimulation Private aGS_ As GasStation Private Function askInt(ByVal message As String) System.Console.Write(message) System.Console.Out.Flush() Try Return System.Int32.Parse(System.Console.ReadLine()) Catch e As System.Exception System.Console.WriteLine("could not get input. Sorry.") Return 0 End Try End Function Public Sub init() System.Console.WriteLine("For the customer:") Dim initial As Integer = askInt(" initial amount? ") GasStation.unitPrice = askInt(" unit price? ") aGS_ = New GasStation(initial) End Sub Public Sub dealCustomer() Dim i As Integer For i = 1 To 5 If Not aGS_.isNotEmpty() Then Exit For End If System.Console.WriteLine(vbCrLf + "For the customer:") Dim amount As Integer = askInt(" refuel amount? ") Dim chargedAmount As Integer = aGS_.refuel(amount) Dim price As Integer = aGS_.price(chargedAmount) aGS_.addSales(price) System.Console.WriteLine(" amount: " + CStr(chargedAmount) + " [liter]") System.Console.WriteLine(" price: " + CStr(price) + " [yen]") Next System.Console.WriteLine(vbCrLf + "Total sales: " + CStr(aGS_.totalSales) + " [yen]") End Sub Public Shared Sub Main() Dim simulation As GasStationSimulation = New GasStationSimulation() simulation.init() simulation.dealCustomer() End Sub End Class
□
今度はfor の後の括弧の中に、変数iの定義や、タンクが空かどうか、5台済んだかどうか、iに1を足すといったことがまとめて書かれています。 この例からわかるように、forを使って書けることはwhileを使っても書くことができます。 どちらも非常によく使われるので覚えておきましょう。
whileを用いた場合と同じ出力が得られるか、実際に試して比べてみてください。
slist<T, Alloc> リファレンス
※ slist<T,Alloc>はSGI版STLが提供するコンテナクラスであり、標準C++ライブラリには含まれていません。
しかしながらこのSGI版STLは大元のHPのリファレンス実装の直系で、STLportなど様々なSTL実装のベースとなっていることから、事実上の標準といっても差支えないものです。gccもこのSGI版STL(もしくはSTLport)を採用しています。
slistは単方向リンクリストです。slist内の各要素は次の要素へのリンクを持っていますが、双方向リンクリスト:std::listのように前の要素へのリンクを持ってはいません。
したがって、(std::listのイテレータが双方向イテレータであるのに対し)slistのイテレータは ‘前方向イテレータ’ であり、イテレータの指す位置を遡ることはできません(operator–()を持っていません)。
また、前の要素へのリンクを持たないことから、std::listにある末尾に対する参照/追加/削除メソッド back / push_back / pop_back をサポートしていません。
slistはstd::listに比べて機能的には劣ります。が、双方向イテレータを必要としない用途であれば、管理しなければならないリンクが単方向で済むため、std::listより若干ながらコンパクトかつ高速です。
標準C++が定めるコンテナの要求を満たすため、slistにはstd::listと同等のメソッドが実装されています。
しかしながら前の要素へのリンクを持たないため、inssertやeraseはstd::listよりかなり低速(要素数に比例)であることに留意してください。代替メソッドとして、定数時間で処理を行なうinsert_after, erase_afterが用意されています。 できる限りこれらをお使いください。
サンプル
#include <slist>
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
slist<int> L;
L.push_front(0);
L.push_front(1);
L.insert_after(L.begin(), 2);
copy(L.begin(), L.end(), // 出力: 1 2 0
ostream_iterator<int>(cout, " "));
cout << endl;
slist<int>::iterator back = L.previous(L.end());
back = L.insert_after(back, 3);
back = L.insert_after(back, 4);
back = L.insert_after(back, 5);
copy(L.begin(), L.end(), // 出力: 1 2 0 3 4 5
ostream_iterator<int>(cout, " "));
cout << endl;
return 0;
}
<slist>
/*
* このヘッダは STLport-4.5-01119 を基に作成されたものであり、
* SGIオリジナル(後述のメソッド一覧)と完全には一致していません
*/
template <class _Tp, _STLP_DEFAULT_ALLOCATOR_SELECT(_Tp) >
class slist : protected _Slist_base<_Tp,_Alloc>
{
public:
typedef _Tp value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef forward_iterator_tag _Iterator_category;
typedef _Slist_iterator<_Tp, _Nonconst_traits<_Tp> > iterator;
typedef _Slist_iterator<_Tp, _Const_traits<_Tp> > const_iterator;
_STLP_FORCE_ALLOCATORS(_Tp, _Alloc)
typedef typename _Base::allocator_type allocator_type;
public:
allocator_type get_allocator() const;
explicit slist(const allocator_type& __a = allocator_type());
slist(size_type __n, const value_type& __x,
const allocator_type& __a = allocator_type());
explicit slist(size_type __n);
template <class _InputIterator>
slist(_InputIterator __first, _InputIterator __last,
const allocator_type& __a _STLP_ALLOCATOR_TYPE_DFL);
slist(const _Self& __x);
_Self& operator= (const _Self& __x);
~slist() {}
void assign(size_type __n, const _Tp& __val);
template <class _InputIterator>
void assign(_InputIterator __first, _InputIterator __last);
iterator before_begin();
const_iterator before_begin() const;
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
size_type size() const;
size_type max_size() const;
bool empty() const;
void swap(_Self& __x);
reference front();
const_reference front() const;
void push_front();
void pop_front();
iterator previous(const_iterator __pos);
const_iterator previous(const_iterator __pos) const;
iterator insert_after(iterator __pos, const value_type& __x);
iterator insert_after(iterator __pos);
void insert_after(iterator __pos, size_type __n, const value_type& __x);
template <class _InIter>
void insert_after(iterator __pos, _InIter __first, _InIter __last);
iterator insert(iterator __pos, const value_type& __x);
iterator insert(iterator __pos);
void insert(iterator __pos, size_type __n, const value_type& __x);
template <class _InIter>
void insert(iterator __pos, _InIter __first, _InIter __last);
iterator erase_after(iterator __pos);
iterator erase(iterator __pos);
iterator erase(iterator __first, iterator __last);
void resize(size_type new_size, const _Tp& __x);
void resize(size_type new_size);
void clear();
void splice_after(iterator __pos,
iterator __before_first, iterator __before_last);
void splice_after(iterator __pos, iterator __prev);
void splice_after(iterator __pos, _Self& __x);
void splice(iterator __pos, _Self& __x);
void splice(iterator __pos, _Self& __x, iterator __i);
void splice(iterator __pos, _Self& __x, iterator __first, iterator __last);
void reverse();
void remove(const _Tp& __val);
void unique();
void merge(_Self& __x);
void sort();
template <class _Predicate>
void remove_if(_Predicate __pred);
template <class _BinaryPredicate>
void unique(_BinaryPredicate __pred);
template <class _StrictWeakOrdering>
void merge(slist<_Tp,_Alloc>& __x, _StrictWeakOrdering __comp);
template <class _StrictWeakOrdering>
void sort(_StrictWeakOrdering __comp);
};
メソッド一覧
iterator begin() |
slistの先頭要素を指すiteratorを返します。 |
iterator end() |
slistの末尾要素(の次)を指すiteratorを返します。 |
const_iterator begin() const |
slistの先頭要素を指すconst_iteratorを返します。 |
const_iterator end() const |
slistの末尾要素(の次)を指すconst_iteratorを返します。 |
size_type size() const |
要素数が0であることを知るには L.size() == 0 ではなく L.empty() することをおすすめします。 |
size_type max_size() const |
slistに格納可能な(論理的な)最大要素数を返します。 |
bool empty() const |
slist内の要素数が 0 のとき trueを返します。 |
slist() |
空のslistを構築します。 |
slist(size_type n) |
n 個の T()を要素とするslistを構築します。 |
slist(size_type n, const T&t) |
n 個の tを要素とするslistを構築します。 |
slist(const slist&) |
コピー・コンストラクタ |
|
シーケンス [f, l) を要素とする slist を構築します。 |
~slist() |
デストラクタ |
slist& operator=(const slist&) |
代入演算子。 |
void swap(slist&) |
お互い全要素を交換します。 |
reference front() |
先頭要素を返します。 |
const_reference front() const |
先頭要素を返します。 |
void push_front(const T&) |
要素を先頭に挿入します。 |
void pop_front() |
先頭要素を削除します。 |
iterator previous(iterator pos) |
++prev == pos なるprevを返します。 |
const_iterator previous(const_iterator pos) |
++prev == pos なるprevを返します。 |
iterator insert(iterator pos, const T& x) |
posが指す位置の直前にxを挿入します。pos. |
|
posが指す位置の直前にシーケンス[first, last)を挿入します。 |
void insert(iterator pos, |
posが指す位置の直前にn個のxを挿入します。 |
iterator erase(iterator pos) |
posが指す位置にある要素を削除します。 |
iterator erase(iterator first, iterator last) |
範囲[first, last)にある要素を削除します。 |
void clear() |
全要素を削除します。 |
void resize(n, t = T()) |
要素数がnとなるよう、要素の削除もしくはtの挿入を行ないます。 |
iterator insert_after(iterator pos) |
posが指す位置の直後にT()を挿入します。挿入された要素を指すiteartorを返します。 |
iterator insert_after(iterator pos, const value_type& x) |
挿入された要素を指す |
|
posが指す位置の直後にシーケンス[f, l)を挿入します。 |
void insert_after(iterator pos, size_type n, const value_type& x) |
posが指す位置の直後に n個のxを挿入します。 |
iterator erase_after(iterator pos) |
posが指す位置の直後の要素を削除します。 |
iterator erase_after(iterator before_first, iterator last) |
範囲[before_first+1, last)にある要素を削除します。 |
void splice(iterator position, slist& L) |
positionが指す位置の直前にLの全要素を移動します。 |
void splice(iterator position, slist& L, iterator i) |
positionが指す位置の直前に iが指すLの要素を移動します。 |
void splice(iterator position, slist& L, iterator f, iterator l) |
positionが指す位置の直前にL内のシーケンス[f, l) |
void splice_after(iterator pos, iterator prev) |
|
void splice_after(iterator pos, iterator before_first, iterator before_last) |
posが指す位置の直後にシーケンス[before_forst+1, before_last+1)を移動します。 |
void remove(const T& value) |
valueと等しい全要素を削除します。 |
|
p(*i) が真となる全要素 *i を削除します。 |
void unique() |
隣接する相等しい要素を削除します。 |
void merge(slist& L) |
ソートされた*thisとLとを*thisにマージします。 |
|
compによる大小関係に基づいてソートされた*thisとLとを*thisにマージします。 |
void reverse() |
全要素の順序を反転します。 |
void sort() |
全要素を昇順にソートします。 |
|
compによる大小関係に基づいて全要素を昇順にソートします。 |
bool operator==(const slist&, const slist&) |
ふたつのslistが等値であればtrueを返します。 |
bool operator<(const slist&, const slist&) |
ふたつslistの各要素を辞書順で比較します。 |
STL samples
:function
:struct
:class
<algorithm>
- 適用
for_each- 検索
find
find_if
find_end
find_first_of
adjacent_find
search
search_n
mismatch
lower_bound
upper_bound
equal_range
binary_search- 係数
count
count_if- 比較
equal
lexicographical_compare- 複写
copy
copy_backward- 交換
swap
swap_ranges
iter_swap- 変換
transform- 置換
replace
replace_if
replace_copy
replace_copy_if- 充填
fill
fill_n
generate
generate_n- 削除
remove
remove_if
remove_copy
remove_copy_if
unique
unique_copy- 反転
reverse
reverse_copy- 回転
rotate
rotate_copy- 攪拌
random_shuffle- 分類
partition
stable_partition
nth_element- 整列
sort
stable_sort
partial_sort
partial_sort_copy- 併合
merge
inplace_mearge- 集合
set_union
set_intersection
set_difference
set_symmetric_difference- 堆積
push_heap
pop_heap
make_heap
sort_heap- 最大/最小
min
max
min_element
max_element- 順列
next_permutatin
prev_permutation
<bitset>
- 固定長bit配列
bitset
<deque>
- 両頭キュー
deque
<functional>
- 関数オブジェクト base
unary_function
binary_function- 算術演算
plus
minus
multiplies
divides
modulus
negate- 比較
equal_to
not_equal_to
greater
less
greater_equal
less_equal- 論理演算
logical_and
logical_or
logical_not- 論理反転
unare_negate
not1
binary_negate
not2- バインダ
binder1st
bind1st
binder2nd
bind2nd- 関数ポインタ
pointer_to_unary_function
pointer_to_binary_function
ptr_fun- メンバ関数ポインタ
mem_fun_t
mem_fun1_t
mem_fun_ref_t
mem_fun1_ref_t
const_mem_fun_t
const_mem_fun1_t
const_mem_fun_ref_t
const_mem_fun1_ref_t
mem_fun
mem_fun_ref
<iterator>
- traits,tag
iterator_traits
input_iterator_tag
output_iterator_tag
forward_iterator_tag
bidirectional_iterator_tag
random_access_iterator_tag- iterator操作
advance
distance- 反転イテレータ
reverse_iterator- 挿入イテレータ
back_insert_iterator
back_inserter
front_insert_iterator
front_inserter
insert_iterator
inserter- ストリームイテレータ
istream_iterator
ostream_iterator
istreambuf_iterator
ostreambuf_iterator
<list>
- 双方向リスト
list
<map>
<memory>
- アロケータ
allocator- テンポラリ領域
raw_storage_iterator
get_temporary_buffer
return_temporary_buffer
uninitialized_copy
uninitialized_fill
uninitialized_fill_n- auto_ptr
auto_ptr
<numeric>
- 数値計算
accumulate
adjacent_difference
inner_product
partial_sum
<queue>
<set>
<stack>
- スタック
stack
<utility>
<vector>
- 可変長配列
vector
[Java入門]3.4 ガソリンスタンドのシミュレーション
ガソリンスタンドのシミュレーションを行なうには、これを実行してくれるオブジェクトを作らなくてはいけません。 このためのクラスGasStationSimulation を定義することにします。 その中で、コンストラクタを使ってガソリンスタンドのオブジェクトを作り、シミュレーションを実行するのです。
3.4.1 GasStationSimulation クラス
GasStationSimulationクラスのメソッドについて考えましょう。 まず、GasStationクラスのオブジェクトを作るメソッドが必要です。 これをinitメソッドとしましょう。 ガソリンスタンドを作る(GasStationクラスのコンストラクタを呼び出す) ためには、蓄えているガソリンの量をどうにかして決めなくてはいけませんね。 ガソリンの単価も必要です。 これらが決まればとりあえずガソリンスタンドを作ることができます。
オブジェクトができたなら、今度はそれを使うメソッドが必要です。 このメソッドでは、お客さんが来たときに給油量を聞いてガソリンを入れます。 さらに、代金を計算して受け取ります。 これをdealCustomerメソッドとしましょう。
ガソリンの単価や最初の備蓄量、毎回の給油量はどのようにして決めればよいでしょうか。 すぐに思いつくのはソースに直接書いてしまうことですが、これでは何回実行しても同じ結果になってしまうので、面白くありません。 ここはひとつ、キーボードから入力することにしましょう。 そのときには、ユーザーが何を入力すればいいのかがわかるような、メッセージを表示するといいですね。 また、必要な情報はすべて整数で表されていますから、整数だけが入力できればいいはずです。 メッセージを表示して数値を入力するメソッドを用意しましょう。 メッセージの内容は、引数で受け渡します。 このメソッドの名前は、askIntということにします。
ここまでのメソッドをまとめると、次のようになります。
| メソッド名 | 内容 |
|---|---|
init |
単価、ガソリンの初期量を決定してガソリンスタンドを作る |
dealCustomer |
給油量を聞いてガソリンを入れ、代金を計算して受け取る |
askInt |
メッセージを表示して整数を入力させ、結果の整数を返す |
さて、メソッドはこれでよさそうですので、変数のことも考えてみましょう。 一時的な変数は必要になったときに作ればいいので、ここではガソリンスタンドのシミュレーション全体に関わるような変数を考えます。
まず、用意したガソリンスタンドを、変数に入れておかないといけません。 他に何か必要になるでしょうか? 単価はガソリンスタンドに保存されていますし、給油量は毎回入力するので保存する必要はありません。 ガソリンスタンド以外の変数は、特に必要なさそうです。
では、ソースを書いてみることにしましょう。 まずクラスを作って、変数を定義します。
class GasStationSimulation {
private GasStation aGS_;
}
3.4.2 メソッドの中身
askIntメソッドは、ガソリンスタンドのオブジェクトを作るために必要な単価や給油量を決定するために使うので、まずこのメソッドから考えることにします。 さきほどまとめた表を基に概要を書いてみると、次のようになります。
private int askInt(String message) {
// メッセージを表示する
// 整数を入力してもらう
// 入力された整数を結果として返す
}
3つ仕事をする必要がありますが、なんだかどれも難しそうです。 でも、安心してください。 ちゃんとこういうことを行なうためのとても便利な方法が用意されているのです。 とにかく、どうすればできるのか、ソースを見てみましょう。
private int askInt(String message) {
try {
System.out.print(message);
System.out.flush();
String line = new BufferedReader(new InputStreamReader(System.in)).readLine();
return Integer.parseInt(line);
} catch ( java.io.IOException e ) {
System.out.println("could not get input. Sorry.");
return 0;
}
}
よくわからない書き方や単語がたくさんでています。 でも、これらの記述がどういう意味なのかは、今はわからなくてかまいません。 この本を読み終えるころにはちゃんと理解できるようになっているはずです。 今はただ、こう書けばメッセージを表示したり、キーボードからの入力を受け取ったり、それを整数の値とすることができると思ってください。
なお、これらのメソッドを利用するためには、BufferedReader, InputStreamReaderクラスを参照することをimport 文でコンパイラに知らせる必要がありますので、ソースの先頭に以下の記述を入れておきます。
import java.io.BufferedReader; import java.io.InputStreamReader;
残りの2つのメソッドがすべきこと自体はどちらも単純です。 今作ったaskIntメソッドを使って書いてみましょう。
public void init() {
System.out.println("For the gas station:");
int initial = askInt(" initial amount? ");
aGS_ = new GasStation(initial);
aGS_.setUnitPrice(askInt(" unit price? "));
}
public void dealCustomer() {
System.out.println("For the cusomer:");
int amount = askInt(" refuel amount? ");
int chargedAmount = aGS_.refuel(amount);
int price = aGS_.price(chargedAmount);
aGS_.addSales(price);
System.out.println(" amount: " + chargedAmount + " [liter]");
System.out.println(" price: " + price + " [yen]");
}
これらのメソッドの中で、initial, amount, chargedAmount, price という4つの変数を定義しています。 このように、メソッド内で作られた変数はローカル変数と呼ばれ、そのメソッドの中でしか使えません。 ここでは、入力された値や計算結果の整数を一時的に保持するために使われています。
new 演算子
initメソッドの最後から2行目に注目してください。
aGS_ = new GasStation(initial)
この行では、new演算子でGasStationクラスのコンストラクタを呼び出しています。 つまり、新しいガソリンスタンドのインスタンスを作り、aGS_という変数に代入しているのです。
そして、dealCustomer, initメソッドには、aGS_.refuel(amount)やaGS_.setUnitPrice(askInt(& unit price? &)といった記述が見られますが、これはaGS_という変数に保持されているガソリンスタンドオブジェクトのメソッドを呼び出しているところなのです。
メソッドsetUnitPriceはGasStationクラスのオブジェクトすべてに共通するメソッドなので、オブジェクトのかわりにクラス名を指定して GasStation.setUnitPrice(askInt(& unit price? &)とすることもできます。 通常、クラス全体で共通なメソッドを呼び出すときはこのようにクラス名を用いますので、これ以降の例ではこの方法をとることにしましょう。
3.4.3 すべては main メソッドから始まる
これでクラスの定義はほぼできましたが、ひとつ重要なことを忘れています。 GasStationクラスのインスタンスは、GasStationSimulationのインスタンスが作ります。 では、GasStationSimulationのインスタンスはいったい誰がどのように作ればいいのでしょう? 実はJavaではクラスごとに mainメソッドを呼ばれる特別なメソッドを用意することができます。
このメソッドは、インタープリタがそのクラスを実行するときに、最初に呼び出されることになっています。 つまり、javaコマンドで引数にクラスを指定して実行すると、そのクラスで定義されているmainメソッドがまず呼び出されます。 HelloWorldクラスを思い出してください。 HelloWorldクラスはmainメソッドだけでできていましたね。
さて、GasStationSimulationクラスにmianメソッドを定義しましょう。 ソースは次のようになります。
public static void main(String[] args) {
GasStationSimulation simulation = new GasStationSimulation();
simulation.init();
simulation.dealCustomer();
}
public static void main(String[] args)というのは決り文句だと思ってください。 また、mainメソッドにはargsという引数がひとつだけあります。 これは、javaコマンドを起動したとき、クラス名以降のコマンドライン引数が順に格納される文字列の配列です。 配列については後ほど 3.6.3 で実際に使う例が出きますので、ここでは説明しません。
メソッドの中身はどうなっているでしょうか。 まず、GasStationSimulationのコンストラクタを呼び出してインスタンスを作っています。 この引数をとらないコンストラクタは、ひとつもコンストラクタを定義しなかった場合に用意されるデフォルトのコンストラクタです。 そして、作ったGasStationSimulationオブジェクトを変数 simulationに代入し、initメソッドとdealCustomerメソッドを順に呼び出しています。
3.4.4 GasStationSimulation クラスのソース
これでGasStationSimulationクラスの定義が出来上がったので、ファイル全体がどうなるか一度見てみることにします。 クラスごとにファイルを分けた方がわかりやすいので、GasStationSimulation.javaとしましょう。 ソースは、$chapter3/ex2/GasStationSimulation.javaに格納されています。
- Java: GasStationSimulation.java
-
import java.io.BufferedReader; import java.io.InputStreamReader; class GasStationSimulation { private GasStation aGS_; private int askInt(String message) { try { System.out.print(message); System.out.flush(); String line = new BufferedReader(new InputStreamReader(System.in)).readLine(); return Integer.parseInt(line); } catch ( java.io.IOException e ) { System.out.println("could not get input. Sorry."); return 0; } } public void init() { System.out.println("For the gas station:"); int initial = askInt(" initial amount? "); GasStation.setUnitPrice(askInt(" unit price? ")); aGS_ = new GasStation(initial); } public void dealCustomer() { System.out.println("For the cusomer:"); int amount = askInt(" refuel amount? "); int chargedAmount = aGS_.refuel(amount); int price = aGS_.price(chargedAmount); aGS_.addSales(price); System.out.println(" amount: " + chargedAmount + " [liter]"); System.out.println(" price: " + price + " [yen]"); } public static void main(String[] args) { GasStationSimulation simulation = new GasStationSimulation(); simulation.init(); simulation.dealCustomer(); } }
■
- C++: GasStationSimulation.h
-
#ifndef GASSTATIONSIMULATION_H__ #define GASSTATIONSIMULATION_H__ #include <string> #include "GasStation.h" class GasStationSimulation { private: GasStation* aGS_; int askInt(const std::string& message); public: ~GasStationSimulation(); void init(); void dealCustomer(); }; #endif - C++: GasStationSimulation.cpp
-
#include <iostream> #include "GasStationSimulation.h" #include "GasStation.h" GasStationSimulation::~GasStationSimulation() { delete aGS_; } int GasStationSimulation::askInt(const std::string& message) { std::cout << message << std::flush; int result; if ( std::cin >> result ) { return result; } std::cout << "could not get input. sorry." << std::endl; return 0; } void GasStationSimulation::init() { std::cout << "For the gas station:" << std::endl; int initial = askInt(" initial amount? "); aGS_ = new GasStation(initial); int unitPrice = askInt(" unit price? "); GasStation::setUnitPrice(unitPrice); } void GasStationSimulation::dealCustomer() { std::cout << "For the customer:" << std::endl; int amount = askInt(" refuel amount? "); int chargedAmount = aGS_->refuel(amount); int price = aGS_->price(chargedAmount); aGS_->addSales(price); std::cout << " amount: " << chargedAmount << " [liter]" << std::endl; std::cout << " price: " << price << " [yen]" << std::endl; } - C++: main.cpp
-
#include "GasStationSimulation.h" int main() { GasStationSimulation simulation; simulation.init(); simulation.dealCustomer(); return 0; }
- C#: GasStationSimulation.cs
-
class GasStationSimulation { private GasStation aGS_; private int askInt(string message) { System.Console.Write(message); System.Console.Out.Flush(); try { return System.Int32.Parse(System.Console.ReadLine()); } catch ( System.Exception ) { System.Console.WriteLine("could not get input. sorry."); return 0; } } public void init() { System.Console.WriteLine("For the gas stations:"); int initial = askInt(" initial amount? "); aGS_ = new GasStation(initial); GasStation.unitPrice = askInt(" unit price? "); } private void dealCustomer() { System.Console.WriteLine("For the customer:"); int amount = askInt(" refuel amount? "); int chargedAmount = aGS_.refuel(amount); int price = aGS_.price(chargedAmount); aGS_.addSales(price); System.Console.WriteLine(" amount: " + chargedAmount + " [liter]"); System.Console.WriteLine(" price: " + price + " [yen]"); } [System.STAThread] static void Main(string[] args) { GasStationSimulation simulation = new GasStationSimulation(); simulation.init(); simulation.dealCustomer(); } }
- VB: GasStationSimulation.vb
-
Class GasStationSimulation Private aGS_ As GasStation Private Function askInt(ByVal message As String) System.Console.Write(message) System.Console.Out.Flush() Try Return System.Int32.Parse(System.Console.ReadLine()) Catch e As System.Exception System.Console.WriteLine("could not get input. Sorry.") Return 0 End Try End Function Public Sub init() System.Console.WriteLine("For the customer:") Dim initial As Integer = askInt(" initial amount? ") GasStation.unitPrice = askInt(" unit price? ") aGS_ = New GasStation(initial) End Sub Public Sub dealCustomer() System.Console.WriteLine("For the customer:") Dim amount As Integer = askInt(" refuel amount? ") Dim chargedAmount As Integer = aGS_.refuel(amount) Dim price As Integer = aGS_.price(chargedAmount) aGS_.addSales(price) System.Console.WriteLine(" amount: " + CStr(chargedAmount) + " [liter]") System.Console.WriteLine(" price: " + CStr(price) + " [yen]") End Sub Public Shared Sub Main() Dim simulation As GasStationSimulation = New GasStationSimulation() simulation.init() simulation.dealCustomer() End Sub End Class
□
3.4.5 コンパイルと実行
ソースが用意できたらコンパイルしてみてください。 コマンドラインから次の下線部の通りに入力して、javacの引数に今作った2つのファイルを指定します。
% javac GasStation.java GasStationSimulation.java
正しく入力されていれば何もメッセージが出力されずにコンパイルが終了します。 もし、エラーメッセージが出力された場合は、もう一度ソースを見直して間違いを修正してください。
コンパイルが終了したら、クラスファイルができていることを確認してください。 クラスファイルができていたら javaコマンドを使って実行しましょう。 実行するには、クラスファイルを作成したディレクトリが、環境変数$CLASSPATHに含まれていなくてはいけません。 現在の作業ディレクトリをあらわす&.&を含めておきましょう。
コマンドラインから次の下線部のように入力して、GasStationSimulationクラスを実行します。
% java GasStationSimulation
メッセージが表示されて、整数の入力待ちになるので、?の後に数字を入力して改行キーを押します。
% java GasStationSimulation For the gas station: initial amount? 500 unit price? 120 For the cusomer: refuel amount? 30 amount: 30 [liter] price: 3600 [yen]
ちゃんと実行できたでしょうか? 何度か試してみて、代金の計算が正しいことを確認してください。