% google-count 予想の{みぎ,右,ひだり,左}{ななめ,斜め}{うえ,上}
0 予想のみぎななめうえ
0 予想のみぎななめ上
0 予想のみぎ斜めうえ
0 予想のみぎ斜め上
0 予想の右ななめうえ
0 予想の右ななめ上
0 予想の右斜めうえ
186 予想の右斜め上
0 予想のひだりななめうえ
0 予想のひだりななめ上
0 予想のひだり斜めうえ
0 予想のひだり斜め上
0 予想の左ななめうえ
0 予想の左ななめ上
0 予想の左斜めうえ
4 予想の左斜め上
昨日は boron が重くて、設定した時間内に autobuild が終らなかった。
設定時間は 30分で、1.9 はコンパイルの途中までいっているが、1.8 は cvs checkout で終っている。
なんか、最初の SIGINT ですぐに死んでないし。 タイムアウトすると SIGINT, SIGINT, SIGTERM, SIGKILL と 5秒間隔で送るのだが、SIGTERM を送るまでは生き残っていたようだ。
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 ***********************************
夕方と真夜中にピークがある。
すこし java.nio を調べる。
「潅仏会」を検索してみると、陰暦4月8日だそうである。
ふむ。陰暦ねぇ。
% google-count プラトニック 多面体 64500 プラトニック 57700 多面体 % google-count --words プラトニック 多面体 28 プラトニック 多面体
ftp proxy をちょっと調べてみる。
http でやるやつじゃなくて、ftp の user に user@host と指定するやつである。
サポートしているサーバには、少なくとも TIS FWTK, ftp.proxy, frox, delegate がある模様。
また、(意識して) サポートしているクライアントは、 fetch, apt がある模様。
どこで始まったのだろう?
https_proxy=https://... というのを検索するといくつか記述が見つかる。 これはどう解釈すべきか?
(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 をとるのはちゃんと動いていてなかなかよろしい。
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)
}
次に、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 のような用途に関しては、この非対称性を一概に悪いものと片付けるわけにはいかないけれど。
SERVER_NAME というのは Host: がそのまま渡されるものだと気がつく。
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 は (指定されなければ) 空いているのを適当に選ぶようにしたが、 固定とどちらが良かっただろうか。
open-uri で https が扱えるようになったので、五月雨でも https を扱えるようになった。
そこで試しになにか登録してみようかと思ったが、読みたいページが思い浮かばない。
最近、Ruby 1.9 は微妙に不安定なわけだが、 rb_gc の呼び出しを加えずに test-all をループさせて、どのくらいの率で落ちるのかを調べてみた。
調べたところ、1000回ループさせて 83回 BUG となったので、約8% の率で落ちることが分かった。
ところで、Ruby hotlinks 五月雨版には、1.9 が 3つ登録されているので、 ある時点でアンテナを見てこの件での BUG が表示されている率は 1-(1-83/1000)**3 で約23% となる、かもしれない。 まぁ、意外な数値ではない。
% 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 は時計の調整には影響されないのか。
autobuild で結果をまとめるページを生成するようにしてみた。
http://www.rubyist.net/~akr/ab/openbsd-3.6/ruby-trunk/summary.html
毎回一行追加される、はず。
全部再生成するか追記だけで済ますかは悩んだのだが、 とりあえず追記で済ますことにした。
一日一回だと間隔が開きすぎると感じたので、 boron の autobuild は commit され (た後に 5分 commit がなかっ) たら build するようにしてみた。
帰りがけに考えて、 興味深いことに気がついた。
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) でまずい場合は本当に存在するのか、というのが疑問か。
思うに、こういうことを正確にやるときには結局整数で計算するのが確実なのだが、 浮動小数点数と整数 (仮数部と指数部の対) を変換するメソッドが標準にないのがいけない気がする。 frexp と ldexp はそれなりにいいところをついているのだが、整数にはしてくれないのでまだきつい。 C の標準にそういう関数がないのは、仮数部を表現可能な整数型が存在することが保証されないのでしょうがないと思うのだが、 Ruby には Bignum があるのでその問題はない。 frexp, ldexp, FLT_RADIX, DBL_MANT_DIG あたりを使えばポータブルに実装できそうな気もする。 (名前を除いて) 他に問題はあるだろうか。
非正規数をどう扱うかが問題か。 frexp してから FLT_RADIX**DBL_MANT_DIG 倍して整数にすると、 下のほうの桁は浮動小数点数で対応するところがないものになってしまう。 整数の世界でいろいろといじくり回してから浮動小数点数に戻したいのでこれは面白くない。 DBL_MIN_EXP も考慮すればいいか?
このくらいで出来るようだ。
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 がそんな感じである気もするが、これは単位が粗くてあまり使えない。
出張の前日に本屋にいき、新刊を見つけたとしよう。
出張 1日目
吉野屋で牛丼を食べた
出張 2日目
タイムゾーンを +8 に変える、という目的に対し、 滞在中の都市名が /usr/share/zoneinfo にない場合、どうしたらいいか?
その都市と同じタイムゾーンな他の都市を選べばいいのだが、 どうやってその都市名を調べれば良いか?
/usr/share/zoneinfo/zone.tab の説明には載っていたので、ここをみればある程度は分かるか。
アンテナでの時差の扱いに関して考える。
アンテナで表示する時刻はアンテナ (を運用している人) のタイムゾーンで表示すべきだろうか。 それとも、監視対象のページ (を運用している人) のタイムゾーンで表示すべきだろうか。
[latest]