配列の要素が、1ずつ増加しているかどうかをチェックする
配列内の要素が1ずつ増えているかどうかをチェックしたかったメモ。
[1, 2, 3, 4] #=> OK [1, 2, 4] #=> NG [4, 5] #=> OK [6] #=> OK [7, 8, 0] #=> NG
今回自分が出くわした場面では、要素内の値が0〜9ということが確定していたので、こんな解決方法を思いついた。
ary = [1, 2, 3, 4] ok_str = (0..9).to_a.join #=> "0123456789" if ok_str.index(ary.join) puts "OK!" else puts "NG." end
あらかじめ0から9までの数字を連結させた文字列を用意して、String#indexでひっかけるやり方である。
まあでもかなり特殊な条件だし、ぱっと見て何をしているか親切ではないので、何かないですかね〜とチームメイトに相談したら、Array#each_consというのを教えてもらった。
ary = [1, 2, 3, 4] ary.each_cons(2) do |v1, v2| puts "v1: #{v1}, v2: #{v2}" end #=> v1: 1, v2: 2 #=> v1: 2, v2: 3 #=> v1: 3, v2: 4
https://ruby-doc.org/core-2.5.0/Enumerable.html#method-i-each_cons
順繰りにまとめたものをループしてくれる。すばらしい。
なので、0〜9とかに限らず、ちゃんと1ずつ増加しているかどうかを確認するやり方をeach_consを使ってみると、
ary = [1, 2, 3, 4] result = ary.each_cons(2) do |v1, v2| if (v1 + 1) != v2 break false end end if result.nil? puts "OK!" elsif result == false puts "NG." end
こんな感じかな。each_consは正常に回りきったらnilを返してくれるので、途中でだめだったときにそれ以外の値でbreakして切り分けるのがちょっとかっこ悪いけど、これなら0〜100とか300〜89654とか、桁数に限らずチェックはできそう。
privateなセッタメソッドはself.をつけて呼ばなければならない
Rubyのprivateなセッタメソッドについてメモ。
Rubyのprivateメソッドはレシーバを省略しないと呼べない仕様だけど、
末尾に「=」がつくセッタメソッドに限ってはself付きで呼ばないと動いてくれないようだ。
class Foo def foo bar=("bar=") # NG(ローカル変数代入扱いになる) self.bar=("self.bar=") # OK bar("bar") # OK self.bar("self.bar") # Error end private def bar(i) puts i end def bar=(i) puts i end end Foo.new.foo
self.bar= bar aaa.rb:7:in `foo': private method `bar' called for #<Foo:0x00007ff5e3930270> (NoMethodError) from aaa.rb:21:in `<main>'
Kernel.openにパイプを渡す
Ruby2.4.3のリリースに、Net::FTPの脆弱性の修正をした、というのを見た。
CVE-2017-17405: Net::FTP におけるコマンドインジェクションの脆弱性について
ローカルのファイルを開くために、それぞれ内部で Kernel#open を使用しています。しかし、もし localfile 引数がパイプ文字 "|" で開始されていた場合、パイプ文字以降に並べられたコマンドが実行されてしまいます。
module function Kernel.#open (Ruby 2.4.0)
ファイル名 file が `|' で始まる時には続く文字列をコマンドとして起動し、コマンドの標準入出力に対してパイプラインを生成します
ファイル名が "|-" である時、open は Ruby の子プロセス を生成し、その子プロセスとの間のパイプ(IOオブジェクト)を返します。(このときの動作は、IO.popen と同じです。File.open にはパイプラインを生成する機能はありません)。
ファイル名の代わりに、 "|" を先頭につけたものを渡せば、パイプとかforkとかできるらしい。そういえばPerlでもこんなのあったわ。
やってみたメモ
パイプから読み取り
foo.txt
aaaaaa bbbbbb cccccc
pipe_r.rb
io = Kernel.open("| cat foo.txt") # 愚直に3回呼ぶ #puts io.gets #puts io.gets #puts io.gets # 一気読みする #puts io.readlines # 一行ずつ読む io.each do |line| puts line end io.close
$ ruby pipe_r.rb aaaaaa bbbbbb cccccc
パイプへ書き込み
pipe_w.rb
io = Kernel.open("| cat", "w") io.puts("Yeeeees!") io.close
$ ruby pipe_w.rb Yeeeees!
forkしてやりとり (IO.#popenのマニュアルのサンプルそのまま)
fork.rb
io = Kernel.open("|-", "r+") if io # parent io.puts "foo" puts io.gets io.close else # child s = gets print "child output: " + s exit end
$ ruby fork.rb child output: foo
使う機会があるかはわからないが、一応まとめてみた。