2009/01/16


今日、とある場所でkanaさんからvimに"syn-include"なんて物があるのを教えて貰いました。

:he syn-include
9. Including syntax files                               *:syn-include* *E397*

It is often useful for one language's syntax file to include a syntax file for
a related language.  Depending on the exact relationship, this can be done in
two different ways:

        - If top-level syntax items in the included syntax file are to be
          allowed at the top level in the including syntax, you can simply use
          the |:runtime| command: >

  " In cpp.vim:
  :runtime! syntax/c.vim
  :unlet b:current_syntax

<       - If top-level syntax items in the included syntax file are to be
          contained within a region in the including syntax, you can use the
          ":syntax include" command:

:sy[ntax] include [@{grouplist-name}] {file-name}

          All syntax items declared in the included file will have the
          "contained" flag added.  In addition, if a group list is specified,
          all top-level syntax items in the included file will be added to
          that list. >

   " In perl.vim:
   :syntax include @Pod <sfile>:p:h/pod.vim
   :syntax region perlPOD start="^=head" end="^=cut" contains=@Pod
<
          When {file-name} is an absolute path (starts with "/", "c:", "$VAR"
          or "<sfile>") that file is sourced.  When it is a relative path
          (e.g., "syntax/pod.vim") the file is searched for in 'runtimepath'.
          All matching files are loaded.  Using a relative path is
          recommended, because it allows a user to replace the included file
          with his own version, without replacing the file that does the ":syn
          include".
知らんかった...
これを使えばPerlのPodだけ別のsyntaxファイルから適用出来るといった物。
これは凄い!

つまりはregionだけ決められれば、その部分に言語毎のsyntaxが適用出来る事になる。試しに、はてなのスーパーPre記法に色を付けられる様にしてみた。
ベースはmotemenさんのhatena-vim
motemen's hatena-vim at master - GitHub

Vim scripts for posting/updating hatena diary/group

http://github.com/motemen/hatena-vim/tree/master
このsyntax/hatena.vimの最下行に以下のコードを貼り付ける。
" append to syntax/hatena.vim
function SyntaxSuperPre()
  let lnum = 1
  let lmax = line("$")
  let mx = '^>|\(.*\)|$'
  while lnum <= lmax
    let curline = getline(lnum)
    if curline =~ mx
      let lang = substitute(curline, mx, '\1', '')
      exec 'runtime! syntax/'.lang.'.vim'
      unlet b:current_syntax
      let syntaxfile = fnameescape(substitute(globpath(&rtp, 'syntax/'.lang.'.vim'), '[\r\n].*$', '', ''))
      if len(syntaxfile)
        exec 'syntax include @inline_'.lang.' '.syntaxfile
        exec 'syn region hatenaSuperPre matchgroup=hatenaBlockDelimiter start=+^>|'.lang.'|$+ end=+^||<$+ contains=@inline_'.lang
      endif
    end
    let lnum = lnum + 1
  endwhile
  " workaround for perl
  syn cluster inline_perl remove=perlFunctionName
endfunction
call SyntaxSuperPre()
適当なので使わないで下さい
perlFunctionNameはregionが広すぎるので無効にしてます
すると...
hatena super pre on vim
おーーー!出ました。

続きはこの辺でやっていきます。出来上がったらmotemenさんにmergeして貰うのも良いかも。
ちなみに、filetype適用時にロードしているので、pre記法の言語を編集途中で変更したり、新しくスーパーPre記法を追加したりするとsyntaxが適用されなくなります。ま、これからですな。

syn-include++
Posted at by



2009/01/14


追記
事情が変わった!(髭男爵)
変わってなかった...orz
詳細は下記。

おもしろい。
static - 素人がプログラミングを勉強するブログ
var counter = function () {
  var static = /(^o^)/;
  return ('i' in static)? ++static.i:
    static.i = 0;
};

console.log(counter()); // 0
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
http://d.hatena.ne.jp/javascripter/20090113/1231863436
正規表現リテラルがコンパイル時に生成され、静的保持される特性を利用したカウンタ。

でも使わない。気持ちワルイ!苦笑
皆、思いつくだろうけど私はやっぱりこう書く。
var counter = (function() {
  var static = 0;
  return function() {return static++};
})();

ちなみにベンチを取ってみた。
if (typeof console == 'undefined') console = {log:print};

