Discussion:
[ruby-list:50670] [質問] キャプチャ付き正規表現の後方参照
Takahiro Yamaguchi
2018-06-02 15:21:10 UTC
Permalink
山口と申します。

$ uname -a
Darwin yamaguchi-no-MacBook-Air.local 17.5.0 Darwin Kernel Version 17.5.0: Fri Apr 13 19:32:32 PDT 2018; root:xnu-4570.51.2~1/RELEASE_X86_64 x86_64
$ ruby --version
ruby 2.3.3p222 (2016-11-21 revision 56859) [universal.x86_64-darwin17]

の環境で、

$ cat regex.rb
tbl_cmd = [
{ regexp: '^ *v +(\d+) *$',
cmd: ":direct #{$1}"},
]
cmd = STDIN.gets # $ v 5
tbl_cmd.each do |c|
if /#{c[:regexp]}/ =~ cmd
puts "match" # => match
p $1 # => "5"
p c[:cmd] # => ":direct "
end
end

を実行。

$ ruby regex.rb
v 5 # v 5 と入力
match
"5"
":direct "

となり、

v 5 と入力した場合、

if 文中 $1 で、キャプチャした内容を参照できているのですが、
p c[:cmd] の、c[:cmd] の、$1 部分、
でキャプチャした内容を後方参照できずに、悩んでいます。
どのようにすればよいか、ご教示いただけないでしょうか?
dogatana
2018-06-04 22:48:19 UTC
Permalink
こんにちは、市田です。
どの様に記述すればいいのですか?
コマンドの文法、エラーチェックの方法、コマンドの数など、どこまでのことをしたいのかによって
実装方法が変わってくるので、答えようが。。

特にrubyでは色々な方法でかけるので、あれこれ考えるのも楽しいですよ。:)

* 1行で完結
* 空白で区切られ、かつ1の引数には空白を含まない
* コマンドは数個
* 引数はせいぜい1個程度で、ほとんど数字

程度であれば、

cmd, *args = str.split

として cmd を if で判定するでしょうね。
そこそこコマンドの数が多いならテーブル(hash)引きするなり、メソッド��
Takahiro Yamaguchi
2018-06-05 12:42:59 UTC
Permalink
市田さん

山口です。
ご教示ありがとうございます。
Post by dogatana
どの様に記述すればいいのですか?
コマンドの文法、エラーチェックの方法、コマンドの数など、どこまでのことをしたいのかによって
実装方法が変わってくるので、答えようが。。
不躾な質問ですみません。
Post by dogatana
特にrubyでは色々な方法でかけるので、あれこれ考えるのも楽しいですよ。:)
* 1行で完結
* 空白で区切られ、かつ1の引数には空白を含まない
* コマンドは数個
* 引数はせいぜい1個程度で、ほとんど数字
程度であれば、
cmd, *args = str.split
として cmd を if で判定するでしょうね。
なるほど。
この様な記述を思い浮かびませんでした。
入力されたのを、正規表現でマッチさせたら楽だなと考え、引数の数字も正規表現でキャプチャという思考に至りました。
勉強になります。
いつも、ご教示ありがとうございます。
Post by dogatana
そこそこコマンドの数が多いならテーブル(hash)引きするなり、メソッドを定義しておいて send するなど。
それっぽい感じにはしてるんですけど…
もっと、プロっぽい記述とかになればいいんですが...
method input は、thread にしています。

def input

