はじめに
あなたがC++開発者で、最近Rubyプログラミング言語に関するあらゆる情報が気になっているとしたら、この記事はまさにあなたにうってつけです。ここでは、C++とRubyの重要な違いを概説し、それぞれの言語で実装された小さいながらも完全なサンプルアプリケーションを紹介します。
ただし、Rubyを覚えることがストレスになる可能性があることは、あらかじめご了承ください。この強力かつシンプルな言語に慣れてしまったら、C++に戻ることが辛くなるかもしれません。
高レベル言語の比較と実行サンプル
C++は静的に型付けされたコンパイル言語であり、ハイブリッド型オブジェクト指向の性質を備えています。「静的に型付けされた」とは、すべての式と変数の型がコンパイル時にわかっており、プログラムの実行前に重要な正確性チェックを行えることを意味します。「ハイブリッド型オブジェクト指向」とは、この言語がオブジェクトでないプリミティブ型(int型やfloat型など)を定義していることと、オブジェクトの外に関数を記述できることを意味しています。
Rubyというプログラミング言語は、コードをすばやく簡単に書けるようにすることを目指して考案されたものです。C++とは異なり、きわめて動的なインタプリタ型言語で、一連の強力なライブラリが含まれます。一般的にはスクリプト言語と呼ばれていますが、これは純粋なオブジェクト指向の言語であり、汎用アプリケーションに対応するための表現力を十分に備えています。
Rubyでは、変数を宣言する必要がなく、各ステートメントで変数の型を自由に変更できます。したがって、たとえば次のコードでは変数x
の型をFixNum(ネイティブのマシンワードに収まる長さの整数)からString、そしてArrayに変更していますが、これはRubyコードの完全に正当なシーケンスです。
x = 10 x += 4 x = "My String" x = [1, "My String", Hash.new ]
Rubyの重大な弱点は、インタプリタを使用することです。Rubyの実行時のパフォーマンスは、C++のようなコンパイル言語とは比較になりません。したがって、どんなにRubyの機能が気に入ったとしても、実行時の効率性が必要なのであれば、C++を使い続けた方がいいでしょう。
C++とRubyの重要な違いを理解したところで、それぞれの言語で実装された、小さいながらも完全なサンプルアプリケーションを見てみることにしましょう。このサンプルアプリケーションでは、指定ディレクトリ内の一連のファイルにおける各ワードの出現総数を計算し、出現ワード数のまとめを示すXMLファイルを生成して出力します。C++での実装をリスト、Rubyでの実装をリスト2に示します。サンプルコードのダウンロード用ファイルもご利用ください。
// for directory manipulation #include <unistd.h> #include <sys/types.h> #include <dirent.h> // streams and strings #include <iostream> #include <fstream> #include <string> // STL collections #include <map> #include <set> struct my_compare { bool operator() (const std::pair<std::string, int> &f1, const std::pair<std::string, int> &f2) { return f1.second < f2.second; } }; struct word_count { std::string word; int total_count; typedef std::multiset< std::pair<std::string, int>, my_compare > file_set_t; word_count(std::string _word) : word(_word), total_count(0) {} virtual void add(std::string filename) { total_count += 1; } virtual file_set_t file_occurrences() { return file_set_t(); } }; bool operator< (const std::pair<std::string, word_count *> &wc1, const std::pair<std::string, word_count *> &wc2) { return wc1.second->total_count < wc2.second->total_count; } bool operator< (const word_count &wc1, const word_count &wc2) { return wc1.total_count < wc2.total_count; } struct word_and_file_count : public word_count { std::map<std::string,int> file_count; word_and_file_count(std::string _word) : word_count(_word) {} void add(std::string filename) { if (file_count.find(filename) == file_count.end()) file_count[filename] = 0; file_count[filename] += 1; total_count += 1; } word_count::file_set_t file_occurrences() { return word_count::file_set_t ( file_count.begin(), file_count.end() ); } }; template <typename W> class word_counter { private: std::map<std::string, word_count *> word_counts; const std::string directory_name; void count_words_in_file(const std::string &filename) { int i = 0; std::ifstream file(filename.c_str()); if ( file.is_open() ) { while (file.good()) { std::string line; getline(file,line); char c = line[i=0]; while ( c != '\0' ) { std::string buffer; while ( c != '\0' && !isalpha(c) ) { c = line[++i]; } while ( c != '\0' && isalpha(c) ) { buffer += c; c = line[++i]; } if (buffer.length()) { if (word_counts.find(buffer) == word_counts.end()) word_counts[buffer] = new W(buffer); word_counts[buffer]->add(filename); } } } } } public: word_counter(const std::string &_directory_name) : directory_name(_directory_name) {} void count_words() { char *cwd = getcwd(NULL,0); if (chdir(directory_name.c_str())) { std::cerr << "Could not open directory" << std::endl; exit(1); } DIR *d = opendir("."); if (!d) { std::cerr << "Could not open directory" << std::endl; exit(1); } while (struct dirent *e = readdir(d)) { std::string filename = e->d_name; count_words_in_file(filename); } chdir(cwd); delete cwd; } void dump_results() { typedef std::multiset< std::pair< std::string, word_count * > > wc_set_t; std::cout << "<counts>" << std::endl; wc_set_t wc_set(word_counts.begin(), word_counts.end() ); for (wc_set_t::const_reverse_iterator it = wc_set.rbegin(); it != wc_set.rend(); it++) { std::cout << "<word occurences=\"" << it->second->total_count << "\">" << std::endl << it->second->word << std::endl; word_count::file_set_t file_set = it->second->file_occurrences(); for (word_count::file_set_t::const_reverse_iterator fit = file_set.rbegin(); fit != file_set.rend(); fit++) { std::cout << "<file occurences=\"" << fit->second << "\">" << fit->first << "</file>" << std::endl; } std::cout << "</word>" << std::endl; delete it->second; } std::cout << "</counts>" << std::endl; } void run() { count_words(); dump_results(); } }; int main(int argc, char *argv[]) { char *dir = argv[1]; if (!strcmp(argv[1],"--no-file-info")) { word_counter<word_count>(argv[2]).run(); } else { word_counter<word_and_file_count>(argv[1]).run(); } return 0; }