静かな環境を手に入れよう。
echo "set bell-style none" >> ~/.inputrc echo "set visualbell t_vb=" >> ~/.vimrc
静かな環境を手に入れよう。
echo "set bell-style none" >> ~/.inputrc echo "set visualbell t_vb=" >> ~/.vimrc
つれづれに追加していきます。
指定した文字列をファイル名に含むファイルを検索する。
find -name "文字列" .
指定した文字列を内容に含むファイルを検索する。
grep "文字列" -rl .
テンポラリファイルを作成せずに、2つの生成した標準出力を比較する。
diff <(command1 ) <(command2)
重複する行を取り除く。
cat ${file} | sort | uniq
重複する行の数を出力する(CSV形式)。
cat ${file} | sort | uniq -c | sed "s/^\s\+//" | sed "s/\s\+/,/
特定文字で区切ったフィールドを抜き出す。
cat ${file} | cut -d ',' -f 3,4,8
GitリポジトリのHEADが示すコミットのSHAを出力する
git rev-parse HEAD
CSVで指定されたパラメータセットに応じてコードを自動生成するスクリプト。
パラメータの取得はPython標準CSVパーサを利用。コード生成はヒアドキュメントに変数を埋め込むことで実現している。
#! /usr/bin/env python import sys import csv #----------------------------------------- # Load config.csv #----------------------------------------- args = sys.argv with open(args[1]) as f: for row in csv.reader(f): if not row[0].startswith('#'): config = row __P0 = int(config[0].replace(' ', '')) __P1 = int(config[1].replace(' ', '')) __P2 = int(config[2].replace(' ', '')) #----------------------------------------- # Generate param.h #----------------------------------------- str=''' #pragma once #define PARAM0 {__p0} #define PARAM1 {__p1} #define PARAM2 {__p2} const float A[PARAM0 * PARAM1] = {{ #include "./data/{__p0}_{__p1}.csv" }} const float B[PARAM1 * PARAM2] = {{ #include "./data/{__p1}_{__p2}.csv" }} float Y[PARAM0 * PARAM2]; '''.format(__p0=str(__P0), __p1=str(__P1), __p2=str(__P2)).strip() print(str)
パラメータ設定ファイル例
# p0, p1, p2 5, 3, 4
Pythonのヒアドキュメントは'''と'''で囲んで作成する。
上記の記述例では、開始の'''の直後と、終了の'''の直前に改行が挿入されるため、stripにて前後の改行を取り除く。
ヒアドキュメント内の変数展開はformatメソッドを使用する。ヒアドキュメント内で{変数名}として展開する変数を記述する。なお、本来の{および}は変数と区別するため、{{、}}と書き換える。
ランダムなテストデータを生成する関数randomizeArrayを作成した。
具体的には、指定した区間([min, max))の一様分布にて配列をランダム化する。
疑似乱数であるstd::mt19937_64を使っているため同一環境での再現性あり。
ただし、一様分布生成std::uniform_real_distributionの実装が環境依存であるため、異なる環境での再現性は保証されない。
コンパイルにはC++11オプションの指定が必要。
#include <iostream> #include <fstream> #include <random> template<typename T> void randomizeArray ( T *X, const int n, T max = 1.0, T min = -1.0, int seed = 0 ) { static std::mt19937_64 mt(seed); std::uniform_real_distribution<> dist(min, max); for (int i=0; i<n; ++i) { X[i] = static_cast<T>(dist(mt)); } } template<typename T> void printMat ( T *X, const int m, const int n, std::ostream& os = std::cout ) { for (int i = 0; i < m-1; ++i) { for (int j = 0; j < n; ++j) { os << X[i*n+j] << ","; } os << std::endl; } for (int j = 0; j < n-1; ++j) { os << X[(m-1)*n + j] << ","; } os << X[m*n-1] << std::endl; } int main(void) { int M = 5; int K = 3; int N = 4; float *A = new float[M*K]; float *B = new float[K*N]; randomizeArray<float>(A, M*K, 100, -100); randomizeArray<float>(B, K*N, 100, -100); std::cout << "# float A(" << M << "," << K << ")" << std::endl; printMat<float>(A, M, K); std::cout << "# float B(" << K << "," << N << ")" << std::endl; printMat<float>(B, K, N); int *A_ = new int[M*K]; int *B_ = new int[K*N]; std::cout << "# int A_(" << M << "," << K << ")" << std::endl; randomizeArray<int>(A_, M*K, 100, -100); std::cout << "# int B_(" << K << "," << N << ")" << std::endl; randomizeArray<int>(B_, K*N, 100, -100); printMat<int>(A_, M, K); printMat<int>(B_, K, N); return 0; }
出力は以下の通り。
# float A(5,3) -68.0413,98.429,-92.0862, 19.4989,8.45699,-88.568, 26.3057,-15.2859,68.4521, 81.2603,-15.3704,31.1052, 86.0862,-31.1229,-49.077 # float B(3,4) -11.0658,58.0379,39.0841,11.5361, -42.5139,-70.8368,15.8528,12.9099, -70.7164,-69.9669,-96.0132,10.8815 # int A_(5,3) -68,98,-92, 19,8,-88, 26,-15,68, 81,-15,31, 86,-31,-49 # int B_(3,4) -11,58,39,11, -42,-70,15,12, -70,-69,-96,10
std::mt19937_64にstatic修飾子を付加することで、randomizeArrayのコール毎に異なる値が出力される。配列A、Bで異なる乱数列が生成されている。
ただし、テンプレート引数が異なる場合、内部で同じ乱数列が生成されるため、同じ数値を異なる型でキャストされた値が出力される。配列AとA、BとBはそれぞれ同じ乱数列をfloat、intでキャストした値が生成されている。
16進数内部表現で与えられたfloatやdoubleの変数値を入力として、floatやdoubleに復元するコードを考えた。
入力となる16進数内部表現には、先頭に”0x”または"0X"が付加されていてもよい。
x86_64にて動作確認。処理系依存のコードになっているため、使用時には注意が必要。
#include <iostream> float hex2float(const std::string& hex) { long hex_l = std::stol(hex, nullptr, 16); float *hex_pf = reinterpret_cast<float*>(&hex_l); return *hex_pf; } double hex2double(const std::string& hex) { long long hex_ll = std::stoll(hex, nullptr, 16); double *hex_pd = reinterpret_cast<double*>(&hex_ll); return *hex_pd; } int main(void) { std::string fstr1 = "40490E56"; float f1 = hex2float(fstr1); std::cout << "float, " << fstr1 << " = " << f1 << std::endl; std::string fstr2 = "0x40490E56"; float f2 = hex2float(fstr2); std::cout << "float, " << fstr2 << " = " << f2 << std::endl; std::string dstr1 = "400921CAC083126F"; double d1 = hex2double(dstr1); std::cout << "double, " << dstr1 << " = " << d1 << std::endl; std::string dstr2 = "0x400921CAC083126F"; double d2 = hex2double(dstr2); std::cout << "double, " << dstr2 << " = " << d2 << std::endl; return 0; }
実行結果は以下の通り
float, 40490E56 = 3.1415 float, 0x40490E56 = 3.1415 double, 400921CAC083126F = 3.1415 double, 0x400921CAC083126F = 3.1415
SystemCでは、ユーザ定義型(クラス)をチャネルのテンプレート引数として使用できる。
ユーザ定義型には、①比較(==)、②標準出力(<<)、③sc_traceの3つの関数が必要とされる。
特に、①はコンパイルエラー回避のため必須である。
②、③は無くてもよいが、デバッグ容易化のために追加が望ましい。
以下では、位置を示す2変数を保持するユーザ定義型pointを例に、①、②を実装した。
まずは、ユーザ定義型のヘッダファイル。
oprerator<<はメンバ関数ではなく、friend関数とする。
#pragma once #include <iostream> class point { private: int x; int y; public: point(int x, int y); ~point(void); bool operator==(const point& obj) const; friend std::ostream& operator<<(std::ostream& str, const point& obj); };
次に、ユーザ定義型の実装ファイル。
#include "userdef.hpp" point::point(int x, int y) : x(x), y(y) { }; point::~point(void) { }; bool point::operator==(const point& obj) const { return ((this->x == obj.x) && (this->y == obj.y)); }; std::ostream& operator<<(std::ostream& str, const point& obj) { str << obj.x << "," << obj.y; return str; };
最後に、メイン関数からの呼び出し。
#include <iostream> #include "userdef.hpp" int main(void) { point p1( 10, 20 ); point p2( 11, 20 ); std::cout << p1 << std::endl; std::cout << p2 << std::endl; std::cout << (p1 == p2) << std::endl; return 0; };
実行結果は以下の通り。
10,20 11,20 0
次回は、久しぶりにSystemC環境を構築してみる。