天泣記

2005/02/01

#1
% google-count 予想の{みぎ,右,ひだり,左}{ななめ,斜め}{うえ,上}
0       予想のみぎななめうえ
0       予想のみぎななめ上
0       予想のみぎ斜めうえ
0       予想のみぎ斜め上
0       予想の右ななめうえ
0       予想の右ななめ上
0       予想の右斜めうえ
186     予想の右斜め上
0       予想のひだりななめうえ
0       予想のひだりななめ上
0       予想のひだり斜めうえ
0       予想のひだり斜め上
0       予想の左ななめうえ
0       予想の左ななめ上
0       予想の左斜めうえ
4       予想の左斜め上

2005/02/02

#1

昨日は boron が重くて、設定した時間内に autobuild が終らなかった。

1.9, 1.8

設定時間は 30分で、1.9 はコンパイルの途中までいっているが、1.8 は cvs checkout で終っている。

なんか、最初の SIGINT ですぐに死んでないし。 タイムアウトすると SIGINT, SIGINT, SIGTERM, SIGKILL と 5秒間隔で送るのだが、SIGTERM を送るまでは生き残っていたようだ。

#2

commit はいつ行われるか。

% cvs log ChangeLog |
ruby -rtime -e '
h = Hash.new(0)
ARGF.each {|line| h[Time.parse($1+" UTC").getlocal.hour]+=1 if %r{^date: ([0-9/ :]+);} =~ line }
h.keys.sort.each {|k| puts "#{k}\t#{%q{*} * (h[k]/10.0)}" }'
0       ********************************
1       *************************
2       *******************
3       **************
4       **********
5       *******
6       *****
7       ******
8       *********
9       *************
10      ***************
11      *************************
12      ***********************
13      *******************************
14      **************************************
15      *************************************
16      ****************************************
17      *********************************************
18      ********************************************
19      ****************************
20      *********************
21      ******************
22      **********************
23      ***********************************

夕方と真夜中にピークがある。

2005/02/03

#1

すこし java.nio を調べる。

2005/02/06

#1

「潅仏会」を検索してみると、陰暦4月8日だそうである。

ふむ。陰暦ねぇ。

2005/02/09

#1

〆切 (1)

〆切 (2)

2005/02/10

#1
% google-count プラトニック 多面体       
64500   プラトニック
57700   多面体
% google-count --words プラトニック 多面体 
28      プラトニック 多面体

2005/02/11

#1

ftp proxy をちょっと調べてみる。

http でやるやつじゃなくて、ftp の user に user@host と指定するやつである。

サポートしているサーバには、少なくとも TIS FWTK, ftp.proxy, frox, delegate がある模様。

また、(意識して) サポートしているクライアントは、 fetch, apt がある模様。

どこで始まったのだろう?

#2

https_proxy=https://... というのを検索するといくつか記述が見つかる。 これはどう解釈すべきか?

2005/02/12

#1

core を吐いている

