IRCの%rubyでeveryが話題になってたので作ってみました。
Arrayで実装としては[ruby-list:20933]や[ruby-list:20932]がありました。
everyでArrayを返すとArray#collectが使われてあんまりうれしくないのと、ArrayのArrayを作るのにコストがかかりそう&最初にeachが終わるのを待たないといけない、ということがあって独自のクラスを返すようにしてみました。
module Enumerable
class Every
include Enumerable
def initialize(enum, n)
@enum = enum
@n = n
end
def each
ary = []
@enum.each do |i|
ary << i
if @n == ary.size
yield(ary)
ary = []
end
end
unless ary.empty?
ary[@n-1] = nil
yield(ary)
end
end
end
def every(n)
raise ArgumentError, "out of range" if n <= 0
Every.new(self, n)
end
end
[1,2,3,4,5,6,7,8,9,10].every(4).each { |x| p x }
#=>[1, 2, 3, 4], [5, 6, 7, 8] ,[9, 10, nil, nil]
p [1,2,3,4,5,6,7,8,9,10].every(3).collect { |x| x.reverse }
#=>[[3, 2, 1], [6, 5, 4], [9, 8, 7], [nil, nil, 10]]
p [1,2,3,4,5,6,7,8,9,10].every(2).collect { |x,y| x+y }
#=>[3, 7, 11, 15, 19]
これを作ってる時に経験したバグ。
yield(ary)の後ary=[]ではなくary.clearとしていた。
最後のary.clearのバグは、eachから見るとyieldにあげちゃったオブジェクトを使い回そうとしたのがいけなかった。yieldで呼ばれるブロックから見ると、前に受け取ったブロック引数をどっかにとっておいたらいつの間にか変わっていておかしなことになる。
C言語版をknuさんが作ってましたが、同じはまったそうです。
yieldに渡したものを後で変更すると変なことになる、というのは落とし穴かもしれません。
[ruby-dev:9930]のあたりのスレッド参照。適当にまとめると以下の通り。
proc{}.arity == proc{|*i|}.arity == -1
[ruby-list:24985]にはないのに[ruby-list:24986]にはto_aryのことが書いてある。
COM Meets Rubyからダウンロード。
拡張子がbinなのがかなり謎。msiっていうのがダメならlzhとかzipとかcabとかでmsiファイルをアーカイブすればいいのに。
ってここに書いても変わらなかったら直メールはあんまり好きじゃないけどメールするかも。
[ruby-list:25325]の「だけど、メーリングリストのアーカイブから検索してやっとみつかる(かもしれない)のをFAQと言われてもなぁ。」をみて思ったのですが、FAQって何度もきかれているからFAQであってQ&A集としてまとめられている必要はないはず。
だからできたばかりのところでQ&A集をFAQという名前で公開していたらできたばかりなのにそんなに質問されてますか:-)とかかかれてしまうと思うのです。
ActiveScriptRubyを入れた影響でrbのアイコンが変わっていた。 ActivePerlの小さくて黄色いのに比べて大きくていい感じ。
ターミナルが化けるのでコントロールコードの除去をしようとした時の試行錯誤の記録。
str.tr('\x00-\x1F\x7F', '') でうまくいかずに悩む。
str.gsub('[\x00-\x1F\x7F]', '') に直すとうまくいったのでtrのバグ?とか思ってしまう。
str.tr_s('\x00-\x1F\x7F', ' ') とか試してみるがやっぱりうまくいかず。
perlでのtrの動作確認をしてみる。思った通りの動作をする。
for (0..0x7F) {
$a .= chr($_);
}
$a =~ tr/\x00-\x1F\x7F/ /s;
print "[[$a]]";
''を""にすればいいことに気づく。質問した直後に解決するというのは結構ありがち:-)
""に直したらtr_sからtrに戻してもいいことに気がついたので戻す。
最終的にはこれで動作確認をした。
a=(0x00..0x7F).collect{|i|i.chr}.join('').gsub('[\x00-\x1F\x7F]', '')
b=(0x00..0x7F).collect{|i|i.chr}.join('').tr("\x00-\x1F\x7F", '')
puts "[[#{a}]],[[#{b}]],#{a==b}"
gsubの第一引数は文字列を渡すと正規表現にコンパイルされるのでそこで\xの処理がされているみたい。
ここの9500踏む。
index.htmを更新しないと更新と見なされないみたい。
Ruby 1.6.1までのThreadってバグ多いかも。
ruby-formulaにequal?の説明を追加した。
安全にevalするために専用のスレッドを使って$SAFE = 4で やってみました。
すると同じメソッド内部のローカル変数(以下の例だとresultなど)が いじれてしまったので、別メソッドでbindingを作るようにしました。
それでもexitの代わりにraise SystemExitで終了できてしまったり raise SignalExceptionで終了できたりしてしまったので、 ScriptError, StandardErrorだったのをExceptionに変更しました。
これで安全なのでしょうか?
Thread.abort_on_exception = true
s = ARGV.shift
class Eval
def sandbox
@binding = binding
self
end
def evaluate(s)
binding = @binding
result = ''
th = Thread.start do
$SAFE = 4
begin
result = eval(s, binding).to_s
rescue Exception
result = $!.to_s
end
end
th.join
puts result
self
end
end
Thread.start do
e = Eval.new
e.sandbox
e.evaluate(s)
end
gets
<URL:http://www.miti.go.jp/kohosys/press/0001021/2/1012it563.html>の一番下にRubyが。 それから一番上にAirWebが。 他に知ってた人はやまざき@BinaryTechnologyの山崎さんかな。
//mと//sの説明が紛らわしい。
修飾子 意味 m 文字列を複数行として扱う s 文字列を単一行として扱う プログラミングPerl改訂版 79ページ より
似たような説明だから逆の意味かと思っていたら全く関係なかった。
修飾子 意味 m ^ $ が改行にマッチするようになる。 s . が改行にマッチするようになる。
ということでそれぞれ影響するものが違います。 ちなみに逆の意味なのは(?-m)や(?-s:...)みたいに-をつけたもの。
PStore#abort(@db.abort)のつもりでObject#abort(abort)を使ってしまって、謎の突然終了で悩んでしまった。
行の途中までしか送られてこないことがあるので以下のようなループで行毎にしているが、/pはobsoleteなのでどうにかしたい。
rest = ''
@nif.waitfor({"Prompt" => @options["Prompt"],
"Timeout" => timeout}){|c|
yield c if iterator?
if rest+c =~ /^(.*\n)([^\n]*)$/p
rest = $2
$1.each_line {|line|
received(line)
}
else
rest.concat c
end
}
昨日%rubyでなぜかperlの話が盛り上がっていた。
<URL:http://pluto.im.uec.ac.jp:1800/~sekita-n/jdocs/perl2ruby.html>の「($A[5][1] は実際には $A->[5]->[1]の略記法)」が間違っている。
% perl -e '$a = [[1, 2], [3, 4]]; print $a[0][0], "\n";' % perl -e '$a = [[1, 2], [3, 4]]; print $a->[0][0], "\n";' 1 % perl -e '@a = ([1, 2], [3, 4]); print $a[0][0], "\n";' 1 % ->が省略できるのは[][]の様な括弧の間のところだけということを再確認。 それからv5.6.0では[]->()も->が省略できると言うことを知った。 5.005_03だと省略できなかったのでforeach修飾子と同じでまだ汎用のスクリプトで使うのには向かないと思う。
IRCの%rubyより。 1.4と1.6の両方で使えるようにするにはrequire 'final'すればいいらしい。 1.6ではfinal.rbは空になっている。
Rubyの落とし穴をまとめてみた。
mp3lsのネタをいただいてRubyにしてみる。 mp3ls.cは"r"で開いてますが、バイナリモードで開かなくても大丈夫なんでしょうか? とりあえずパクリ元のcopyrightも入れておこう。
#!/usr/bin/ruby
=begin
= copyright
== ruby version copyright
copyright (c) 2000 Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
== original C version copyright
/*
* mp3ls - printing ID3 of MPEG1 Audio Layer-3 file
* Version 0.0.2
* Copyright (C) 2000 Yoichi Imai <yoichi@silver-forest.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* http://www.gnu.org/copyleft/gpl.html
*/
=end
require 'nkf'
nkf_arg = '-e'
ARGV.each do |filename|
File.open(filename, 'rb') do |f|
f.seek(-128, IO::SEEK_END)
id3 = f.read(128)
tag, title, artist, album, year, comment, genre =
id3.unpack('Z3 Z30 Z30 Z30 Z4 Z30 C')
title = NKF.nkf(nkf_arg, title.strip)
artist = NKF.nkf(nkf_arg, artist.strip)
puts "#{filename} / #{title} / #{artist}"
end
end
<URL:http://members.linuxstart.com/~sunnyone/cgi-bin/diary/?200010b&to=200010194#200010194>
Windowsとかでの動作は全く考えてませんでしたか。 RubyならWindowsでも…と思ったけどnkf_argはいちいち書き換えないとダメか。 書き換えやすいように2つあったのをまとめて上に持っていったわけだけど。
return unless defined? Hoge && Hoge::Flag[0]という感じのを書いたら、
return unless defined?(Hoge && Hoge::Flag[0])と解釈されてで常に真("expression")になってしまっていたので、
return unless defined?(Hoge) && Hoge::Flag[0]になおした。
最初から&&ではなくandなら大丈夫だったみたい。
while str.gsub!(/.\cH/n, '');endとしていたら連続した"\cH"が消えてしまって変になっていたのでwhile str.sub!(/.\cH/n, '');endに修正した。
rd2がshebang(ファイルの最初の#!の行)に対応したら便利かも、とふと思った。
CVSでとってきたのを自分でmakeしたrubyでpeeraddrが変だったので、ruby -v -rsocket -e 'port=12121;p TCPServer.open(port).accept.peeraddr'&sleep 1;telnet localhost 12121って感じでいろんなので試してみました。
最初の行はDebian/GNU Linux 2.2 (potato)でmakeしたもの。 2行目以降はVine Linux 2.0標準のgcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)でmakeしたもの。 VinePlusのrpmのruby.specを参考にしてconfigureにオプションを追加していったところ以下のような結果に。 xxxxのところは毎回変わるポート番号。
["AF_INET", xxxx, "localhost", "127.0.0.1"] ruby 1.6.2 (2000-10-21) [i586-linux] ["unknown:10", xxxx, "localhost", "::ffff:127.0.0.1"] configure --prefix=/tmp/$USERのみ [false, xxxx, "localhost", "::ffff:127.0.0.1"] 同1.4.6 ["AF_INET6", xxxx, "localhost", "::ffff:127.0.0.1"] --enable-ipv6を追加 ["unknown:10", xxxx, "localhost", "::ffff:127.0.0.1"] --with-lookup-order-hack=INETを追加 ["AF_INET", xxxx, "localhost.localdomain", "127.0.0.1"] --enable-ipv6と--with-lookup-order-hack=INETを追加
結局--enable-ipv6と--with-lookup-order-hack=INETがあれば正常になるということがわかりました。
<URL:http://kirara.prec.kyoto-u.ac.jp/~tam/diary/#10-24:2>
bang methodは変更しなかったときにnilを返す、っていうのも落とし穴候補かも。
while str.gsub!(/(\d+)(\d{3})/){"$1,$2"};endみたいなことをするときには便利な仕様なんだけど、メソッドチェーンをするのには向かないなぁ。
ちなみにsortもuniqも実行時間は要素数に比例するので、sort.uniqよりもuniq.sortの方が速いです。
ソート済みを前提としたuniqを別に用意するとか、いっそのことsort_uniqなんて作ってみると便利かもしれない。
今日が発売日のはず。 とりあえず途中まで読んだのでerrataっぽいのを書いてみた。
<URL:http://www2.pos.to/~tosh/ruby/diary/2000/10/c.html#sec25_6>
ファイル「hoge」の1行目が「#!foo bar」だったら「hoge arg」で「foo bar hoge arg」が起動されると思います。 対応というのは「#!/usr/bin/ruby -S rd2 -someoption」とか書いてたら-someoptionをコマンドラインに指定されたのと同じように解釈してほしいと言うことだったのですが、実際に実行可能と言うことまで考えるとhogeとargの区別(./hoge argのつもりで起動されたのかrd2 hoge.rd arg.rdのつもりで起動されたのか)が難しいかも。
<URL:http://www.weatherlight.org/~crouton/journal/?200010C#200010271>
昔はダウンロードキャプターさくらという名前だったけど正式名称がver.1.50からDCさくらになったはず。
<URL:http://member.nifty.ne.jp/phinloda/index.htm>の2000年10月28日の 「perlで $foo{key1}{key2} を配列とみなして push する方法というくだらない所でひっかかっていたが、聞いてみたら簡単だったのでショック。」
Perlのリファレンスってほとんど使ったことないので勘違いしてるかもしれませんが、基本的にはスカラーとして扱うときは$hoge{foo}[bar]みたいに後ろに括弧をつけていくだけで良くて、配列とかハッシュとして扱いたいときは@{$hoge[foo]{bar}}みたいに全体を{}でくくって前に@や%をつければ良いと理解しているんですが。
str.tr("\x00-\x1F\x7F", '')ってやってたのをstr.delete("\x00-\x1F\x7F")に修正。
SymbolからStringへの変換はSymbol#id2name。StringからSymbolへの変換はString#intern。
%wと%q、%Wと%Qという感じで%Wっていうのも使えるようにしたら便利かも、と思ったのですが、エスケープがややこしくなるのであんまり便利じゃないかも。