実装関連事項

各種プログラミング言語の基本的な書き方やソフトウェア等の使用方法について.

コンパイラのインストール

ルート権限がない状況においてコンパイラ GCC をインストールするには以下のようにする.最初に GCC のインストールに必要な GMP,MPFR,MPC と GCC の本体をそれぞれ以下のページからダウンロードし,それらを $HOME/build 等の適当なディレクトリに入れる.インストールは $HOME/local にする.

GMP を以下のようにインストールする.インストールは $HOME/local にする.

$ tar xf gmp-6.0.0a.tar.bz2
$ pushd gmp-6.0.0
$ ./configure --prefix=$HOME/local
$ make
$ make install
$ popd

次に,MPFR をインストールする.インストールは同じく $HOME/local にする.

$ tar xf mpfr-3.1.2.tar.gz
$ pushd mpfr-3.1.2
$ CFLAGS=-I$HOME/local/include LDFLAGS=-L$HOME/local/lib ./configure --prefix=$HOME/local
$ make
$ make install
$ popd

次に,MPC をインストールする.インストールは同じく $HOME/local にする.

$ tar xf mpc-1.0.2.tar.gz
$ pushd mpc-1.0.2
$ CFLAGS=-I$HOME/local/include LDFLAGS=-L$HOME/local/lib ./configure --prefix=$HOME/local
$ make
$ make install
$ popd

次に,GCC をインストールする.インストールは同じく $HOME/local にする.

$ tar xf gcc-4.9.2.tar.gz
$ pushd gcc-4.9.2
$ CFLAGS=-I$HOME/local/include LDFLAGS=-L$HOME/local/lib LC_ALL=C ./configure --prefix=$HOME/local --with-gmp=$HOME/local --with-mpc=$HOME/local --with-mpfr=$HOME/local --disable-multilib
$ make
$ make install
$ popd

最後に,bashを使っている場合は~/.bashrcに以下の2行を追加する.

PATH=$HOME/local/bin:$PATH
export LD_LIBRARY_PATH=$HOME/local/lib:$HOME/local/lib64

次回ログイン時,または $HOME/.bashrc を更新後から gcc を使用できる.ソースコード sample.cc は以下のようにコンパイルできる.オプション -o で生成するプログラムの名前を指定できる.

$ g++ sample.cc -o sample

Boostのインストール

ライブラリ boost には様々な機能が備わっており,有用である.インストールは以下のように行う.最初に,ソースコードを任意の場所にダウンロードする.

$ wget http://sourceforge.net/projects/boost/files/boost/1.59.0/boost_1_59_0.tar.gz

ビルド用の適当なディレクトリを作成し,そこにソースコードの入ったファイルを移動させ解凍する.

$ mkdir $HOME/build
$ mv boost_1_59_0.tar.gz $HOME/build
$ cd $HOME/build
$ tar zxvf boost_1_59_0.tar.gz

解凍して出てきたディレクトリに移動し,最後に,以下のコマンドでビルドおよびインストールする.この場合,boost は $HOME/local にインストールされる.

$ cd boost_1_59_0
$ ./bootstrap.sh --prefix=$HOME/local
$ ./b2 install -j4 --prefix=$HOME/local

ライブラリを使う際には,ソースコードのヘッダ部分でライブラリをインクルードする.

#include <boost/...>
using namespace std;

int main()
{
	...
}

以上のコードを sample.cc として保存する.それをコンパイルするには以下のようにする.

g++ -I$HOME/local/include sample.cc

または,以下のようにしてもコンパイルできる.

CPLUS_INCLUDE_PATH=$HOME/local/include/:$CPLUS_INCLUDE_PATH g++ sample.cc

コマンドライン引数の取得

C++ でコマンドライン引数を取得するためには "int main()" の括弧内に以下のような記述をする.argv[1] には1番目の引数,argc には引数の個数よりひとつ大きい数値が格納される.

#include <iostream>
using std::cout;
using std::endl;

int main(int argc,char *argv[])
{
	cout<<argv[1]<<endl;
	cout<<argv<<endl;
}

以上のコードを sample.cc として保存し,それをコンパイルしてプログラム sample を生成し,以下のように実行する.

$ sample foo bar baz

これを実行したときの結果は以下のようになる.

foo
4

ファイルの入出力

ファイル some.txt を開いてその内容を一行ずつ表示させるには以下のように書く.

#include <string>
#include <iostream>
#include <fstream>
using std::cout;
using std::endl;
 
