From: Minero Aoki Date: 2001-08-16T21:10:25+09:00 Subject: [ruby-list:30932] Re: [REQ] Regexp#match! あおきです。 In mail "[ruby-list:30927] Re: [REQ] Regexp#match!" matz@ruby-lang.org (Yukihiro Matsumoto) wrote: > まつもと ゆきひろです > > In message "[ruby-list:30926] Re: [REQ] Regexp#match!" > on 01/08/16, Hiromasa KONISHI writes: > > | Akinori MUSHAさんに一票 > > なかなか青木さんの願いは叶いませんねえ(苦笑)。 また連敗記録がのびるのだろうか…… いやしかし今回こそは勝つ。勝ちたい。勝てるといいなあ (弱気) > PythonではString#indexが見つからないときに例外(IndexError)を > 返しますし、全然駄目とまでは思いません。もっとも、個人的には > indexが例外を返すのはやっぱ変だろうとは思いますが。 > ということは、マッチが例外を返すのもやっぱ変なのか。^^;;; index は、ぼくも変かなあと思います。マッチでも match という 名前なら変だと思います。でもうまい名前があれば例外でもいい (or 例外のほうが便利) 場面はあるだろうと。 > | わざわざ例外を起こす理由がわからないのですが? > | コマンドラインの引数解析などが目的なのでしょうか? > > 青木さんが欲しいと思った局面を具体的に紹介すると良いかもです。 そーですねえ。or/unless ではダメな理由は、まず process re.match( str )[1] のように書きたい (ローカル変数を無駄に増やしたくない) という点 です。つまりマッチが目的でなくその結果の MatchData が目的で、 その結果を(入りくんだ) 式の中に使いたいのです。この場合の目的は 主に気持ちよさです。 また前提としてマッチすることを期待したい場面というのもあります。 たとえると Eiffel の precondition に re.match(arg) が書いてある ようなもので、マッチする文字列を用意するのはクライアント側の 責任である場合です。この場合にはマッチするかどうかはそもそも そのメソッドの責任の外にあると考えられるので、マッチしないのは まさに「例外」として扱ってほしいわけです。あるいは感覚的に言うと、 ゼロ除算をいちいちチェックしたくない、というのと一緒です。 (この論理で行くと NameError でもいいじゃないか、と言われそうですが それに対しては「じゃあなんで RegexpMissmatch ではいけないのか?」 と答えます。わざわざ名前も変えているわけだから、Ruby が知っている 範囲で詳細に答えてくれるぶんにはなにも問題はないはず。) ぼくの書いたものから拾ってきた具体例だと、match と raise を組み合わ せていてしかも MatchData を一回しか使わない例には以下のようなのが ありました。ついでに失敗した場合の責任の所在も書きました。 # net/http.rb HTTP サーバの出力をパース ……失敗の責任はたぶんライブラリ作者 (サーバが変なものを返す確率は少ないと思われるから) s.split(',').each do |spec| m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match( spec ) m or raise HTTPHeaderSyntaxError, "wrong Range: #{spec}" d1 = m[1].to_i d2 = m[2].to_i # 二回使ってるけど to_a すると一回 # net/pop.rb APOP のシグネチャを取る ……失敗の責任はライブラリユーザまたはエンドユーザ (普通の POP サーバに APOP でアクセスした) m = /<.+>/.match( rep.msg ) or raise ProtoAuthError, "not APOP server: cannot login" @stamp = m[0] end # refe/fbuilder.rb ruby のソースの一部から関数名を取りだす ……失敗の責任はプログラマ def get_fname( pre ) m = /\A([^(]+)/.match( pre[1] ) or m = /(\w+)\s*\(/.match( pre[0] ) or raise ArgumentError, "cannot get func name: #{pre[1]}" m[1] end # refe/mtof.rb rb_define_method からクラス/メソッド名/関数名をとりだす ……失敗の責任はプログラマ def getcvar( line ) m = /\((\w+)/.match( line ) or raise ArgumentError, "cannot find class name: #{line}" m[1] end def getmethod( line ) m = /"((?:[^"\\]+|\\.)*)"/.match( line ) or raise ArgumentError, "cannot find method name: #{line}" m[1] end def get_function_name( line ) m = /_method\(\w+,\s*"[^"\\]+",\s*(\w+),/.match( line ) or raise ArgumentError, "cannot find function name: #{line}" m[1] end # vi/mkcmdtab メタデータからコマンドテーブルを作る ……失敗の責任はプログラマ while (line = r.gets.chop) != '----' do m = /"([^"]+)"/.match(line) or raise 'must not happen' name = m[1] # delta/gtk_if リソースファイルの一部からキーバインドをとりだす ……失敗の責任はユーザ def new_item( line, f ) line = line.strip m = RE.match(line) or raise ArgumentError, "wrong menu item format at line #{f.lineno}" label, key, action = m.to_a[1,3] # amstd/version.rb バージョンナンバーをパース ……失敗の責任はライブラリユーザ def initialize( arg, *nums ) if String === arg then unless m = VERSION_EXP.match( arg ) then raise ArgumentError, "wrong version string format '#{arg}'" end @num = m[0].split('.').collect {|i| i.to_i } ------------------------------------------------------------------- 青木峰郎