erl-creole: ユニコード文字列とマルチバイト列の変換ライブラリ

少し必要になったのでErlangユニコード文字列と各種エンコーディングのマルチバイト列(バイナリ)の相互変換を行うモジュールを作成した。
github: erl-creole-0.0.1


現状、対応しているエンコーディングは以下の通り*1:

使用例

%% 入力文字列
> S = "Unicode (ユニコード) とは、世界中の多くのコンピュータ上の文字列を一貫した方法で符号化し、表現し、扱うためのコンピュータ業界の標準である。".

%% EUC-JPに変換
> creole:from_string(S, eucjp).
<<"Unicode (\&#230;\&#203;\&#179;&#161;&#188;\&#201;) &#164;&#200;&#164;&#207;&#161;¢&#192;&#164;&#179;釗&#195;&#230;&#164;&#206;&#194;&#191;&#164;&#175;&#164;&#206;\&#179;\&#243;\&#212;\&#229;&#161;&#188;\&#191;&#190;&#229;&#164;&#206;&#202;&#184;&#187;&#250;&#206;&#243;&#164;&#242;°&#236;´&#211;&#164;&#183;&#164;&#191;&#202;&#253;&#203;&#161;&#164;&#199;&#201;&#228;&#185;&#230;&#178;&#189;&#164;&#183;&#161;¢&#201;&#189;&#184;&#189;&#164;&#183;&#161;¢°&#183;&#164;釗&#164;&#191;&#164;&#225;&#164;&#206;\&#179;\&#243;\&#212;\&#229;&#161;"...>>

%% JIS(ISO-2022-JP)に変換
> Bin = creole:from_string(S, jis).
<<"Unicode (\e$B%f%K%3!<%I\e(B) \e$B$H$O!\"@$3&Cf$NB?$/$N%3%s%T%e!<%?>e$NJ8;zNs$r0l4S$7$?J}K!$GId9f2=$7!\"I=8=$7!\"07$&$?$a$N"...>>

%% バイト列からユニコード文字列に変換
> creole:to_string(Bin, jis).
[85,110,105,99,111,100,101,32,40,12518,12491,12467,12540,
 12489,41,32,12392,12399,12289,19990,30028,20013,12398,22810,
 12367,12398,12467,12531,12500|...]

> io:format("~ts~n", [creole:to_string(Bin, jis)]).
Unicode (ユニコード) とは、世界中の多くのコンピュータ上の文字列を一貫した方法で符号化し、表現し、扱うためのコンピュータ業界の標準である。
ok