int main()
{
	ifstream fin;
	fin.open("some.txt");
	
	string line;
	while(getline(fin,line))
	{
		cout<<line<<endl;
	}
	
	fin.close();
}

ファイル名の指定に文字列を用いる場合は以下のように書く.

#include <string>
#include <iostream>
#include <fstream>
using std::cout;
using std::endl;
 
int main()
{
	string fileName="some.txt";
	ifstream fin;
	fin.open(fileName.c_str());
	
	string line;
	while(getline(fin,line))
	{
		cout<<line<<endl;
	}
	
	fin.close();
}

ファイル some.dat に出力する場合は以下のように書く.

#include <string>
#include <iostream>
#include <fstream>
using std::cout;
using std::endl;
 
int main()
{
	string fileName="some.dat";
	ofstream fout;
	fout.open(fileName.c_str());
	
	fout<<"hoge"<<endl;
	
	fout.close();
}

以下のようにも書ける.

#include <string>
#include <iostream>
#include <fstream>
using std::cout;
using std::endl;
 
int main()
{
	string fileName="some.dat";
	ofstream fout(fileName.c_str());
	
	fout<<"hoge"<<endl;
	
	fout.close();
}

外部プログラム等の実行

外部コマンドやプログラム等を呼び出したいときは system() を用いるのが便利である.

#include <iostream>
using std::cout;
using std::endl;
 
int main()
{
	system("ls");
}

一方で,外部コマンドやプログラム等を実行させて,さらにその結果を処理したい場合は以下のように popen() を利用する.以下のソースコードは,ファイル some.fa の中において行の先頭に > を持つ行を抜き出してそれを標準出力に表示させるプログラムを生成する.

#include <string>
#include <iostream>
using std::cout;
using std::endl;
 
int main()
{
	string fileName="some.fa";
	string command; // external command
	command=command.erase()+"egrep \"^>\" "+fileName;
	
	FILE* result;
	char buf[1024];
	result=popen(command.c_str(),"r");
	while(fgets(buf,sizeof(buf),result)!=NULL)
	{
		cout<<buf; // processing results
	}
	pclose(result);
}

文字列の初期化

ある文字列 (string filename) を複数の string 等を連結させて初期化したいときは以下のように書く.

#include <string>
using std::cout;
using std::endl;

int main()
{
	string ent="1abc";
	
	string filename;
	fileName=fileName.erase()+"work/result/"+ent+".pdb";
}

文字列処理

文字列の正規表現マッチの判定をしたい場合,C++11 以降は標準ライブラリで正規表現を使うことができる.それ以前の場合,POSIX 準拠の標準ライブラリに regex.h があり,これを利用することができる.

#include <iostream>
#include <regex.h>
using std::cout;
using std::endl;

int main()
{
    string line1="   1   4.3";
    string line2="   A   4.3";
    string line3="       0.3";
    const char regex[]="^[[:blank:]]+[0-9][^.]";
    regex_t buffer;
    regcomp(&buffer,regex,REG_EXTENDED|REG_NEWLINE);
    regmatch_t patternMatch[1];
    int size=sizeof(patternMatch)/sizeof(regmatch_t);
    if(regexec(&buffer,line1.c_str(),size,patternMatch,0)==0)
    {
        cout<<"line 1: Match suru."<<endl;
    }
    if(regexec(&buffer,line2.c_str(),size,patternMatch,0)!=0)
    {
        cout<<"line 2: Match shinai."<<endl;
    }
    if(regexec(&buffer,line3.c_str(),size,patternMatch,0)!=0)
    {
        cout<<"line 3: Match shinai."<<endl;
    }
    regfree(buffer);
}

文字列を指定した文字で分割したい場合は strtok() を用いる.

#include <iostream>
#include <cstdlib>
#include <regex.h>
#include <cstring>
using std::cout;
using std::endl;

int main()
{
	string line="The quick brown fox jumps over the lazy dog.";
	char *cline=(char*)malloc(sizeof(char)*(line.length()+1));
	strcpy(cline,line.c_str());
	char *tok;
	char del[]=" ";
	tok=strtok(cline,del);
	while(tok!=NULL)
	{
		cout<<tok<<endl;
		tok=strtok(NULL,del);
	}
}

配列の取り扱い

1次元配列を関数に渡したい場合は以下のようにする.

#include <string>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;


void change(string strseq,char *cseq);
 
int main()
{
	string strseq="ARNDCQEGHI";
	char cseq[strseq.length()];
	change(strseq,cseq);
	
	for(unsigned int i=0;i<strseq.length();i++)
	{
		cout<<cseq[i];
	}
	cout<<endl;
}
 