(gdb) up
#1  0x40099f12 in abort () from /lib/tls/libc.so.6
(gdb) 
#2  0x080d6a16 in rb_bug (fmt=0x0) at error.c:214
214         abort();
(gdb) 
#3  0x080b37a2 in sigsegv (sig=11) at signal.c:446
446         rb_bug("Segmentation fault");
(gdb) 
#4  <signal handler called>
(gdb) 
#5  type_cclass_hash (key=0x955afe8) at regparse.c:4537
4537        val = val * 997 + (int )*p++;
(gdb) p p
$1 = (unsigned char *) 0x955b000 <Address 0x955b000 out of bounds>
(gdb) l
4532
4533      val = 0;
4534
4535      p = (unsigned char* )&(key->enc);
4536      for (i = 0; i < sizeof(OnigEncodingType); i++) {
4537        val = val * 997 + (int )*p++;
4538      }
4539
4540      p = (unsigned char* )(&key->type);
4541      for (i = 0; i < sizeof(int); i++) {
(gdb) p key
$2 = (type_cclass_key *) 0x955afe8
(gdb) p *key
$3 = {enc = 0x0, not = 0, type = 0}

oniguruma の中というのは見かけた覚えがない。

まぁ、それはそれとして、自動的に backtrace をとるのはちゃんと動いていてなかなかよろしい。

2005/02/14

#1

CPS で I/O をやるとどうなるか書いてみたくなる。

まず、ループのかわりに再帰を使うので、 tailcall をまともに扱えないといけない。 というわけでまずはそういうのを書く。

def tailcall_start(&starting_block)
  catch(:finish_tailcall_loop) {
    answer = lambda {|val| throw :finish_tailcall_loop, val }
    callable = lambda { starting_block.call(answer) }
    loop {
      callable = catch(:tailcall) { answer.call(callable.call) }
    }
  }
end

def tailcall(cont, arg)
  throw :tailcall, lambda { cont.call arg }
end

これだけであるが、そこそこ動く。 まぁ、Scheme を使えばここはいらないわけだが。

とりあえず目標を copy_stream とすると、readpartial と write の CPS 版が必要なので書く。

class IO
  def readpartial_cps(len, &k)
    begin
      str = self.readpartial(len)
    rescue EOFError
      tailcall(k, nil)
    end
    tailcall(k, str)
  end

  def write_cps(str, &k)
    self.write str
    tailcall(k, nil)
  end
end

CPS だと組み込みの例外がうまく扱えない。 例外は外側に伝播するが、CPS だと外側は存在しないのでうまくない。 ここでは readpartial が生成する EOFError をどうするか悩む。 複数の継続を渡すのが定番なのだが、Ruby のブロックを使って渡すので複数渡すのはやりにくい。 なので今回は EOF を nil で表現することにする。

ここまでできると copy_stream が書ける。

def copy_stream(r, w, &k)
  r.readpartial_cps(4096) {|str|
    if str
      w.write_cps(str) {
        copy_stream(r, w, &k)
      }
    else
      tailcall(k, nil)
    end
  }
end

readpartial_cps 内でせっかく制御の流れが分かれているのを混ぜた結果、 継続内で分岐が必要になっていて悲しい。 が、それは気にしないことにすれば、 とくに問題なく動くようだ。

tailcall_start {|k|
  copy_stream(STDIN, STDOUT, &k)
}
#2

次に、I/O 待ちの間に他の作業が出来るよう、マルチスレッドっぽいことをしてみる。 プログラムの流れは tailcall_start 内のループで実現されているので、 そこで複数の継続を扱って、順番に進めていけばいい。

def tailcall_start(&starting_block)
  catch(:finish_tailcall_loop) {
    answer = lambda {|val| throw :finish_tailcall_loop, val }
    Thread.current[:tailcall_queue] = queue = []
    queue << lambda { starting_block.call(answer) }
    loop {
      break if queue.empty?
      o = catch(:tailcall) { answer.call(queue.shift.call) }
      queue << o if o
    }
  }
end
    
def tailcall(cont, arg)
  throw :tailcall, lambda { cont.call arg }
end

def tailcall_fork(&block)
  Thread.current[:tailcall_queue] << lambda {
    finish = lambda {|val| tailcall_thread_finish }
    block.call(finish)
    tailcall_thread_finish
  }
end

def tailcall_thread_finish
  throw :tailcall, nil
end

継続を保持するキューに余計に追加するのが thread の生成となり、 継続が進んだときに次の作業を登録しないのが thread の終了になる。

今度は双方向通信を書くのを目標とする。 readpartial と write で I/O が即座に出来ないときには 他のスレッドに作業を回す必要があるのでそうする。 実用品は目指さないことにして busy loop でやるとすれば、 select して I/O 待ちが入るようならば他のスレッドに回せば良い。

class IO
  def readpartial_cps(len, &k)
    waitread_cps {
      begin
        str = self.readpartial(len)
      rescue EOFError
        tailcall(k, nil)
      end
      tailcall(k, str)
    }
  end

  def write_cps(str, &k)
    waitwrite_cps {
      self.write str
      tailcall(k, nil)
    }
  end

  def waitread_cps(&k)
    r,_,_ = IO.select([self],nil,nil,0)
    if r
      k.call
    else
      tailcall(lambda {|v| waitread_cps(&k) }, nil)
    end
  end

  def waitwrite_cps(&k)
    _,w,_ = IO.select(nil,[self],nil,0)
    if w
      k.call
    else
      tailcall(lambda {|v| waitwrite_cps(&k) }, nil)
    end
  end
end

で、双方向に転送するのは次のようになる。

def copy_duplex_stream(s1, s2, &k)
  tailcall_fork {|k2|
    copy_stream(s2, s1, &k2)
  }
  copy_stream(s1, s2, &k)
end

そうすると、次のようにして telnet っぽいことが出来る。

TCPSocket.open("localhost", 9001) {|sock|
  tailcall_start {|k|
    copy_duplex_stream2(sock, open("/dev/tty", "r+"), &k)      
  }
}

ただ、copy_duplex_stream で、s1->s2 と s2->s1 が非対称なのは美しくない。 こうなっていると、s1 が EOF になると k が起動し、 上記の telnet もどきのように k に tailcall_start を終了させる継続が入っている場合には、 s2->s1 の転送も含め全体が終了する。 それに対し、s2 が EOF になると k2 が起動するが、 これはそのスレッドだけが終了する継続なので、 s1->s2 の転送はそのまま続く。

まぁ、telnet のような用途に関しては、この非対称性を一概に悪いものと片付けるわけにはいかないけれど。

2005/02/18

#1

SERVER_NAME というのは Host: がそのまま渡されるものだと気がつく。

2005/02/19

#1

webapp に server mode をつけてみる。

% cat hello.cgi 
#!/usr/bin/env ruby
require 'webapp'
WebApp {|webapp|
  webapp.puts "Hello World."
}
% ./hello.cgi server
http://serein:40805/
[2005-02-19 12:41:54] INFO  WEBrick 1.3.1
[2005-02-19 12:41:54] INFO  ruby 1.9.0 (2005-02-17) [i686-linux]
[2005-02-19 12:41:54] WARN  TCPServer Error: Address family not supported by protocol - socket(2)
[2005-02-19 12:41:54] INFO  WEBrick::HTTPServer#start: pid=11309 port=40805
...

port は (指定されなければ) 空いているのを適当に選ぶようにしたが、 固定とどちらが良かっただろうか。

#2

open-uri で https が扱えるようになったので、五月雨でも https を扱えるようになった。

そこで試しになにか登録してみようかと思ったが、読みたいページが思い浮かばない。

2005/02/20

#1

最近、Ruby 1.9 は微妙に不安定なわけだが、 rb_gc の呼び出しを加えずに test-all をループさせて、どのくらいの率で落ちるのかを調べてみた。

調べたところ、1000回ループさせて 83回 BUG となったので、約8% の率で落ちることが分かった。

ところで、Ruby hotlinks 五月雨版には、1.9 が 3つ登録されているので、 ある時点でアンテナを見てこの件での BUG が表示されている率は 1-(1-83/1000)**3 で約23% となる、かもしれない。 まぁ、意外な数値ではない。

2005/02/21

#1
% while :
do
 date
 sleep 60
done
Sun Feb 20 20:49:35 JST 2005
Sun Feb 20 20:50:37 JST 2005
Sun Feb 20 20:51:40 JST 2005
Sun Feb 20 20:52:42 JST 2005
Sun Feb 20 20:53:45 JST 2005
Sun Feb 20 20:54:47 JST 2005
Sun Feb 20 20:55:50 JST 2005
Sun Feb 20 20:56:52 JST 2005
Sun Feb 20 20:57:55 JST 2005
Sun Feb 20 20:58:57 JST 2005
Sun Feb 20 21:00:00 JST 2005
Sun Feb 20 21:01:02 JST 2005
Sun Feb 20 21:02:04 JST 2005
Sun Feb 20 21:03:07 JST 2005

これは date と sleep 60 をループした結果である。 date の出力をみると、ループのひとまわりに 62〜63秒かかっているように見える。

これは date とシェルのオーバーヘッドが 2〜3秒ある... わけではなく、 じつは、ntpd が時計を進めている最中なのである。

sleep は時計の調整には影響されないのか。

#2

autobuild で結果をまとめるページを生成するようにしてみた。

http://www.rubyist.net/~akr/ab/openbsd-3.6/ruby-trunk/summary.html

毎回一行追加される、はず。

全部再生成するか追記だけで済ますかは悩んだのだが、 とりあえず追記で済ますことにした。

2005/02/23

#1

一日一回だと間隔が開きすぎると感じたので、 boron の autobuild は commit され (た後に 5分 commit がなかっ) たら build するようにしてみた。

http://www.rubyist.net/~akr/autobuild/

2005/02/25

#1

帰りがけに考えて、 興味深いことに気がついた。

x = DBL_MIN, y = (2^31)/(1+DBL_EPSILON) という場合を考えると、 乱数で決まるビットの範囲は DBL_MANT_DIG ビットをはるかに越えるのである。

典型的には x が 2^(-1021) のあたり、y が 2^31 のあたりなので、 大雑把にいっても 31+1021=1052ビットくらいの範囲を持っているわけで、 DBL_MANT_DIG = 53 ビットをはるかに越えている。

考えてみると、実数の乱数を生成するには、 理想的には無限に下位のビットまで乱数で生成しなければならない。 そこを最終的な結果であるところの浮動小数点数で表現できるところまで求めると考えれば、 そういうことがあっても変ではない。

IEEE754 倍精度と Ruby 1.8.3 以降を仮定して生真面目に書くとこんな感じか。

def decode_float(f)
  s = [f].pack("G").unpack("B*")[0]
  sign = s[0,1] == '0' ? "+" : "-"
  exp = Integer("0b#{s[1,11]}")
  mant = s[12,52]
  if exp == 2047
    if /\A0*\z/ !~ mant
      raise ArgumentError, "NaN"
    else
      raise ArgumentError, "Inf"
    end
  elsif exp == 0
    mant = "0#{mant}"
    exp = exp - 1022 - 52
  else
    mant = "1#{mant}"
    exp = exp - 1023 - 52
  end
  # "#{sign}0b#{mant}p#{exp}"
  [sign, exp, mant]
end

def rand_float_closed_range(x,y)
  _, xexp, xmant = decode_float(x)
  _, yexp, ymant = decode_float(y)

  exp = xexp
  ymant << "0" * (yexp - xexp)

  xmant = Integer("0b#{xmant}")
  ymant = Integer("0b#{ymant}")
  r = rand(ymant-xmant+1) + xmant
  r = r.to_s(2)
  r.sub!(/\A0*/, '')

  if r.length < 53
    exp -= 53-r.length
    r << ("0" * (53-r.length))
  end

  exp = exp + r.length - 53
  r = r[0,53]

  exp = exp + 1023 + 52
  if exp <= 0
    r = "0" * (1-exp) + r[0...(exp-1)]
    exp = 0
  end

  exp = exp.to_s(2)
  exp = "0" * (11-exp.length) + exp
  s = "0#{exp}#{r[1,52]}"
  [s].pack("B*").unpack("G")[0]
end

x = Float::MIN
y = (2**31)/(1+Float::EPSILON)
p rand_float_closed_range(x,y)

いろいろと面倒臭いが、本質的には x の桁にあわせてから整数で rand しているだけである。

rand の結果は上位 53ビットしかいらないので、乱数を余計に求めることがあるのが気になるところではある。

あとは、Math.frexp や Math.ldexp がうまく使えないかということと、 x+(y-x)*rand(1<<n)/((1<<n).to_f-1) でまずい場合は本当に存在するのか、というのが疑問か。

2005/02/26

#1

思うに、こういうことを正確にやるときには結局整数で計算するのが確実なのだが、 浮動小数点数と整数 (仮数部と指数部の対) を変換するメソッドが標準にないのがいけない気がする。 frexp と ldexp はそれなりにいいところをついているのだが、整数にはしてくれないのでまだきつい。 C の標準にそういう関数がないのは、仮数部を表現可能な整数型が存在することが保証されないのでしょうがないと思うのだが、 Ruby には Bignum があるのでその問題はない。 frexp, ldexp, FLT_RADIX, DBL_MANT_DIG あたりを使えばポータブルに実装できそうな気もする。 (名前を除いて) 他に問題はあるだろうか。

非正規数をどう扱うかが問題か。 frexp してから FLT_RADIX**DBL_MANT_DIG 倍して整数にすると、 下のほうの桁は浮動小数点数で対応するところがないものになってしまう。 整数の世界でいろいろといじくり回してから浮動小数点数に戻したいのでこれは面白くない。 DBL_MIN_EXP も考慮すればいいか?

#2

このくらいで出来るようだ。

class Float
  def decompose3
    if !self.finite?
      raise ArgumentError, "not a finite float: #{self.inspect}"
    end
    if 0 < self
      sign = 1
      f = self
    elsif self < 0
      sign = -1
      f = -self
    else
      if 0 < 1.0/self
        return 1, 0, 0
      else
        return -1, 0, 0
      end
    end
    f, e = Math.frexp(f)
    bits = Float::MANT_DIG
    bits -= Float::MIN_EXP-e if e < Float::MIN_EXP
    f = Math.ldexp(f, bits)
    e -= bits
    [sign, f.to_i, e]
  end
 
  def Float.compose3(sign, mantissa, exponent)
    max = 1 << Float::MANT_DIG
    while max <= mantissa
      mantissa >>= 1 # xxx: round to zero
      exponent += 1
    end
    sign * Math.ldexp(mantissa, exponent)
  end
end

def rand_float_closed_range(x,y)
  _, xmant, xexp = x.to_f.decompose3
  _, ymant, yexp = y.to_f.decompose3
  ymant <<= (yexp - xexp)
  r = rand(ymant-xmant+1) + xmant
  Float.compose3(1, r, xexp)
end

Float.compose3 内の while ループが気に入らない。 Integer でビット数を求めるのがないかと探してみたが、ない感じである。 size がそんな感じである気もするが、これは単位が粗くてあまり使えない。

#3

出張の前日に本屋にいき、新刊を見つけたとしよう。

  1. それは好運である。出張中に買い逃さなくて済んだのだから。
  2. それは不運である。準備時間がとられるのだから。
  3. それは好運である。移動中に読むものが出来たのだから。
  4. それは不運である。荷物が重くなるのだから。
  5. それは好運である。いずれにせよ読みたい本があるのは。

2005/02/27

#1

出張 1日目

#2

吉野屋で牛丼を食べた

2005/02/28

#1

出張 2日目

#2

タイムゾーンを +8 に変える、という目的に対し、 滞在中の都市名が /usr/share/zoneinfo にない場合、どうしたらいいか?

その都市と同じタイムゾーンな他の都市を選べばいいのだが、 どうやってその都市名を調べれば良いか?

#3

/usr/share/zoneinfo/zone.tab の説明には載っていたので、ここをみればある程度は分かるか。

#4

アンテナでの時差の扱いに関して考える。

アンテナで表示する時刻はアンテナ (を運用している人) のタイムゾーンで表示すべきだろうか。 それとも、監視対象のページ (を運用している人) のタイムゾーンで表示すべきだろうか。


[latest]


田中哲