Rubyで乱数生成
Rubyの乱数生成メモです。
rand関数
Rubyで乱数生成をするにはrandメソッドを使います。
10.times do puts rand(1000) end
このプログラムは、0~999までの範囲の乱数を10回出力します。
ただ、この乱数は擬似乱数と呼ばれるものであり、乱数生成に使われる種(シード)を元に算出されている数になりますので、純粋にランダムな数が生成されているわけではないということに注意が必要です。
擬似乱数の偏りについて説明しだすと長くなりすぎるので詳細はWikipediaのほうを参照。
因みに、srandメソッドを使うことで乱数の種を明示的に与える事が可能です。
srand(3) 10.times do puts rand(1000) end
実行結果
874 664 249 643 952 968 256 789 659 714
種を指定した方のプログラムは何回実行しても同じ結果を返します。
Rubyで書式付き出力を行う
Rubyの書式付き出力に関するメモ。
Rubyの書式付き出力
C言語なんかでもみるsprintf
メソッドを使うやり方と、%
演算子を使うやり方があります。先ずは%
演算子を使う例を見てみます。
プログラム
(1..10).each do |n| puts "%03d" % n end
実行結果
001 002 003 004 005 006 007 008 009 010
このプログラムの"%03d" % n
は、sprintf("%03d", n)
と同じ意味になります。
個人的には、%
の演算というと、どうも剰余を求めている様な感じがするので違和感ありまくりでしたが、演算子が文字列にかかっているか、数値にかかっているかの違いと理解すれば良いのでしょうか。
因みに、複数の書式を一度に扱う例は以下の通り。
puts "100 to bin ->%b ,oct->%o ,hex->%X" % [100,100,100] #=>100 to bin ->1100100 ,oct->144 ,hex->64 puts sprintf("100 to bin ->%b ,oct->%o ,hex->%X", 100,100,100) #=>100 to bin ->1100100 ,oct->144 ,hex->64
Rubyのsortメモ
Rubyのsortに関するメモです。
sortメソッド
配列をソートするには、sort(もしくはsort!)メソッドを使います。後ろにブロック処理を記述することで、比較方法を定義する事が出来ます。ここでは昇順でソートする方法と降順でソートする方法を記載します。
p [5,3,7,4,9,0,1,8,2,6].sort #=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] p [5,3,7,4,9,0,1,8,2,6].sort{|a, b| a <=> b } #=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] p [5,3,7,4,9,0,1,8,2,6].sort.reverse #=>[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] p [5,3,7,4,9,0,1,8,2,6].sort{|a, b| b <=> a } #=>[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
因みに、ブロックの中で出てきた<=>
は比較演算子と呼ばれるもので、左右の項の大小関係により、0,1,-1が返却されます。
p 1 <=> 1 #=> 0 p 1 <=> 0 #=> 1 p 0 <=> 1 #=> -1 p 2 <=> 0 #=> 1 p 0 <=> 2 #=> -1
sort_byメソッド
ブロック処理の戻り値を元にソートすることを前提としたのがsort_byメソッドです。以下の例のように、アルファベット順でなく、文字数でソートしたい時などに使えます。
langs = ["Java", "C", "Python", "C++", "Ruby", "Basic"] p langs.sort_by{|s| s } #=>["Basic", "C", "C++", "Java", "Python", "Ruby"] p langs.sort_by{|s| s.size } #=>["C", "C++", "Java", "Ruby", "Basic", "Python"]
Rubyでhashから値を取る方法のメモ
Rubyでhashを使ってて、Javaとかとは勝手が違っていたところで結構ハマッたので、調べた情報をメモ。
keyが文字列かシンボルか?
Rubyのhashはkeyとして文字列はもちろん、シンボルをkeyとして使うことも出来ます。例として、文字列、シンボルそれぞれをkeyとしたhashから値を取得するプログラムを書いてみます。
strhash={"key1" => "value1", "key2" => "value2", "key3" => "value3"} symbolhash={key1: "value1", key2: "value2", key3: "value3"} p strhash["key1"] # => "value1" p strhash[:key1] # => nil p symbolhash["key1"]# => nil p symbolhash[:key1] # => "value1"
もちろん、文字列とシンボルは中身が全然異なるので、それを混同するとおかしなことになります。keyが何の型かを認識しておかなければなりません。
fetchメソッドの仕様
先の例では[]を使って値を取得していましたが、fetchメソッドを使って値を取得することも出来ます。[]を使うのと、fetchを使うのとでは、hashから値が取れなかった時の扱いに大きな違いがあります。
単純なfetchメソッド
keyのみを引数とするfetchメソッドは、値が取れなかったときに例外が発生します。
symbolhash={key1: "value1", key2: "value2", key3: "value3"} p symbolhash.fetch(:key1) # => "value1" p symbolhash.fetch(:key4) # => `fetch': key not found: :key4 (KeyError)
値が絶対に取得できる前提で使うにはいいのかもしれませんね。
デフォルト値付きfetch()
第2引数にデフォルト値を渡すと、値が取れなかったときにデフォルト値が返却されます。
symbolhash={key1: "value1", key2: "value2", key3: "value3"} p symbolhash.fetch(:key1, "not found") # => "value1" p symbolhash.fetch(:key4, "not found") # => "not found"
[]で取るのと、fetch(key, nil)とは同じということですね。
ブロック付きfetch()
ブロックを渡すと、値が取れなかったときにブロックの値を評価した値が返却されます。
symbolhash={key1: "value1", key2: "value2", key3: "value3"} p symbolhash.fetch(:key1){|key| key.to_s + " not found"} # => "value1" p symbolhash.fetch(:key4){|key| key.to_s + " not found"} # => "key4 not found"
うーん。。正直使いどころが良く分からんが、まーこういう使い方も出来るということですね。
参考URL
RubyでStreamから渡された数値群をFixnumに変換する方法
CodeIQ、paiza等で出るプログラミングの問題では、標準入力から渡された数値データを処理する事が多いですが、標準入力経由だと基本文字列として受け取る為、計算に利用するにはプログラム内部で数値型に変換する必要があります。
この変換処理をいちいち書くのが結構おっくうだったりするのですが、Rubyでは結構簡単に処理を記述することができたので、ここにメモしておきます。
文字で与えられた数値群を一括変換
例として、標準入力から与えられた数値群を全て足した値を標準出力に出力するプログラム書いてみます。
先ずは、ダメな例から。
(a, b, c)=DATA.gets.split puts a + b + c __END__ 10 20 30
こいつを実行すると、
102030
という結果になります。単に文字を連結してるだけですね(^^;)
とりあえず動かす為の修正方法として、
puts a.to_i + b.to_i + c.to_i
とすれば目先要件は満たせるでしょうが、如何にも書き方が野暮ったいという感じがします。
to_iの処理を何回も書かないような記述方法がこれ。
(a, b, c)=DATA.gets.split.map(&:to_i) puts a + b + c __END__ 10 20 30
実行すると、
60
というわけで、それぞれ問題なく数値型に変換されています。
map(&:to_i)のやっていること
初めてこのような記述を見て、何がなんだか訳分からん状態だったので、自分なりに調べた内容でこれを要約すると、
:to_iシンボルに対するto_procメソッドの結果として返ってきたProcオブジェクトをブロックとしてmapメソッドに渡す。
という事になります。
ちょっとややこしいので細かく分解してみましょう。
先ず、mapメソッドのやっている事。
mapメソッド
mapメソッドは、要素の数だけ繰り返しブロックを実行し、ブロックの戻り値を集めた配列を作成して返します
http://ref.xaio.jp/ruby/classes/array/map
次に、(&:to_i)の部分の、「&」の意味。
ブロックの部分だけを先に定義して変数に保存しておき、後からブロック付きメソッドに渡すことも出来ます。 それを実現するのが手続きオブジェクト(Proc)です。 それをブロックとして渡すにはブロック付きメソッドの最後の引数として `&' で修飾した手続きオブジェクトを渡 します。
https://docs.ruby-lang.org/ja/2.2.0/doc/spec=2fcall.html#block
ま、mapに手続きオブジェクトを渡しているというのは確かなようです。
最後に、「&」の後に続く:to_iの意味。
これはSymbolと呼ばれるオブジェクトです。しかし「&」に続く物としては手続きオブジェクトが期待されているので型が合いません。
そのためSymbolが持つto_procメソッドで暗黙的にProcオブジェクトに変換されているのです。
https://docs.ruby-lang.org/ja/2.2.0/class/Symbol.html#I_TO_PROC
以上が、map(&:to_i)のやっている事になります。
雑感
今回のケース、正直ソースの見た目も内部でやっている事も大分ややこしいんですが、to_procが省略されているという事が発見出来れば理解するのも難しくないかなぁという感じです。
参考URL
Rubyにおける「真」と「偽」の判定
Ruby及びその他の言語に於ける真と偽の扱いのメモ。
「真」と「偽」の扱い
if文やwhile文での条件判定に使う「真」と「偽」の扱いはプログラム言語により様々だったりします。
例えばC言語ではint型の0が「偽」であり、それ以外が「真」です。Java言語ではプリミティブ型であるbooleanが用意されており、trueが「真」、falseが「偽」として扱われます。
では、Rubyはどうなの?ということで調べて見ますと、Rubyではfalseとnilが「偽」、それ以外が「真」として扱われます。
実際の挙動を見てみましょう。
プログラム
if nil puts "nil is true" else puts "nil is false" end if false puts "false is true" else puts "false is false" end if 0 puts "0 is true" else puts "0 is false" end if 1 puts "1 is true" else puts "1 is false" end
実行結果
nil is false false is false 0 is true 1 is true
このように、0が「偽」扱いにはならないという点はRubyを使う上で留意する必要がありますね。
Rubyプログラムをプロファイラで計測する
今回は、Rubyのプロファイルを取得する機能についてのメモ。
プロファイラの実行
Rubyでは標準でプロファイルを取る機能が用意されています。ここでいうプロファイルとは、どの処理にどれ位時間が掛かってるかや、どの処理が何回呼ばれたかという分析結果です。rubyコマンドに -r profile オプションをつけて実行することで、プロファイルを取る事が出来ます。
ruby -r profile test.rb
因みに私はeclipseから実行することが主なので、eclipseから実行する場合の手順ものせておきます。
[Run As]->[Run Configurations ...]から、ArgumentsタブのInterpreter argumentsに -r profileオプションをつけます。
では、物は試しということで、簡単なfizzbuzz問題のプログラムのプロファイルを取ってみましょう。
プログラム
(1..100).each do |x| if x % 15 == 0 then puts "FizzBuzz" elsif x % 3 == 0 then puts "Fizz" elsif x % 5 == 0 then puts "Buzz" else puts x end end
実行結果
1 2 Fizz 4 Buzz . . 中略 . . Fizz 97 98 Fizz Buzz % cumulative self self total time seconds seconds calls ms/call ms/call name 0.00 0.00 0.00 1 0.00 0.00 TracePoint#enable 0.00 0.00 0.00 3 0.00 0.00 Thread.current 0.00 0.00 0.00 1 0.00 0.00 Mutex#lock 0.00 0.00 0.00 1 0.00 0.00 MonitorMixin#mon_enter 0.00 0.00 0.00 1 0.00 0.00 Kernel#respond_to_missing? 0.00 0.00 0.00 1 0.00 0.00 Kernel#respond_to? 0.00 0.00 0.00 1 0.00 0.00 Gem.suffixes 0.00 0.00 0.00 4 0.00 0.00 Gem.find_unresolved_default_spec 0.00 0.00 0.00 1 0.00 0.00 Array#each 0.00 0.00 0.00 1 0.00 0.00 Gem::Specification.unresolved_deps 0.00 0.00 0.00 1 0.00 0.00 MonitorMixin#mon_check_owner 0.00 0.00 0.00 1 0.00 0.00 Mutex#unlock 0.00 0.00 0.00 1 0.00 0.00 MonitorMixin#mon_exit 0.00 0.00 0.00 4 0.00 0.00 IO#set_encoding 0.00 0.00 0.00 2 0.00 0.00 IO#sync= 0.00 0.00 0.00 1 0.00 0.00 Kernel#gem_original_require 0.00 0.00 0.00 1 0.00 0.00 Kernel#require 0.00 0.00 0.00 261 0.00 0.00 Fixnum#% 0.00 0.00 0.00 261 0.00 0.00 Fixnum#== 0.00 0.00 0.00 53 0.00 0.00 Fixnum#to_s 0.00 0.00 0.00 200 0.00 0.00 IO#write 0.00 0.00 0.00 100 0.00 0.00 IO#puts 0.00 0.00 0.00 100 0.00 0.00 Kernel#puts 0.00 0.00 0.00 100 0.00 0.00 nil# 0.00 0.00 0.00 1 0.00 0.00 Range#each 0.00 0.00 0.00 1 0.00 0.00 TracePoint#disable 0.00 0.01 0.00 1 0.00 10.00 #toplevel
このような感じで、実行結果の後にプロファイルが出力されます。今回は単純なプログラムなので時間計測はほぼ意味が無いですが、剰余演算子の処理が何回行われたかというような情報は得られます。
各項目の意味
それぞれがどのような意味かは、リファレンスマニュアルに詳しいので引用します。
https://docs.ruby-lang.org/ja/latest/library/profile.html
- 全体時間のパーセンテージ
- 全体時間の総和(単位は秒)
- 正味時間の総和(秒)
- 呼び出された回数
- 1回の呼び出し当たりの平均正味時間(ミリ秒)
- 1回の呼び出し当たりの平均全体時間(ミリ秒)
- メソッド名
その他のプロファイラ
標準で利用できるprofile以外にも、ruby-profという軽量なプロファイラ等いろいろあるようなので使って見ようと思います。