void change(string strseq,char *cseq)
{
	for(unsigned int i=0;i<strseq.length();i++)
	{
		cseq[i]=strseq.c_str()[i];
	}
}

2次元配列の場合は以下のようにする.

#include <string>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;

void change(string strseq,char (*cseq)[3]);

int main()
{
	string strseq="AAAAGAGAGG";
	char cseq[strseq.length()][3];
	change(strseq,cseq);
	
	for(unsigned int i=0;i<strseq.length();i++)
	{
		cout<<cseq[i];
	}
	cout<<endl;
}

void change(string strseq,char (*cseq)[3])
{
	for(unsigned int i=0;i<strseq.length();i++)
	{
		if(strseq[i]=='A')
		{
			cseq[i][0]='6';
			cseq[i][1]='5';
			cseq[i][2]='\0';
		}
		else if(strseq[i]=='G')
		{
			cseq[i][0]='7';
			cseq[i][1]='1';
			cseq[i][2]='\0';
		}
	}
}

2次元配列の要素数を知りたい場合は以下のようにする.2次元配列を X とした場合において,全要素数と各要素のサイズの積は sizeof(X) で計算でき,それを各要素のサイズ sizeof(X[0][0]) を割ることで求まる.さらに,全要素数と各要素数のサイズを各列の要素数と要素のサイズの積は sizeof(seq[0]) で計算できるため,これで sizeof(X) を割ると,行の数が計算できる.同様に列数も計算できる.

#include <string>
using std::cout;
using std::endl;

int main()
{
	int seq[3][8];
	
	int anum=sizeof(seq)/sizeof(seq[0][0]);
	int rnum=sizeof(seq)/sizeof(seq[0]);
	int cnum=sizeof(seq[0])/sizeof(seq[0][0]); // same as anum/rnum
}

配列はベクターを用いて表現する.C++11 では配列を "{,}" によって一気に初期化できる.内容の読み出しは,以下のように for を用いると簡単にできる.要素の追加は push_back() で可能となる.

#include <string>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;

int main()
{
	vector<string> test={"foo","bar","baz"};
	
	for(auto i=0;i<test.size();i++)
	{
		cout<<test[i]<<" ";
	}
	cout<<endl;
	
	test.push_back("hoge");
	
	for(auto i=test.begin();i!=test.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

リファレンス

ポインタがアドレスを格納することを前提とした変数であるのに対し,リファレンスはある変数の純粋たる別名,すなわちただ単に名前だけ変化するが本質的には同じものとして理解できる.

#include <iostream>
using std::cout;
using std::endl;

void multiple(int& num);

int main()
{
	int num1=100;
	cout<<num1<<endl; /* 100 */
	multiple(num1);
	cout<<num1<<endl; /* 500 */
}
 
void multiple(int& num)
{
	num=5*num;
}

構造体

C では構造体は以下のように定義する.

#include <iostream>
using std::cout;
using std::endl;

typedef struct{
	int base;
	int exponent;
}powerdata;

int main()
{
	powerdata kappa;
	kappa.base=425;
	kappa.exponent=-16;
	
	cout<<kappa.base<<endl;
}

C++ では以下のように書くことで構造体を生成することができるようになった.

#include <iostream>
using std::cout;
using std::endl;

struct powerdata
{
	int base;
	int exponent;
};

int main()
{
	powerdata kappa;
	kappa.base=425;
	kappa.exponent=-16;
	
	cout<<kappa.base<<endl;
}

行列の掛け算

行列 $a$ = $\pmatrix{1&2\\3&4\\5&6}$ と 行 列$b$ = $\pmatrix{1&2&3&4&5\\6&7&8&9&0}$ の掛け算を計算したいときは以下のように for を3回繰り返す.

int main()
{
	int a[3][2]=
	{
		{1,2},
		{3,4},
		{5,6}
	};
	
	int b[2][5]=
	{
		{1,2,3,4,5},
		{6,7,8,9,0}
	};
	
	int **m=(int**)malloc(sizeof(int*)*3);
	for(int i=0;i<3;i++)
	{
		m[i]=(int*)malloc(sizeof(int)*5);
	}
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<5;j++)
		{
			m[i][j]=0;
		}
	}
	
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<5;j++)
		{
			for(int t=0;t<2;t++)
			{
				m[i][j]+=a[i][t]*b[t][j];
			}
		}
	}
	
	for(int i=0;i<3;i++)
	{
		free(m[i]);
	}
	free(m);
}
Hatena Google+