%% 変換不能なバイト列がある場合は、デフォルトでは "?" が代わりに使用される
> io:format("~ts~n", [creole:to_string(<<"Unicode (\e$B%~^s^sjaf*(asf7aK%3!<%I">>, jis)]).
Unicode (??潁潁裃罟?癈羞疔コード
ok

%% "?"の代わりに"_"を使用
> io:format("~ts~n", [creole:to_string(<<"Unicode (\e$B%~^s^sjaf*(asf7aK%3!<%I">>, jis, creole:replace($_))]).
Unicode (__潁潁裃罟_癈羞疔コード
ok

*1:ユニコードと他のエンコーディングのコードポイントの対応は主に http://source.icu-project.org/repos/icu/data/trunk/charset/data/ucm/ を参考にさせてもらった。

文字列/バイト列用のハッシュ関数ライブラリ

A Hash Function for Hash Table Lookupに載っているハッシュ関数(Jenkins Hash)Common Lispで実装した。
github: jenkins-hash(0.0.2)


作成の主な動機は以下の二つ:

おそらくSBCL以外の処理系でも動くと思うけど、動作は未確認。

使用例

以下、使用例。

;;;; SBCL-1.0.51-64bit

;;; 【文字列】
;; 文字列からハッシュ値を算出
(jenkins-hash:hash-string "ハッシュ関数")
--> 3188986421   ; 一つのキーに対して二つのハッシュ値(32bit)を返す
    2167986557

;; パラメータ(seed1,seed2)を替えて別のハッシュ値を算出
(jenkins-hash:hash-string "ハッシュ関数" :seed1 13 :seed2 44444)
--> 2402597428
    3323692532

;; 範囲指定
(jenkins-hash:hash-string "ハッシュ関数" :start 2 :end 4)
--> 58741211
    888923469

;;; 【バイト列】
;; バイト列からハッシュ値を算出
(jenkins-hash:hash-octets (sb-ext:string-to-octets "ハッシュ関数"))
--> 1523938354
    936250363

;; sxhash関数だと配列を与えた場合、全て同じハッシュ値になってしまう
(sxhash (sb-ext:string-to-octets "ハッシュ関数"))
--> 518591303

(sxhash (sb-ext:string-to-octets "別の値"))
--> 518591303

;;; 【複数のハッシュ値】
;; nth-hash関数を使って、任意個のハッシュ値を安価に生成可能
;; ※ 内部的にはDoubleHashingを用いて生成している => 可算一つと乗算一つで算出可能

;; 一つのキーに対する10個の異なるハッシュ値を取得する
(defun hash10 (key)
  (multiple-value-bind (h1 h2) (jenkins-hash:hash-string key)
    ;; 最初の二つはそのまま使って、残りはnth-hash関数で生成する
    `(,h1 ,h2 . ,(loop FOR i FROM 2 BELOW 10 COLLECT (jenkins-hash:nth-hash i h1 h2)))))
       
(hash10 "ハッシュ関数")
--> (3188986421 2167986557 3229992239 1103011500 3270998057 1144017318 3312003875
     1185023136 3353009693 1226028954)

*1:sxhash関数を配列に適用すると常に同じ値が返ってきてしまう。sbcl-1.0.51-64bit

N-Queen (Haskell + Common Lisp)

Etsukata blog: Haskellでlist monadを使ってN-Queens問題を解いてみました を見たのをきっかけに久しぶりにN-Queen問題を解くプログラムをHaskellで書いてみた。

---- ファイル名: nqueen.hs
---- コンパイル: ghc -O2 -o nqueen nqueen.hs  # Glasgow Haskell Compiler, Version 7.0.3

import System

-- クイーンの配置: リスト内のオフセットが列を、値が行を表す
type Queens = [Int]

-- N-Queenを解く: ボードのサイズを受け取り、全ての解答(可能な配置のリスト)を返す
nQueens :: Int -> [Queens]
nQueens n = solve n []
  where solve 0   queens = [queens]   -- 最後の列: 全てのクイーンを配置できた
        solve col queens =            -- 途中の列: 全ての行に対して配置可能かを調べ、可能なら次の列に進む
          concat $ map (solve (col-1) . (:queens)) $ filter (check queens 1) [0..(n-1)]

-- クイーンが配置可能かどうか調べる
check :: Queens -> Int -> Int -> Bool  
check [] _ _  = True
check (q:qs) r row    -- rは対角線上の(チェックすべき)クイーンの位置を知るための変数
  | q /= row && q /= row+r && q /= row-r = check qs (r+1) row
  | otherwise = False
  
-- メイン関数
main = do
  args <- getArgs
  let size = (read $ head args)::Int
  let rlt = nQueens size
  putStrLn $ show . length $ rlt

実行結果:

$ ./nqueen 12
14200

処理時間

冒頭で挙げた記事のもの(Etsutaka)、および、Common Lisp(後述)との処理速度の比較。

  処理時間(サイズ=11) 処理時間(サイズ=12) 処理時間(サイズ=13)
nqueen(Haskell:本記事) 0.080秒 0.424秒 2.592秒
nqueen(Haskell:Etsutaka) 0.132秒 0.736秒 4.424秒
nqueen(Common Lisp) 0.071秒 0.375秒 2.289秒

この中ではCommon Lisp版が一番速くなっているけど、Haskellで効率の良いプログラムの書き方とかが全く分かっていないので、その辺を把握してちゃんと書けばHaskell版はもっと速くなるかもしれない。

Common Lisp

Common Lisp版のソースコード
内容的には N-Queen(1) - sileの日記 に最適化宣言を加えただけ。

(defvar *fastest* '(optimize (speed 3) (safety 0) (debug 0) (compilation-speed 0)))
(deftype max-board-size () '(mod #x100))

(defun check (row queens &optional (r 1))
  (declare #.*fastest*
           (max-board-size r row))
  (or (null queens) 
      (and (/= (the max-board-size (car queens)) row (+ row r) (- row r)) 
	   (check row (cdr queens) (1+ r)))))

(defun n-queen (n)
  (declare #.*fastest*
           (max-board-size n))
  (nlet-acc self (queens (col n))
    (if (zerop col)
        (accumulate queens) 
      (dotimes (row n)
        (when (check row queens)
          (self (cons row queens) (1- col)))))))
;; SBCL-1.0.51
> (n-queen 4)
--> ((2 0 3 1) (1 3 0 2))

> (time (length (n-queen 12)))
Evaluation took:
  0.401 seconds of real time
  0.400025 seconds of total run time (0.400025 user, 0.000000 system)
  [ Run times consist of 0.012 seconds GC time, and 0.389 seconds non-GC time. ]
  99.75% CPU
  800,068,094 processor cycles
  13,926,400 bytes consed
  
--> 14200

ビットリバース

割合汎用的な、整数のビットを前後反転する関数を作成してみた。
2の乗数サイズの任意の整数型のビット反転が可能。

// 反転例
bit_reverse(0x0000FFFF) => 0xFFFF0000
// 実装

// バイト単位での変換表
const unsigned char REV_BYTE[]={
  0,128,64,192,32,160,96,224,16,144, 80,208,48,176,112,240,8,136,72,200,
  40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100,228,
  20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220,
  60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,
  10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,
  38,166,102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,
  30,158,94,222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,
  49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,
  5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,
  45,173,109,237,29,157,93,221,61,189,125,253,3,131,67,195,35,163,99,227,
  19,147,83,211,51,179,115,243,11,139,75,203,43,171,107,235,27,155,91,219,
  59,187,123,251,7,135,71,199,39,167,103,231,23,151,87,215,55,183,119,247,
  15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255};

// リバースクラス
template <int BYTE_SIZE> 
struct bit_rev {
  static const int HALF_SIZE = BYTE_SIZE/2;
  static const int HALF_BITS = HALF_SIZE*8;
  
  // BYTE_SIZEが1以外なら、上位ビットと下位ビットに再帰的に処理を適用した後に、二つを入れ替える
  template <typename T>
  static T reverse(T n) {
    return 
      (bit_rev<HALF_SIZE>::reverse(n) << HALF_BITS) | 
      (bit_rev<HALF_SIZE>::reverse(n>>HALF_BITS));
  }  
};

// BYTE_SIZEが1ならテーブルを参照して、バイトを変換する
template <> struct bit_rev<1> {
  template <typename T>
  static T reverse(T n) {
    return REV_BYTE[n&0xFF];
  }
};

// インターフェース関数
template <typename T>
T bit_reverse(T n) {
  return bit_rev<sizeof(T)>::reverse(n);
}

サンプルコマンド:

/**
 * ファイル名: rev.cc
 * コンパイル: g++ -o rev rev.cc
 */
#include <iostream>

/*
 bit_reverse関数定義
 */

int main() {
  unsigned char n08 = 0x0F;
  unsigned short n16 = 0x009F;
  unsigned int n32 = 0x0000699F;
  unsigned long long n64 = 0x00000000666699FF;
  
  std::cout.setf(std::ios::hex, std::ios::basefield);
  std::cout.setf( std::ios::showbase );

  std::cout << "n08: " << (long long)n08 << " => " << (long long)bit_reverse(n08) << std::endl;
  std::cout << "n16: " << (long long)n16 << " => " << (long long)bit_reverse(n16) << std::endl;
  std::cout << "n32: " << (long long)n32 << " => " << (long long)bit_reverse(n32) << std::endl;
  std::cout << "n64: " << (long long)n64 << " => " << (long long)bit_reverse(n64) << std::endl;
  return 0;
}

実行結果:

$ ./rev
n08: 0xf => 0xf0
n16: 0x9f => 0xf900
n32: 0x699f => 0xf9960000
n64: 0x666699ff => 0xff99666600000000

自分の環境で軽く試した感じでは、ビット演算のみを使用して手書きで最適化したものと同等以上の速度が出ていた。

cc-dict: ハッシュテーブル

ハッシュテーブルを実装してみた。
cc-dict-0.0.3
チェイン法を用いたハッシュテーブルで、リンクリスト(チェイン)のノードを割り当てる際に自前のアロケータを使っている点以外は、特に変わったところもないと思う。

ベンチマーク

一応、ベンチマークも取ったので載せておく。
比較対象はg++のunordered_mapとGoogleのsparse_hash_map及びdense_hash_map。
ベンチマークプログラムにはHash Table Benchmarksに記載のものを使用させてもらった*1
※ 実行環境は Ubuntu-11.11-64bit / Intel(R) Core(TM) i7 CPU L 620 @ 2.00GHz。

このベンチマーク結果を見る限りは、特別性能が悪い、ということはなさそう*2

*1:ただし以下の追加・修正を施した。
 1: 検索処理時間の計測の追加
 2: もともとは文字列のベンチマークではキーの型const char*が使用されていたのをstd::stringに変更(関連)
 3: 処理時間にキー配列を生成する時間も含まれていたので、キー配列は最初にまとめて生成しておき、その後から計時を開始するように修正

*2:ベンチマークデータが結構恣意的(連番 or 乱数)なので、必ずしも実データで同様の性能がでるかは分からないけど・・・

std::tr1::unordered_mapのキーの型をconst char*にした場合の挙動

g++(及びその他)のunordered_mapで文字列をキーにしたい場合の注意書き。
unordered_mapのようにconst char*を指定すると、キーが文字列ではなくポインタ(整数値)として解釈されてしまう*1
以下、その際の挙動の確認用のコード:

/**
 * ファイル名: str_map.cc
 * コンパイル: g++ -o str_map str_map.cc
 * gcc-4.6.1
 */
#include <string>
#include <cstring>
#include <tr1/unordered_map>
#include <iostream>

int main() {
  // ハッシュマップ作成
  std::tr1::unordered_map<const char*, int> cstr_map;  // const char*用
  std::tr1::unordered_map<std::string, int> str_map;   // std::string用
  
  // キーを用意
  char key1[] = "abc";
  char* key2 = new char[4];
  strcpy(key2, "abc");
  
  // キーの値とアドレスを出力
  std::cout << std::endl << "--" << std::endl;
  std::cout << "key1: '" << key1 << "' (addr: " << (long)(key1) << ")" << std::endl;
  std::cout << "key2: '" << key2 << "' (addr: " << (long)(key2) << ")" << std::endl;
  
  // key1を要素挿入
  cstr_map[key1] = 10;
  str_map[key1] = 10;

  // key1とkey2を検索
  std::cout << std::endl << "--" << std::endl;
  std::cout << "cstr_map: key1=" << cstr_map[key1] << ", key2=" << cstr_map[key2] << std::endl;
  std::cout << " str_map: key1=" << str_map[key1] << ", key2=" << str_map[key2] << std::endl;

  // key1の値を変えてみる
  key1[1] = '2';  // key1 = "a2c"

  // key1を検索
  std::cout << std::endl << "--" << std::endl;
  std::cout << "key1: '" << key1 << "' (addr: " << (long)(key1) << ")" << std::endl;
  std::cout << "cstr_map: key1=" << cstr_map[key1] << std::endl;
  std::cout << " str_map: key1=" << str_map[key1] << std::endl;    
  return 0;
}

実行結果:

--
key1: 'abc' (addr: 140736322707456)  # key1とkey2は同じ文字列だけど、アドレスは異なる
key2: 'abc' (addr: 30732528)

--
cstr_map: key1=10, key2=0   # key1のみヒット
 str_map: key1=10, key2=10  # key1とkey2の両方にヒット

--
key1: 'a2c' (addr: 140736035469872)
cstr_map: key1=10           # ヒット(ポインタ値が同じなため)
 str_map: key1=0            # ヒットしない(文字列が変わっているため)

分かってみれば、これはこれで自然な動作なので特に違和感もないけど、最近はC++がご無沙汰だったためか(文字列ではなくポインタ値として扱われていることに)すぐに気づかずに少しはまってしまった。

*1:もちろん const char* 用に自前で定義したハッシュ関数と比較関数をテンプレート引数に渡してあげれば特に問題はない(はず)

フィールドのポインタから、オブジェクト全体のポインタを取得するためのマクロ

構造体やクラスのインスタンスの特定のフィールドのアドレス(ポインタ)だけ分かっている場合に、そこからオブジェクト全体のポインタを取得したい場合に使うマクロ。
やっていることは単にフィールドのアドレスから、そのフィールドの(クラスor構造体の)先頭からのオフセットを引いているだけ。

// type.fieldが、typeの先頭の何バイト目に配置されているかを求める
#define field_offset(type, field) \
  ((char*)&(((type*)NULL)->field) - (char*)NULL)

// type.filedのアドレス(field_ptr)から、fieldのオフセット分マイナスし、typeのアドレスを求める
#define obj_ptr(type, field, field_ptr) \
  ((type*)((char*)field_ptr - field_offset(type, field)))

メモリアロケータを自作する際に、割当メモリ領域にタグ付けするのに利用できそうだと思い作成。

/* main.cc */
#include <iostream>

// タグ付きメモリチャンク
struct chunk {
  struct {            // タグ情報:
    const char* type; // - 型名
    int size;         // - サイズ
  } tag;
  void* buf;
};

// メモリ割り当て
inline void* mem_allocate(const char* type, int size) {
  chunk* c = (chunk*)new char[sizeof(chunk::tag)+size];

  // タグ情報を保存する
  c->tag.type = type;
  c->tag.size = size;

  std::cout << "[" << c->tag.type << "] allocate: " << c->tag.size << " bytes" << std::endl;

  // 割り当てアドレスを返す
  return &c->buf;
}

// メモリ解放
void mem_free(void* p) {
  // 解放するアドレス(chunk::buf)から、タグ情報(chunk)を取得する
  chunk* c = obj_ptr(chunk, buf, p);
  std::cout << "[" << c->tag.type << "] free: " << c->tag.size << " bytes" << std::endl;
  delete c;
}

struct abc {
  int a;
  char b;
  long c;
};

// main関数
int main() {
  void* ptrs[3];

  std::cout << "# allocate:" << std::endl;
  ptrs[0] = new (mem_allocate("int", sizeof(int))) int;
  ptrs[1] = new (mem_allocate("pointer", sizeof(void*))) void*;
  ptrs[2] = new (mem_allocate("struct", sizeof(abc))) abc;
  
  std::cout << std::endl;
  std::cout << "# free:" << std::endl;
  for(int i=2; i >=0; i--)
    mem_free(ptrs[i]);

  return 0;
}
$ g++ -o main main.cc
$ ./main
# allocate:
[int] allocate: 4 bytes
[pointer] allocate: 8 bytes
[struct] allocate: 16 bytes

# free:
[struct] free: 16 bytes
[pointer] free: 8 bytes
[int] free: 4 bytes