soukouki’s diary

誰かの役に立つ記事をかけたらいいなあ

コンパクトなRubyのREPLを作ったよ!

コンパクト。悪く言ったら低機能。

irbやpryとシェルの相性が悪かったので。

github.com (作ったよ!って言う割には別リポジトリに数か月前からあるのは内緒のお話)

対応してるもの

  • ローカル変数
  • マルチバイト文字

対応していないもの

  • 定義途中の改行(Enter押下)
  • ドキュメント等の読み込み
  • ステップ実行とか
  • などなど

23行のコンパクトなREPLです!

あ、最後に。

requireし直したい時は一回REPLを閉じてから開きます。

コマンドの引数にrequireするパスを指定できるのでうまく使ってくださいな。

Rubyのヒアドキュメントの配列の書き方

普通のヒアドキュメント

   <<-EOS
なんかいろいろ
  EOS

メソッドがついたヒアドキュメント

   <<-EOS.length
なんかいろいろ
  EOS

・・・配列のコンマはどこに置くんだ・・?

2回目のEOSの後ろ、2回目のEOSの次の行、3回目のEOSの前・・どれもうまくいかない

メソッドをそこに置くんだよな・・?もしや・・

ヒアドキュメントの配列

[
    <<-EOS,
なんかいろいろ
  EOS
    <<-EOS
なんかいろいろ
  EOS
]

できた!

Ruby いろんなループ(トリッキーなもの含む)

ループと言いつつ、やっているのは1から10の整数の乱数を作って、そこから6以上の値はもう一度やり直す。というもの

言い換えると、途中脱出が可能な無限ループです。え?スタックオーバーフロー?・・・

多分、最初のと最後(2018年11月24日追記分)がいいと思います。はい

puts "loopを使ったもの。多分これが一番メジャー"
a = loop{
    n = rand(1..10)
    break n if n<=5
}
p a

puts "再帰を使ったもの"
def f
    n = rand(1..10)
    return f if n>5
    n
end
a = f
p a

puts "ラムダ式を使ったもの。関数使ったやつとほぼ同じ"
f = ->{
    n = rand(1..10)
    (n<=5)? n : f.call
}
a = f.call
p a

puts "ただのwhile。他言語ならdo-whileを使う"
a = nil
while a.nil? || a>5
    a = rand(1..10)
end
p a

puts "whileとbreakを使ったもの"
a =
while true
    n = rand(1..10)
    break n if n<=5
end
p a

puts "例外のretryを使ったもの。速度的にも例外的にもあまり良くない気がする"
a =
begin
    n = rand(1..10)
    raise if n>5
    n
rescue
    retry
end
p a

puts "いい感じのやつを自分で定義したもの"
def loop1
    yield
end
a = loop1{
    n = rand(1..10)
    redo if n>5
    n
}
p a

puts "Integer#timesを使ったもの。"
a = nil
1.times{
    n = rand(1..10)
    if n>5
        redo
    else
        a = n
    end
}
p a

puts "Enumerable#mapを使ったもの。eachとかでもできるよね"
a = [nil].map{
    n = rand(1..10)
    redo if n>5
    n
}.first
p a

puts "無限リストを使ったもの。loop使ってるじゃんとか言わないで。selectで表現できるのは加点"
a = Enumerator.new{|y|loop{y<<rand(1..10)}}
    .lazy
    .select{|n|n<=5}
    .first
p a

puts "関数に囲ってきれいに書けるようにしたもの"
def infinite_list
    Enumerator.new{|y|loop{y<<yield}}.lazy
end
a = infinite_list{rand(1..10)}
    .select{|n|n<=5}
    .first
p a

# 2017年11月21日追加

puts "Rangeからの無限リスト"
a = (1..Float::INFINITY) # 1/0.0で無限が作れるので、そっちでもいいのかも
    .lazy
    .map{|_|rand(1..10)}
    .select{|n|n<=5}
    .first
p a

puts "デフォルト引数を使ったもの"
def f n=(n=rand(1..10);(n>5)? f : n)
    n
end
a = f()
p a

# 2018年11月24日追加

puts "loop+lazy+with_index 一年間の成長が詰まってる感じがする"
a = loop
    .lazy
    .map{rand(1..10)}
    .select{|n|n<=5}
    .first
p a

JavaScriptならイベントを使う方法があるらしい。

メモ Ruby対数正規分布の乱数

(2017/11/16)あああタイトル間違ってましたぁ・・更新前に見た方申し訳ないです・・

合っているかは自信ないです・・

ゲームの乱数くらいにお使いください。

require "random_bell"

# http://pmsl.planet.sci.kobe-u.ac.jp/~seto/?page_id=316
# をRubyに移植
def lognormal mu, sigma
    return Float::NAN if (mu <= 0)
    bell = RandomBell.new(
        mu: Math.log(mu*mu) - Math.log(mu*mu + sigma*sigma) / 2.0,
        sigma: Math.sqrt(Math.log(1 + (sigma / mu) * (sigma / mu))),
        range: -10..10)
    Math.exp(bell.rand)
end

分布確認用

require"./lognormal"
puts (1..10000).to_a.map{|n|lognormal(平均,分布).to_i}.inject({}){|r,n|r[n]||=0;r[n]+=1;r}.sort_by{|n,v|n}.map{|n,v|"#{n} #{"*"*v}"}

C++(標準ライブラリ)にもPython(NumPy)にもC#(Math.net)にもあるのに
Rubyにはない悲しみ

gem install random_bell random_bellを使用しています。

random_bell (0.1.1) ruby 2.3.3p222 (2016-11-21 revision 56859) [x64-mingw32]

Ruby 呼び出せないメソッド

define_method("!bar", ->{"foo"})

p method("!bar")

p method("!bar").call # 呼び出し方

!bar #< 呼び出せない

こんなことができるとは思ってなかった

ruby 2.3.3p222 (2016-11-21 revision 56859) [x64-mingw32]にて確認