進・日進月歩

IT, Jazz, study, engineering, すべての真実とクリエイティビティのために

Monday
Oct 12,2009

o(np)のdiffアルゴリズムを作りました。
元ネタはこの辺。
An O(NP) sequence comparison algorithm

なんで車輪の再発明したかというと、既存のライブラリだと挙動がわからない上に、使い勝手がいまいちだからです。
自分で全部書けば、自由自在(ぉ
とりあえずリファクタリングの余地は多々ある感じですが。

一般的な意味でのdiffと違うところと言えば、追加/削除のみしか考えておらず、「置換」がありません。
けど、アルゴリズム上「置換」に当たる場合は「追加→削除」または「削除→追加」のいずれかの順番でSESの中に現れるので、その場合を「置換」と処理すればよいかと思います。

使い方とかは暇があったら書く。

このあたりを参考にさせてもらいました。日本語の解説ではこれが一番わかりがよく正確だと思います。
diff(1)

CODE:
  1. <?php
  2. class DiffModel{
  3.     private $flip = false;
  4.     private $arr1 = null;
  5.     private $arr2 = null;
  6.     private $size1 = null;
  7.     private $size2 = null;
  8.     private $ses = null;
  9.  
  10.     private $patharr=null;
  11.     const SAME=0;
  12.     const ADD=1;
  13.     const DEL=2;
  14.  
  15.     public function __construct($article1="", $article2=""){
  16.         if(is_array($article1) and is_array($article2)){
  17.             $this->setArray($article1, $article2);
  18.         }else{
  19.             $this->setArticles($article1, $article2);
  20.         }
  21.     }
  22.  
  23.     public function setArticles($article1, $article2){
  24.         if(strlen($article1)> strlen($article2)){
  25.             $this->arr1 = empty($article1) ? array() : mb_str_split($article1);
  26.             $this->arr2 = empty($article2) ? array() : mb_str_split($article2);
  27.  
  28.             $this->size1 = count($this->arr1);
  29.             $this->size2 = count($this->arr2);
  30.             $this->flip = true;
  31.         }else{
  32.             $this->arr1 = empty($article2) ? array() : mb_str_split($article2);
  33.             $this->arr2 = empty($article1) ? array() : mb_str_split($article1);
  34.  
  35.             $this->size1 = count($this->arr1);
  36.             $this->size2 = count($this->arr2);
  37.             $this->flip = false;
  38.         }
  39.     }
  40.  
  41.     public function setArray($arr1, $arr2){
  42.         if(count($arr1)> count($arr2)){
  43.             $this->arr1 = $arr1;
  44.             $this->arr2 = $arr2;
  45.  
  46.             $this->size1 = count($this->arr1);
  47.             $this->size2 = count($this->arr2);
  48.             $this->flip = true;
  49.         }else{
  50.             $this->arr1 = $arr2;
  51.             $this->arr2 = $arr1;
  52.  
  53.             $this->size1 = count($this->arr1);
  54.             $this->size2 = count($this->arr2);
  55.             $this->flip = false;
  56.         }
  57.  
  58.     }
  59.  
  60.     public function diff(){
  61.  
  62.         $offset = $this->size2 + 1;
  63.  
  64.         $this->patharr = array();
  65.  
  66.         $fp = array_fill(0, $this->size1 + $this->size2 + 3, -1);
  67.         $delta = $this->size1 - $this->size2;
  68.  
  69.         $p=0;
  70.         for(; $fp[$delta + $offset] <$this->size1; $p++){
  71.             for($k = -$p; $k <$delta; $k++){
  72.                 $fp[$k + $offset] = $this->snake($k, $offset, $fp);
  73.             }
  74.  
  75.             for($k = $delta + $p; $k>= $delta; $k--){
  76.                 $fp[$k + $offset] = $this->snake($k, $offset, $fp);
  77.             }
  78.  
  79.         }
  80.  
  81.         return $delta + 2*($p - 1);
  82.     }
  83.  
  84.     public function getDiff(){
  85.         $ind = $this->size2 + $this->size1*($this->size2+1);
  86.         $item = array(-1, $this->size2, $this->size1);
  87.         $this->ses = array();
  88.         do{
  89.             switch($this->patharr[$ind][0]){
  90.             case (self::ADD):
  91.                 if($item[0] != $this->patharr[$ind][0]){
  92.                     $this->ses[] = array($this->flip ? self::ADD : self::DEL, $this->patharr[$ind][1], $item[1] - $this->patharr[$ind][1]);
  93.                 }else{
  94.                     $this->ses[count($this->ses)-1][1] = $this->patharr[$ind][1];
  95.                     $this->ses[count($this->ses)-1][2] += $item[1] - $this->patharr[$ind][1];
  96.                 }
  97.                 //echo sprintf("add %3d-%3d %s\n", $this->patharr[$ind][1], $item[1], mb_substr($this->article2, $this->patharr[$ind][1], $item[1] - $this->patharr[$ind][1], "utf-8"));
  98.                 break;
  99.             case (self::DEL):
  100.                 if($item[0] != $this->patharr[$ind][0]){
  101.                     $this->ses[] = array($this->flip ? self::DEL : self::ADD, $this->patharr[$ind][2], $item[2] - $this->patharr[$ind][2]);
  102.                 }else{
  103.                     $this->ses[count($this->ses)-1][1] = $this->patharr[$ind][2];
  104.                     $this->ses[count($this->ses)-1][2] += $item[2] - $this->patharr[$ind][2];
  105.                 }
  106.                 //echo sprintf("del %3d-%3d %s\n", $this->patharr[$ind][2], $item[2], mb_substr($this->article1, $this->patharr[$ind][2], $item[2] - $this->patharr[$ind][2], "utf-8"));
  107.                 break;
  108.             case (self::SAME):
  109.                 if($item[0] != $this->patharr[$ind][0]){
  110.                     $this->ses[] = array(self::SAME, $this->patharr[$ind][1], $item[1] - $this->patharr[$ind][1]);
  111.                 }else{
  112.                     $this->ses[count($this->ses)-1][1] = $this->patharr[$ind][1];
  113.                     $this->ses[count($this->ses)-1][2] += $item[1] - $this->patharr[$ind][1];
  114.                 }
  115.                 //echo sprintf("sam %3d-%3d %s\n", $this->patharr[$ind][1], $item[1], mb_substr($this->article2, $this->patharr[$ind][1], $item[1] - $this->patharr[$ind][1], "utf-8"));
  116.                 break;
  117.             }
  118.  
  119.             $item = $this->patharr[$ind];
  120.             $ind = $this->patharr[$ind][1] + $this->patharr[$ind][2]*($this->size2+1);
  121.  
  122.         }while($ind> 0);
  123.  
  124.         $this->ses = array_reverse($this->ses);
  125.         return $this->ses;
  126.  
  127.     }
  128.  
  129.     private function snake($k, $offset, &$fp){
  130.         $y=0;
  131.         if($fp[$k-1 + $offset] + 1> $fp[$k+1+$offset]){
  132.             $y = $fp[$k-1 + $offset] + 1;
  133.             //$this->patharr[($y-$k) + $y*($this->size2+1)] = array($y-$k,$y-1);
  134.             $this->patharr[($y-$k) + $y*($this->size2+1)] = array(self::DEL, $y-$k,$y-1);
  135.         }else{
  136.             $y = $fp[$k+1 + $offset];
  137.             $this->patharr[($y-$k) + $y*($this->size2+1)] = array(self::ADD, $y-$k-1, $y);
  138.         }
  139.  
  140.         $x = $y-$k;
  141.         $sx = $x;
  142.         $sy = $y;
  143.  
  144.         while($x <$this->size2 and $y <$this->size1 and $this->arr2[$x] == $this->arr1[$y]){
  145.             $x++;
  146.             $y++;
  147.         }
  148.  
  149.         if(!($x == $sx and $y == $sy) )$this->patharr[$x+$y*($this->size2+1)] = array(self::SAME, $sx,$sy);
  150.  
  151.         return $y;
  152.     }
  153.  
  154. }
  155. ?>

Wednesday
Oct 7,2009

JavaScriptで文字列型から整数型への変換速度比較
http://kur.jp/2009/10/06/stringtoint-by-javascript/
というのがあって、なんかコードを見てたらなんか思うところがあったので追試してみた。
結果から言うと、だいたい傾向は一緒だった。IE用の回避コードを入れてみて気づいたけど、実行順序によって速さが変わるかもしれない。(一度読み込んだ関数は二回目は若干速い?

CODE:
  1. <script type="text/javascript">
  2.     var randomnums = new Array(1000000);
  3.     for(i=0; i<1000000; i++){
  4.         randomnums[i] = String(Math.round(Math.random() * 100));
  5.     }
  6.  
  7. function warmup(){
  8.     for(i=0; i<1000000; i++){
  9.         randomnums[i];
  10.     }
  11. }
  12. function useSub(){
  13.     var startTime = new Date();
  14.     for(var i = 0; i <1000000;i++){
  15.         randomnums[i] - 0;
  16.     }
  17.     document.write(new Date() - startTime);
  18. }
  19. function useDiv(){
  20.     var startTime = new Date();
  21.  
  22.     for(var i = 0; i <1000000;i++){
  23.         randomnums[i] / 1;
  24.     }
  25.     document.write(new Date() - startTime);
  26. }
  27. function useparseInt(){
  28.     var startTime = new Date();
  29.  
  30.     for(var i = 0; i <1000000;i++){
  31.         parseInt(randomnums[i],10);
  32.     }
  33.     document.write(new Date() - startTime);
  34. }
  35. function useNumber(){
  36.     var startTime = new Date();
  37.  
  38.     for(var i = 0; i <1000000;i++){
  39.         Number(randomnums[i]);
  40.     }
  41.     document.write(new Date() - startTime);
  42. }
  43. //IEがおそすぎて変なalertを出すので1回分はダミー
  44. warmup();
  45. useSub();
  46. document.write("<br />");
  47. warmup();
  48. useSub();
  49. document.write("<br />");
  50. warmup();
  51. useDiv();
  52. document.write("<br />");
  53. warmup();
  54. useparseInt();
  55. document.write("<br />");
  56. warmup();
  57. useNumber();
  58. </script>

結果はこちら

firefox
6
6
6
119
274 

IE
801
781
851
2003
1553 

chrome
311
292
299
135
234
Friday
Sep 18,2009

screenには大変なじみがあるのではないかと思いますが、最近windowの縦分割(左右に分ける)をしたくなり、tscreenやscreenのパッチなど色々と考えた末に、tmuxにしてみました。

理由は特にない(^^;;んですが、使ってみた結果tmuxの方が若干見た目に奇麗であるように感じました。

現在最新版はバージョン0.9なのですが、ドキュメントがなくソースを読むしかない(?)ので、軽く使えるコマンドについて言及したいと思います。

とりあえずmacの人はmacportsにあるのでコマンド一発ではいります。

CODE:
  1. sudo port install tmux

そして、次は設定ファイルをおきましょう。screenでいうところの.screenrcは、.tmux.confというファイルになります。
私の設定ファイルはこちらをごらんください。
基本的にソースに付属しているscreen-keys.confをもとにしています。
このファイルを.tmux.confにリネームしてホームフォルダに置くだけで、screen風なキーバインドになります。

さて、screen-keys.confだけだとwindowを左右に分割することができないので、以下の設定を書き足してあります。
(他にも書いたんですが、ど忘れ・・・・)

CODE:
  1. #layout
  2. bind h select-layout even-horizontal
  3. bind v select-layout even-vertical
  4. bind f select-layout active-only

こうして、tmuxを立ち上げたあとC-a C-Sで画面を分割し、C-a C-hで分割を左右にすることができます。

さて問題はこの「select-layout even-horizontal」というコマンドをどこから見つけてくるかです。ドキュメントがないっぽいので、ソースを見ます。
ビビらなくても大丈夫。大変奇麗に作られてるのですぐに読めると思います。

ソースをここからダウンロードして展開してみてください。
そして、cmd-から始まるファイル名のファイルを探します。これが全部コマンドになっています。バージョン0.9だとこんな感じになっています。

CODE:
  1. % ls |grep "cmd-"
  2. cmd-attach-session.c
  3. cmd-bind-key.c
  4. cmd-break-pane.c
  5. cmd-choose-session.c
  6. cmd-choose-window.c
  7. cmd-clear-history.c
  8. cmd-clock-mode.c
  9. cmd-command-prompt.c
  10. cmd-confirm-before.c
  11. cmd-copy-buffer.c
  12. cmd-copy-mode.c
  13. cmd-delete-buffer.c
  14. cmd-detach-client.c
  15. cmd-down-pane.c
  16. cmd-find-window.c
  17. cmd-generic.c
  18. cmd-has-session.c
  19. cmd-kill-pane.c
  20. cmd-kill-server.c
  21. cmd-kill-session.c
  22. cmd-kill-window.c
  23. cmd-last-window.c
  24. cmd-link-window.c
  25. cmd-list-buffers.c
  26. cmd-list-clients.c
  27. cmd-list-commands.c
  28. cmd-list-keys.c
  29. cmd-list-sessions.c
  30. cmd-list-windows.c
  31. cmd-list.c
  32. cmd-load-buffer.c
  33. cmd-lock-server.c
  34. cmd-move-window.c
  35. cmd-new-session.c
  36. cmd-new-window.c
  37. cmd-next-layout.c
  38. cmd-next-window.c
  39. cmd-paste-buffer.c
  40. cmd-previous-layout.c
  41. cmd-previous-window.c
  42. cmd-refresh-client.c
  43. cmd-rename-session.c
  44. cmd-rename-window.c
  45. cmd-resize-pane.c
  46. cmd-respawn-window.c
  47. cmd-rotate-window.c
  48. cmd-save-buffer.c
  49. cmd-scroll-mode.c
  50. cmd-select-layout.c
  51. cmd-select-pane.c
  52. cmd-select-prompt.c
  53. cmd-select-window.c
  54. cmd-send-keys.c
  55. cmd-send-prefix.c
  56. cmd-server-info.c
  57. cmd-set-buffer.c
  58. cmd-set-option.c
  59. cmd-set-password.c
  60. cmd-set-window-option.c
  61. cmd-show-buffer.c
  62. cmd-show-options.c
  63. cmd-show-window-options.c
  64. cmd-source-file.c
  65. cmd-split-window.c
  66. cmd-start-server.c
  67. cmd-string.c
  68. cmd-suspend-client.c
  69. cmd-swap-pane.c
  70. cmd-swap-window.c
  71. cmd-switch-client.c
  72. cmd-unbind-key.c
  73. cmd-unlink-window.c
  74. cmd-up-pane.c

ここから「cmd-」を除いた部分がコマンド名です。機能はだいたいコマンド名見ればわかりますね。
各々のコマンドは引数を取ったりします。それを調べる時はソースを見ます。
たとえばさっきの「select-layout」であれば「cmd-select-layout.c」を見ます。すると、こんな感じのコードが見えるはずです。(一部抜粋)

CODE:
  1. const struct cmd_entry cmd_select_layout_entry = {
  2.     "select-layout", "selectl",
  3.     CMD_TARGET_WINDOW_USAGE " layout-name",
  4.     CMD_ARG1,
  5.     cmd_select_layout_init,
  6.     cmd_target_parse,
  7.     cmd_select_layout_exec,
  8.     cmd_target_send,
  9.     cmd_target_recv,
  10.     cmd_target_free,
  11.     cmd_target_print
  12. };

細かい説明ははしょりますが、というか理解してないのですが、ここからコマンド名が「select-layout」であり、エイリアスが「selectl」であることが読み取れます。
ちなみにこの書き方はxwindowの拡張であるglxの実装の一つであるcompizのプラグインもこういった書き方になっているので、見慣れておくといいような気もします。

次に、引数ですが次の部分に注目します。

CODE:
  1. void
  2. cmd_select_layout_init(struct cmd *self, int key)
  3. {
  4.     struct cmd_target_data  *data;
  5.  
  6.     cmd_target_init(self, key);
  7.     data = self->data;
  8.  
  9.     switch (key) {
  10.     case KEYC_ADDESC('0'):
  11.         data->arg = xstrdup("manual-vertical");
  12.         break;
  13.     case KEYC_ADDESC('1'):
  14.         data->arg = xstrdup("even-horizontal");
  15.         break;
  16.     case KEYC_ADDESC('2'):
  17.         data->arg = xstrdup("even-vertical");
  18.         break;
  19.     case KEYC_ADDESC('9'):
  20.         data->arg = xstrdup("active-only");
  21.         break;
  22.     }
  23. }

はい。ここで「even-vertical」とか出てきましたね。細かい説明はまたしてもはしょりますが、ここが引数でありそうな事はぱっと見でわかると思います。わかってください。
これをもとにさっきの設定を見てみましょう。

CODE:
  1. #layout
  2. bind h select-layout even-horizontal
  3. bind v select-layout even-vertical
  4. bind f select-layout active-only

今まで見てきた通りですね!

じつは、他のコマンドもだいたい似たような感じになっています。それぞれのコマンドは非常にコンパクトにわかりやすく書いてあるので山勘で何とかなるかと思います。

しかし、0.8系と0.9系でコマンドが変わったりしてるようなので、まだちょっと注意がいるかもしれません・・・・

というかんじで、みなさん楽しいtmuxライフをおくりましょう!

キーワード自動リンクの話

  • Filed under: 雑記
Tuesday
Sep 1,2009

キーワード自動リンクの話を某勉強会でしました。その資料を公開します。

自動リンクのためのデータ構造としてTrieを採用し、今回は有名な実装であるsenna, Tx, dartsの比較をしました。
それぞれのライブラリの背景は次の様なデータ構造です。

  • senna - パトリシア木
  • Tx - LOUDS
  • darts - Double Array

今回の実験はとりあえず実装しましたレベルなので、厳密な速度や容量にはなっていない可能生があります。
というのは、それぞれのライブラリを精査したわけではないからです。
またキーワード抽出した結果がそれぞれで若干異なっています。抽出できたキーワードの件数に大きなさや検索にかかる時間に相関が無さそうなので概ね正しいと考えることにしました。

というわけで、ご参考まで。

Monday
Aug 31,2009

Txでdartsのようなtraverseをする関数を作ってみました。

使い方はdartsのtraverseと同じだと思います。たまたま作ったのでパッチをそのまま貼っておきます。動作は未保証。dartsよりも爆速でふきました。

CODE:
  1. diff -cb tx-0.13/tx.cpp tx_original/tx.cpp
  2. *** tx-0.13/tx.cpp  2009-04-13 13:21:14.000000000 +0900
  3. --- tx_original/tx.cpp  2009-08-27 22:45:25.000000000 +0900
  4. ***************
  5. *** 195,200 ****
  6. --- 195,221 ----
  7.       return 0;
  8.     }
  9.    
  10. +   uint tx::traverse(const char* str, size_t& pos) const {
  11. +       uint curPos = 2;
  12. +       uint retId = NOTFOUND;
  13. +       size_t begin = pos;
  14. +       if (terminal.getSize() <= 2) return retId;
  15. +
  16. +       for (size_t i = 0 ; ; i++){
  17. +           const uint nodeId = loud.rank(curPos-1,1)-1;
  18. +           if (terminal.getBit(nodeId)){
  19. +               pos = begin + i;
  20. +               retId = terminal.rank(nodeId,1)-1;
  21. +           }
  22. +           uint nextPos = getChild(curPos,str[begin + i]);
  23. +           if (nextPos == UINT_MAX){
  24. +               break;
  25. +           }
  26. +           curPos = nextPos;
  27. +       }
  28. +       return retId;
  29. +   }
  30. +
  31.     uint tx::prefixSearch(const char* str, const size_t len, size_t& retLen) const {
  32.       uint curPos = 2;
  33.       uint retId = NOTFOUND;
  34. diff -cb tx-0.13/tx.hpp tx_original/tx.hpp
  35. *** tx-0.13/tx.hpp  2008-06-03 21:05:00.000000000 +0900
  36. --- tx_original/tx.hpp  2009-08-27 22:41:04.000000000 +0900
  37. ***************
  38. *** 26,31 ****
  39. --- 26,32 ----
  40.       int read(const char* fileName);
  41.  
  42.       uint prefixSearch(const char* str, const size_t len, size_t& retLen) const;
  43. +     uint traverse(const char* str, size_t& pos) const;
  44.       uint expandSearch(const char* str, const size_t len, std::vector<std::string>& ret, const uint limit = 0) const;
  45.       uint commonPrefixSearch(const char* str, const size_t len, std::vector<std::string>& ret, std::vector<uint>& retID,  const uint limit = TX_LIMIT_DEFAULT) const;
  46.       uint commonPrefixSearch(const char* str, const size_t len, std::vector<uint>& retLen, std::vector<uint>& retID,  const uint limit = TX_LIMIT_DEFAULT) const;
  47. ***************
  48. *** 42,48 ****
  49.      
  50.       static uint NOTFOUND;
  51.      
  52. !   private:
  53.       uint getChild(const uint pos, const char c) const;
  54.       uint getParent(const uint pos, char& c) const;
  55.       void enumerateAll(const uint pos, const std::string str, std::vector<std::pair<size_t, std::pair<std::string, uint>>>& ret) const;
  56. --- 43,49 ----
  57.      
  58.       static uint NOTFOUND;
  59.      
  60. ! //  private:
  61.       uint getChild(const uint pos, const char c) const;
  62.       uint getParent(const uint pos, char& c) const;
  63.       void enumerateAll(const uint pos, const std::string str, std::vector<std::pair<size_t, std::pair<std::string, uint>>>& ret) const;

sennaでpatricia treeを作る

  • Filed under: IT
Friday
Aug 28,2009

はてなのようなキーワードリンクをRubyで付与する実例
と、いうのをつくってもらったので、これをもとにCでsennaのpatricia treeを試すプログラムを書いたよ。

機能的にはとりあえずキーワードの検出だけ。

CODE:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <senna/senna.h>
  4. #include <string.h>
  5. //#include <exectime.h>
  6.  
  7. void create_index(char* index, char* filename){
  8.  
  9.         sen_sym *sym;
  10.  
  11.         if(!(sym = sen_sym_create(index, 0, SEN_INDEX_NORMALIZE, sen_enc_utf8))){
  12.             printf("cannot open or create sym file\m");
  13.             abort();
  14.         }
  15.  
  16.         FILE * fp = fopen(filename, "rb");
  17.  
  18.         char buffer[2048];
  19.        
  20.         sen_id sym_id;
  21.         while(fgets(buffer, 2048, fp)){
  22.             sscanf(buffer, "%s\n", buffer);
  23.            if(!(sym_id = sen_sym_get(sym, buffer))){
  24.                 printf("cannot create link\n");
  25.                 abort();
  26.            }
  27.  
  28. //           printf("%d\t", sym_id);
  29.         }
  30.  
  31.         fclose(fp);
  32.    
  33.         sen_sym_close(sym);
  34.  
  35. }
  36.  
  37. void traverse(char* index, char* filename){
  38.     printf("traverse\n");
  39.     sen_sym* sym;
  40.  
  41.     if(!(sym = sen_sym_open(index))){
  42.         printf("cannot open index file\n");
  43.         abort();
  44.     }
  45.  
  46.     FILE * fp = fopen(filename, "rb");
  47.  
  48.     if(!fp){
  49.         printf("cannot open file\n");
  50.         abort();
  51.     }
  52.  
  53.     fseek(fp, 0, SEEK_END);
  54.  
  55.     int filesize = ftell(fp);
  56.     int offset = 0;
  57.  
  58.     fseek(fp, 0, SEEK_SET);
  59.  
  60.     char * content = new char[filesize + 1];
  61.     const char * cp = content;
  62.  
  63.  
  64.     fread(content, sizeof(char), filesize, fp);
  65.     content[filesize] = '\0';
  66.  
  67.     sen_sym_scan_hit sh[32];
  68.  
  69.     char buffer[2048];
  70.  
  71.     const char * rest;
  72.  
  73.     exectime::start_timer();
  74.  
  75.     while((rest - content) <filesize){
  76.         int found;
  77.         if(!(found = sen_sym_scan(sym, cp, filesize, sh, 32, &rest))){
  78.             break;
  79.         }       
  80.  
  81.         for(int i=0; i<found; i++){
  82.             int key_len = sen_sym_key(sym, sh[i].id, buffer, 2048);
  83.             if(key_len> 0 && sh[i].length> 7){
  84.                printf("%s\n", buffer);
  85.             }
  86.  
  87.         }
  88.         cp = rest;
  89.  
  90.     }
  91.  
  92.     exectime::end_timer();
  93.  
  94.     printf("time: %f sec\n", exectime::time_result());
  95.  
  96.  
  97. }
  98.  
  99. int main(int argc, char** argv){
  100.  
  101.     if(!strcmp(argv[1], "make")){
  102.         create_index(argv[2], argv[3]);
  103.     }else if(!strcmp(argv[1], "traverse")){
  104.         traverse(argv[2], argv[3]);
  105.     }
  106.    
  107. }

  • Comments Off
  • Tuesday
    Aug 25,2009

    Darts: Double-ARray Trie System

    http://chasen.org/~taku/software/darts/

    dartsというのは、Trie木をdouble arrayで実装したライブラリです。ヘッダファイル一つだけの配布なので大変使いやすい。

    今回HAT-trieを実装してみるにあたって、Trie木の部分の実装としてこれを使ってみることにしました。

    しかし、サンプルが動かない・・・・!どうやらよく見るとテンプレート周りとconstを付け加えた際に、もともとのサンプルが動かなくなった模様。
    なので、これを修正しました。別に大したことはしていません。

    CODE:
    1. #include &lt;iostream&gt;
    2. #include &lt;darts.h&gt;
    3.  
    4. int main (int argc, char **argv)
    5. {
    6. using namespace std;
    7.  
    8. const Darts::DoubleArray::key_type   *str[] = { "ALGOL", "ANSI", "ARCO""ARPA", "ARPANET", "ASCII" }; // same as char*
    9. Darts::DoubleArray::result_type val[] = { 1, 2, 3, 4, 5, 6 }; // same as int
    10.  
    11. Darts::DoubleArray da;
    12. da.build (6, str, 0, val);
    13.  
    14. cout &lt;&lt;da.exactMatchSearch&lt;int&gt;("ALGOL") &lt;&lt;endl;
    15. cout &lt;&lt;da.exactMatchSearch&lt;int&gt;("ANSI") &lt;&lt;endl;
    16. cout &lt;&lt;da.exactMatchSearch&lt;int&gt;("ARCO") &lt;&lt;endl;
    17. cout &lt;&lt;da.exactMatchSearch&lt;int&gt;("ARPA") &lt;&lt;endl;
    18. cout &lt;&lt;da.exactMatchSearch&lt;int&gt;("ARPANET") &lt;&lt;endl;
    19. cout &lt;&lt;da.exactMatchSearch&lt;int&gt;("ASCII") &lt;&lt;endl;
    20. cout &lt;&lt;da.exactMatchSearch&lt;int&gt;("APPARE") &lt;&lt;endl;
    21.  
    22. da.save("some_file");
    23. }

  • Comments Off
  • Tuesday
    May 12,2009

    activerecordの様なDBラッパーを作ろうと思って、 @ujm の協力の下module_evalを使って実装してみた。
    必要最小限のことしか書いてないのでDBラッパーとしては貧弱だけど、module_evalの動きに注目してみてほしい。
    本物のactiverecordも似たような実装をしていたと思う。(うろ覚え)

    RUBY:
    1. require "rubygems"
    2. require "mysql"
    3.  
    4. class ModelBase
    5.  
    6. DEFAULT_DBUSER = "user"
    7. DEFAULT_DBPASS = "pass"
    8. DEFAULT_DBHOST = "host"
    9. DEFAULT_DBNAME = "name"
    10.  
    11. @@options = {"dbhost" =&gt; DEFAULT_DBHOST,
    12. "dbuser" =&gt; DEFAULT_DBUSER,
    13. "dbpass" =&gt; DEFAULT_DBPASS,
    14. "dbname" =&gt; DEFAULT_DBNAME
    15. }
    16.  
    17. def self.get_db_object()
    18. @@mysql_obj ||= Mysql::new(@@options["dbhost"], @@options["dbuser"], @@options["dbpass"], @@options["dbname"])
    19. end
    20.  
    21. def self.table_name(tn)
    22. module_eval <<"EOS"
    23. class &lt;&lt;self
    24. def get_table_name
    25. '#{tn.to_s}'
    26. end
    27. end
    28. EOS
    29. end
    30.  
    31. def self.query(sql, place_holder=[])
    32. db_obj = get_db_object
    33. stmt = db_obj.prepare(sql)
    34. stmt.execute(*place_holder)
    35. stmt
    36. end
    37.  
    38. def self.count(condition="1", options=[])
    39. sql = "select count(*) as count from #{get_table_name} where " + condition
    40. res = query(sql, options)
    41. count = res.fetch
    42. count[0].to_i
    43. end
    44.  
    45. private_class_method :get_db_object
    46. end

    これを次のように使う。たとえばuserテーブルを扱うクラスはこんな感じ。

    RUBY:
    1. require "lib/ModelBase.rb"
    2.  
    3. class UserModel <ModelBase
    4. table_name "users"
    5.  
    6. def initialize(id, name, screen_name, location, description, profile_image_url, url, protected, followers_count, friends_count, time_zone, favorites)
    7. @id = id
    8. @name = name
    9. @screen_name = screen_name
    10. @location = location
    11. @description = description
    12. @profile_image_url = profile_image_url
    13. @url = url
    14. @protected = protected
    15. @followers_count = followers_count
    16. @friends_count = friends_count
    17. @time_zone = time_zone
    18. @favorites = favorites
    19. end
    20.  
    21. def save
    22.  
    23. sql = "insert ignore into #{UserModel::get_table_name} (id, name, screen_name, location, description, profile_image_url, url, protected, followers_count, friends_count, time_zone, favorites) values (?,?,?,?,?,?,?,?,?,?,?,?)"
    24. UserModel::query(sql, [@id, @name, @screen_name, @location, @description,
    25. @profile_image_url, @url, @protected, @followers_count,
    26. @friends_count, @time_zone, @favorites])
    27. end
    28.  
    29. end

    ModelBaseクラスを継承し、table_nameメソッドでテーブル名を指示しているわけです。

    で、ポイントはModelBaseクラスの次の部分。

    RUBY:
    1. def self.table_name(tn)
    2. module_eval <<"EOS"
    3. class <<self
    4. def get_table_name
    5. '#{tn.to_s}'
    6. end
    7. end
    8. EOS
    9. end

    UserModelの冒頭でtable_nameとこのメソッドを呼んでいます。

    module_evalは、そのメソッドが呼ばれたところをselfにして実行されるメソッドであるため、このtable_nameメソッドの中で定義されているget_table_nameは、UserModelのクラスメソッドとして定義されます。

    つまり、UserModel内でtable_nameが呼ばれた時点では、次のコードと同じことをします。

    RUBY:
    1. class <<UserModel
    2. def get_table_name
    3. '#{tn.to_s}'
    4. end
    5. end

    こうすることによって、プログラム中でテーブル名をハードコードしなくてもすむようになるとともに、countなどの関数を親クラスで定義できるようになるわけです。

    例えば、この方法で作ったクラスで全レコード数を数えるコードはこうなります。

    RUBY:
    1. require "lib/UserModel.rb"
    2.  
    3. UserModel::count

    大変すっきりしていますね。

    蛇足:

    これはtwitterのポストを扱うために書いているプログラムの一部です。
    rails使おうかとも思ったのだけど、railsはまだ多機能すぎるためこの程度なら書いてしまった方が悩みが少ない(重いとかファイルの配置とか拡張性とか)かと思って自分で書いています。

    しかし、自分でフレームワーク作るかというと・・・・たぶんないですね。
    フレームワークは必要じゃない機能もたくさん足すことになります。
    なんの目的もなしに機能追加を繰り返していくと、フレームワーク自身のメンテナンスに負荷がかかるようになってしまうし、コードの自由度がどんどん下がっていきます。

    それよりは、構造化を心がけながらプログラミングをし、拡張性を担保しながら、必要十分なものを作っていく・・・と、いうのが賢いプログラミングである気がしました。

  • Comments Off
  • テクノロジーの神話

    • Filed under: 雑記
    Monday
    Mar 30,2009

    「テクノロジーが重要なんだ」

    こういう人は少なくないです。工学部を今年卒業する自分としても、「テクノロジーが重要」というのはとても同意できることです。
    しかし、これが一度サービスに関する議論になった時どうでしょう?
    私はこういうときにいつもこういいます。

    「技術的問題はほとんどの場合解決可能なので、ユーザが受けるべきメリットについてよく考えてください」

    これがなかなか難しいらしいことに最近気づく。
    技術者は「技術的面白さ」が「一般人の感覚から離れすぎている」ことで、ユーザのメリットを考えにくい傾向があるように感じられる。
    これはよくよく指摘されていることです。
    が、面白いことに「技術者じゃない人」も同じく、「技術ありきでサービスを考える」傾向がとても強いことに最近気づいた。
    やっかいなのは技術に関する知識がない分、発想が貧弱だということだ。

    よくgoogleが引き合いに出される。(一応特定個人のことを言ってるわけではなくて本当にそういう人が多いということです。)
    だいたい理屈はこんな感じだ。
    「googleにはpagerankというテクノロジーがあった。かならずイノベーションにはテクノロジーが必要なんだ」
    「googleにはcloudを持つ技術があった。だからイノベーションにはテクノロジーが必要なんだ」
    しかし、これをいってる人の何人がpagerankによる効用やcloudによる効用を説明できるのかはかなり疑問です。
    「テクノロジーが必要」といいながら、その実「テクノロジーが本当に重要だったかどうか」については全く検討されていないように感じられる。
    とくに「cloudを持つ技術がある」というあたりは、もはや末期的な発言に聞こえる。cloudに関する技術的な定義は大変あいまいで、技術がわかる人は少なくともこういう発言はしない(と、俺は少なくとも思う

    私はここに「テクノロジーの神話」があるように思えてならない。

    もうテクノロジーの時代は去ろうとしているということに気づかないといけない。
    「サービスの時代」といわれ、先進国は三次産業にうつっているといわれて久しい昨今。「テクノロジーの発明」に固執することはおかしいのではないだろうか?

    もはやほとんどの電化製品は技術の集大成であるということを認識しないといけない。webサービスはもっと多くの技術の上に成り立っている。
    RDBひとつとってみたところで、技術の集大成だ。
    もはやフィラメントを光らせたらそのまま製品にできるような時代はとっくの昔に終わっている。ひとつの技術がイノベーションを起こせるというのはすでに時代錯誤でしかない。

    現代のテクノロジーにおいて必要なことは2つ。「市場をみること」と「多くのテクノロジーを知ること」だ。
    市場を見ることで、どのようなテクノロジーが必要とされており、なにを開発するべきかを知らないといけない。
    そして、市場の問題を解決するために、適切なテクノロジーを選択できなくてはいけない。

    もし、現代にイノベーションが求められているというのであれば、「技術の発明」に固執してはいけない。
    イノベーションは、テクノロジーの与えた経済的・社会的インパクトで計られ、しかも技術の発明がそのまま社会にインパクトを与える時代は終わってしまっているからだ。
    今はむしろ、与えるべきインパクトから逆算することが求められている。与えるべきインパクトから、適切な技術を開発・選択することが求められている。

    今一度真摯に「テクノロジー」「イノベーション」という言葉について考えてほしい。
    本当に、あなたが考えている「pagerank」や「cloud」がイノベーションに必要だったんだろうか?むしろ、単純に「検索結果がよかったから」じゃないだろうか?
    インターネットは本当にあなたの生活を変えたのだろうか?ニコニコ動画がなかったら?Amazonがなかったら?googleがなかったら?あなたはwebサービスがなくてもインターネットに感動しただろうか?

    こういう言い方をしてもいいかもしれないです。あなたの「目で見たもの、耳で聞いたもの、肌で感じたもの」を信じてください。
    本当にpagerankにあなたは感動したのでしょうか?メディアにだまされてそんな気がしてるだけじゃないですか?
    本当にインターネットに感動したのでしょうか?今あなたの見ているものはインターネットなんですか?

    重要なのは、事実がどうだとかではないです。あなたの目で見、頭で考え、そして納得したことなんでしょうか?

    誰かが作った「テクノロジーの神話」に騙されてませんか?

    技術をマネージする・予期する

    • Filed under: 雑記
    Tuesday
    Jan 27,2009

    今日の日経新聞朝刊に「科学研究 国の役割増大」という記事があって面白かったので転載。

    ドイツでは、ある完了が大蔵大臣に銃を突きつけるようにして、科学研究費の獲得に勤めていた。その官僚の名前はフリートリヒ・アルトホーフという。

    (中略)

    彼は優れた科学上のアイディアを持ちながら、それを発展させる資金がないために困っている人物を見つけると、一存で研究費を投入して完成に導いた。あるいは優れた能力を持ちながら、なかなか教授になれないでいる人物を見つけると、大学側の反対を押し切り、教授に任命した。

    そうした研究者が数年もすると、次々にノーベル賞を受賞して言った。そのため彼は後年「ノーベル賞受賞者のゴッドファーザーと呼ばれた」

    (中略)

    しかしその反面、アトホーフはいつまでも国家財政に頼っているのでは、限界があることを見抜いていた。彼は巨額な民間資金が科学研究に投入されている米国を横目で見ながら、それに強い危機感を抱いていた。そこでひそかに同様に仕組みをドイツに導入する計画を練り始めた。

    しかし彼の企画はスムーズには進まなかった。

    (中略)

    アルトホーフの没後百周年にあたる2008年10月彼の人物や功績を再検討しようと、八カ国の報告者が参加した国際シンポジウムが開かれた。シンポジウムでは、今後科学研究はいかなる資金によって支えられるべきかという点に議論が集中した。

    「役に立つ知識か否かは、市場によって選別される。市場から求められない研究分野が消滅するのは、当然の結果である」「科学研究にかかるコストは、その成果から直接利益を得る企業が負担すればよい」「なんらの企業利益も生まないような科学研究は支援する価値がない。」・・・・こうした発想が現代では主流となっている。

    しかし学問とか科学研究は、レンガを一つ一つ積み上げてゆく作業である。一つ一つのレンガは目に見える利益を生まなくても、それがなければ知識全体がつみあがっていかない。

    (中略)

    この現代では新たな研究成果は、出資者の私有財産となり、他者が自由には使えないケースが増えた。

    (中略)

    大学予算削減が連続する時代には、第二、第三のアルトホーフもまた欠かせない。

    なるほど。研究成果と企業云々の部分に関して似たようなことをドラッカーも実は書いている。

    テクノロジストの条件 ドラッカー

    p131

    近代企業は、いわば技術の産物である。(中略)今日の大企業のルーツは、最初の大企業である19世紀半ばの鉄道、つまり当時の技術的イノベーションだった。それ以降、今日のコンピュータメーカーや製薬会社に至る成長企業のほとんどが、それぞれの時代の新技術による産物だった。

    それどころか、やがて企業が新技術を生み出すようになった。(中略)技術は、イノベーションとして社会的に有効なものとなる上で、ますます企業に頼るようになった。

    今日たまたま素粒子物理の友人と話していたが、「加速器一回まわすのに一千万くらいかかる」という話である。一回の実験で一千万。ひとつの研究をなすのにも世界中の企業からお金を集めなければない世界がここにある。

    素粒子物理はともかくとして、実際に企業活動をベースとして技術を捕らえる事に関して、ドラッカーは「技術を予期することが肝心だ」と説く。

    テクノロジストの条件 ドラッカー

    p132

    第一に、技術は神秘的なものでも予期できないものでもない。合理的に予期することは可能であり、そうすることが必要である。

    (中略)

    第二に、技術は事業活動と別種の活動ではない。そもそも事業と別のものとしてしまったら、マネージメントできない。

    (中略)

    技術は把握できないとする考えは、もはや時代遅れである。まさにこの考えが技術に対する恐怖をもたらす。発明は予期したり計画したりできないとする考えも、また間違いである。

    (中略)

    特にイノベーションについては、予期すること、計画することが重要だって、かつ必須である。

    「技術を予期することが容易だというのではない」と言ってはいるが、繰り返し繰り返し、技術やイノベーションを予期することの大切さを説いている。おもしろいのは、この続き。

    マネジメントが関心を持つべきものは、発明ではなくイノベーションである。イノベーションとは社会用語であり、経済用語であって技術用語ではない。(中略)イノベーションは、たとえ発明から生まれることが多くとも、発明と同義ではない。

    言われてみると当たり前のことなんだけど、実はこの視点、結構かけているように感じることが多い。
    正直よく「こんなことできるなんてすごい!イノベーションだ!」とか「これが開発できたら今までになかった!イノベーションだ!」ときいたりするんだけど、その経済効果とかってあまり考えられていないなぁとよく感じることが多い。
    結局売れなかったものは「イノベーション」と呼ばないにもかかわらず、なぜか開発段階やコンセプトの段階で、この「経済効果」という概念を欠いているものが多いように感じることが多い。
    むしろ個人的な感想としては、開発やコンセプト段階で経済効果について言及することが悪とさえ思われてるんじゃないかと思うことすらある。

    また一方で、「イノベーションなんか予期できない」と言っている人がいますが、実は割りと限定的に予想することは可能だったりします。それなりの業界知識と工学知識があると、「何があたるか」はわかんなくとも「何が可能そうか」はわかるし「何が売れそうか」もなんとなくわかる。俺的に問題は「可能そう」でかつ「売れそう」なものを考えるのが案外難しい。

    こっからは持論ですが、結果的に工学や科学・ソフトウェア、なんでもいいんですが、そういったものに対するそれなりに深い知見と、ある程度のマーケットへの洞察が「イノベーション」を起こすためには重要であると思っています。「それなりに深い」っていうのは難しいところですが、工学が全くわからない人は論外だけど、それほどマニアックでもないといったところであるきがします。

    現に工学部の研究室の研究というのは、現代の技術のちょっとした発展を扱っているというものが多いです。結局研究もほとんどのものは従来のものに自分なりに方向付けをして付け足していると考えてもそれほど間違ってはいないと思います。これからの企業は、現代のテクノロジーの把握と、マーケットを見据えた方向付け、そして資源の集中と開発という技術とともに発展していく形というのがいいのではないかなと思います。技術の発展には企業活動による資金の供給が必要であり、企業はマーケットを見据えその方向性をコントロールすることで技術をお金に変え発展する。こういう技術に対する先見性と経営が必要なんじゃないかなと思う今日この頃でした。

  • Comments Off
  • about me

    原田 惇

    応用数学や統計、ITなどを利用していろんなことをしています。
    自己紹介はこちら

    adsense

    amazonお勧め

    Recent Posts


    Recent Comments


    Twitter



    Archives


    Meta