@cmd_tbl = [
{ regexp: '^ *p *$',
input: "p",
explanation: "pause execution",
queue: $qsl,
cmd: :pause},
{ regexp: '^ *q(uit)* *$',
input: "q or quit",
explanation: "exit",
queue: nil,
cmd: :quit},
{ regexp: '^ *prev *$',
input: "prev",
explanation: "previous content",
queue: $qsl,
cmd: :prev},
{ regexp: '^ *n(ext)* *$',
input: "n or next",
explanation: "next content",
queue: $qsl,
cmd: :next},
{ regexp: '^ *l(ist)* *$',
input: "l or list",
explanation: "list the speech table",
queue: $qsl,
cmd: :list},
{ regexp: '^ *r(epeat)* *$',
input: "r or repeat",
explanation: "toggle repeat execution",
queue: $qsl,
cmd: :repeat},
{ regexp: '^ *s(tatus)* *$',
input: "s or status",
explanation: "info execution thread status",
queue: $qsl,
cmd: :status},
{ regexp: '^ *v +(\d+) *$',
input: "v no, e.g. v 12",
explanation: "speech the 'no' content specified in the speech tbl without target device name",
queue: $qsl,
cmd: '":direct #{$1}"'},
{ regexp: '^ *t +(\d+) *$',
input: "t no, e.g. v 12",
explanation: "speech the 'no' content specified in the speech tbl with tareget device name",
queue: $qsl,
cmd: '":tdirect #{$1}"'},

{ regexp: '^ *sl *$',
input: "sl",
explanation: "list the single speech table",
queue: $qss,
cmd: :list},
{ regexp: '^ *sv +(\d+) *$',
input: "sv no e.g sv 10",
explanation: "speech 'no' content specified in the single speech tbl without target device name",
queue: $qss,
cmd: '":direct #{$1}"'},
{ regexp: '^ *st +(\d+) *$',
input: "st no e.g sv 10",
explanation: "speech 'no' content specified in the single speech tbl with target device name",
queue: $qss,
cmd: '":tdirect #{$1}"'},
{ regexp: '^ *h(elp)* *$',
input: "h or help",
explanation: "show the help",
queue: nil,
cmd: :help},
]

alias quit exit

def help
@cmd_tbl.each do |h|
puts "#{h[:input]}: #{h[:explanation]}"
end
end

loop do
cmd = STDIN.gets
puts "[INFO] input:#{cmd}"

match = nil
@cmd_tbl.each do |c|
if /#{c[:regexp]}/ =~ cmd
match = true
x = $1 ? eval(c[:cmd]) : c[:cmd]
c[:queue] ? c[:queue].push(x) : send(x)
break
end
end

puts "[WARN] unrecognized cmd:#{cmd}" unless match
end

end
dogatana
2018-06-10 10:19:56 UTC
Permalink
こんにちは、市田です。
Post by Takahiro Yamaguchi
もっと、プロっぽい記述とかになればいいんですが...
メソッド内部の処理の組み立てレベルは、「これはあきらかにだめ」なものも
あるでしょうが、そうでなければ個人の好みも大きく影響すると考えます。

そう言う意味でプロっぽいかどうかは別として、私なら変えようと思うのは

* regexp には正規表現リテラルにする
* 先頭と最後の空白をはぎ取るのは事前に strip を使う
* テーブルから探すのは each ではなく find を使う
* help も map と join で文字列生成した文字列を puts にする
* cmd にシンボルを入れているが、quue に積むときは文字列になるのが気持ち悪い

あたりです。

なお、書かれている例だと

* /q(uit)*/ だと uit が 0回以上の繰り返しにマッチする
* /q(uit)/ の uit が $1 に入るが考慮されてないのでは
* eval は $1 を使うなら不要(直接 $1 を�
y***@u08.itscom.net
2018-06-12 05:46:42 UTC
Permalink
$B;38}$G$9!#(B

$B;TED$5$s!"$465<($"$j$,$H$&$4$6$$$^$9!#(B
$BBgJQ;29M$K$J$j$^$7$?!#(B
... $B;d$J$iJQ$($h$&$H;W$&$N$O(B
* $B%F!<%V%k$+$iC5$9$N$O(B each $B$G$O$J$/(B find $B$r;H$&(B
$B$A0-$$(B
$B$"$?$j$G$9!#(B
* /q(uit)/ $B$N(B uit $B$,(B $1 $B$KF~$k$,9MN8$5$l$F$J$$$N$G$O(B
$B$N$O3NG'$5$l$k$N$,NI$$$+$H!#(B
Loading...