var counter1 = function () {
  var static = /(^o^)/;
  return ('i' in static)? ++static.i:
    static.i = 0;
};

var counter2 = (function() {
  var static = 0;
  return function() {return static++};
})();

var start;
start = new Date().getTime();
for(var n = 0; n < 10000000; n++) counter1();
console.log(new Date().getTime() - start)

start = new Date().getTime();
for(var n = 0; n < 10000000; n++) counter2();
console.log(new Date().getTime() - start)
Windows XP、P4 3GHz CPU、1G Mem。

tracemonkey : JavaScript-C 1.8.0 pre-release 1 2007-10-03
6515
5500

v8 : V8 version 0.3.4 (internal)
1558
383

ま、やっぱりね...というところ。
結論
V8はえーーー!

追記 os0xさんから
var counter1 = function () { var static = /(^o^)/; return ++static.i || (static.i=0); }; こうすれば差は縮まる http://la.ma.la/blog/diary_200705301141.htm
とブックマークコメントを貰った。
counter3としてベンチに追加した所、結果が変わった
if (typeof console == 'undefined'{
  if (typeof print == 'function') console = {log:print};
  else if (typeof WScript != 'undefined') console = {log:function(s){WScript.StdOut.WriteLine(s)}};
  else console = {log:alert};
}

var counter1 = function () {
  var static = /(^o^)/;
  return ('i' in static)? ++static.i:
    static.i = 0;
};

var counter2 = (function() {
  var static = 0;
  return function() {return static++};
})();

var counter3 = function () {
  var static = /(^o^)/; return ++static.i || (static.i=0);
};

var start;
start = new Date().getTime();
for(var n = 0; n < 10000000; n++) counter1();
console.log("counter1:" + (new Date().getTime() - start))

start = new Date().getTime();
for(var n = 0; n < 10000000; n++) counter2();
console.log("counter2:" + (new Date().getTime() - start))

start = new Date().getTime();
for(var n = 0; n < 10000000; n++) counter3();
console.log("counter3:" + (new Date().getTime() - start))
さらにベンチにwindows scripting hostも足してみた。

tracemonkey : JavaScript-C 1.8.0 pre-release 1 2007-10-03
counter1:1624
counter2:381
counter3:529

v8 : V8 version 0.3.4 (internal)
counter1:1610
counter2:403
counter3:665

cscript : Microsoft (R) Windows Script Host Version 5.6
counter1:107282
counter3:44016
counter2:51297

おぉぉぉぉぉぉぉぉぉぉ!!!!なんと
正規表現オブジェクトの方が速いではないか!!!
詳細はos0xさんか何方かが書いてくれるとして...
失礼しました!!!
上の結果が正しいです。

結論
windows scripting hostおせーーー!

Posted at by




追記

POSIX では明確にソケットの最大値とはうたってはいないものの、Linux の実装を見ても最大値と扱う方が良い様です。また Winsock では select(2) の第一引数は無視されるようです。

C言語でソケットを使うプログラミングを行う際、ソケットディスクリプタがシグナル状態かを調べる方法としてselect(2)があります。
使い方は int r;
fd_set rfds;

FD_ZERO(&rfds);
FD_SET(sock, &rfds);

r = select(1, &rfds, NULL, NULL, NULL);
といった感じ。ここでselect(2)の第一引数に渡している値は、ディスクリプタ集合rfdsの内、いくつ検証するかを指す値。つまりrfdsに対してFD_ZERO/FD_CLRしてからFD_SETした回数となります。
ちなみに戻り値は、ディスクリプタ集合の内どれだけシグナル状態かの数が返ります。つまり r = select(num_fds, &rfds, &wfds, &efds, NULL);
の場合、rfds/wfds/efdsの内、シグナル状態であるディスクリプタの総数が返ります。

昔のC言語で書かれたソースを見ると、よく int r;
fd_set rfds;

FD_ZERO(&rfds);
FD_SET(sock, &rfds);

r = select(FD_SETSIZE, &rfds, NULL, NULL, NULL);
FD_SETSIZEを使って書かれた記述を見ます。これはrfdsが構造体であり、そのメンバに持つディスクリプタ格納配列fd_arrayFD_SETSIZEでサイズ定義されている事を利用している為です。
これを無駄と考える人がいた為か、こういう記述も未だに良く見かけます。
int r;
fd_set rfds;

FD_ZERO(&rfds);
FD_SET(sock1, &rfds);
FD_SET(sock2, &rfds);
int check_sock = max(sock1, sock2);

r = select(max(sock1, sock2), &rfds, NULL, NULL, NULL);
確かにFD_SETSIZEの理屈から言えば納得が行く話かも知れませんが、FD_SETSIZEの定義とはfd_arrayの個数でありディスクリプタの値が取り得る値の最大値ではありません。ディスクリプタが順列で生成されるなんて仕様もありません。
つまり、間違いです。

にも関わらず、ディスクリプタの最大値はFD_SETSIZEだと思わせる記述が出回ってしまったのだと思います。
Manpage of SELECT_TUT

nfds: 全ての集合に含まれるファイルディスクリプタのうち、値が最大のものに 1 を足した整数である。すなわち、ファイルディスクリプタを集合に加える作業の途中で、全てのファイルディスクリプタを見て最大値を求め、それに 1 を加えて nfds として select に渡さないといけない、ということだ。

http://www.linux.or.jp/JM/html/LDP_man-pages/man2/select_tut.2.html
おそらく初期のディスクリプタの実装が配列の添え字であった為、こういう記述となり残っていったのだと思う。

例えばWindowsで言えば、socket(7)関数で返るディスクリプタの値はFD_SETSIZEに収まらない値で返ります。またMinGW(Minimalist GNU for Windows)のwinsock.hで定義されているFD_SETSIZEは64と中途半端な値になっています。
それはなぜか...
適当だからです。

通常、1プロセスが扱えるディスクリプタの数が設定されるべきですが、それが明確に定義すべきでない環境では意味のない値になったのだと思います。
しかしながら、多数のディスクリプタ(ファイルもソケットも)を扱うプログラムならば64個使い切ってしまう事はあり得りえるでしょうね。
ではどうすれば良いか。FD_SETSIZEfd_arrayの個数を定義するマクロであり、FD_ZERO/FD_CLR/FD_SETでそれを操作する再に用いられる閾値であり、ループ回数なのです。さらにFD_ZERO/FD_CLR/FD_SETFD_SETSIZEと同じくマクロなのです。 #define FD_CLR(fd,set) do { u_int __i;\
for (__i = 0; __i < ((fd_set *)(set))->fd_count ; __i++) {\
       if (((fd_set *)(set))->fd_array[__i] == (fd)) {\
       while (__i < ((fd_set *)(set))->fd_count-1) {\
               ((fd_set*)(set))->fd_array[__i] = ((fd_set*)(set))->fd_array[__i+1];\
               __i++;\
       }\
       ((fd_set*)(set))->fd_count--;\
       break;\
       }\
}\
} while (0)
しかもFD_SETSIZEの定義は#ifdefにより使い手側が変更出来る様になっています。もし通常よりも多くディスクリプタを扱いたいならばコンパイル時にFD_SETSIZEを定義してやれば良いのです。
この事は、MSDNにも書いてあります。
select Function (Windows) http://msdn.microsoft.com/en-us/library/ms740141.aspx

Four macros are defined in the header file Winsock2.h for manipulating and checking the descriptor sets. The variable FD_SETSIZE determines the maximum number of descriptors in a set. (The default value of FD_SETSIZE is 64, which can be modified by defining FD_SETSIZE to another value before including Winsock2.h.)

ディスクリプタ集合をを操作/チェックするためにヘッダーファイルWinsock2.hに4つのマクロが定義されています。FD_SETSIZEはディスクリプタ集合の最大個数を記述子の最大数を決定します。 (FD_SETSIZEの初期値は64です。これはwinsock2.hをインクルードする前に別の値で変更する事が出来ます。)


但し、このFD_SETSIZEが少ない値のままコンパイルされたライブラリと、多く設定した値のライブラリを併用すると場合によっては誤動作する可能性があるので注意が必要です。まぁこれはUNIXでも同じ話ですね。

だらだら書きましたが、何を言いたいかというと

UNIXで開発していて、将来的にWindowsにも移植するかもしれないソフトウェアならば、ディスクリプタの値がディスクリプタ集合の最大個数である...といった様なコーディングは辞めましょう。

という事です。
Posted at by