第55回 Ruby/Rails 勉強会@関西で初級者向けレッスンやってきた

初級者向けレッスンを担当したので、以下スライドを解説。*1 *2

クラスとは


クラスを作ってみよう

class Person; end

obj = Person.new
  # => #<Person:0x10102718>

obj.class           # => Person
Person.superclass   # => Object


  • Person という名前のクラスを作る。
  • class から end までがクラス。
  • クラス名は大文字で始める (キャメルケース)

属性を持たせてみよう

class Person
  def initialize name
    @name = name
  end
end

matz = Person.new('matz')
  # => #<Person:0x10138598 @name="matz">


  • initialize は特別なメソッド
  • def から end がメソッド

属性にアクセスしてみよう

class Person
  attr_reader :name
end

matz.name       # => "matz"


attr_writer
セッター
attr_reader
ゲッター
attr_accessor
セッター&ゲッター

変数・定数のおさらい

ローカル変数
person
インスタンス変数
@person
クラス変数
@@person
グローバル変数
$person
定数
Person


  • 最初の数文字を見れば区別できる
  • クラス名は定数

属性を増やしてみよう

class Person
  def initialize name, born = nil
    @name, @born = name, born
  end
  attr_accessor :born
end

matz.methods.map(&:to_s).grep(/born/)
  # => ["born", "born="]


  • initialize メソッドを変更
  • attr_accessor で born メソッドと born= メソッドが生成された

属性を増やしてみよう (2)

  • アクセスしてみる
matz.born = Time.local(1965, 4, 14)
dhh = Person.new('dhh',
        Time.local(1979, 10, 15))

matz.born
  # => 1965-04-14 00:00:00 +0900
dhh.born
  # => 1979-10-15 00:00:00 +0900


  • 代入のような表記で born= メソッドが呼ばれる

メソッドを作ってみよう

class Person
  def age
    (Time.now.strftime('%Y%m%d').to_i -
     @born.strftime('%Y%m%d').to_i) /
      10000
  end
end
matz.age    # => 47
dhh.age     # => 32


  • メソッド名は小文字で始める (スネークケース)
  • return は省略可能
    • 最後に評価した式の値がメソッドの値になる

メソッドを上書きしてみよう

matz.to_s  # => "#<Person:0x10138598>"

class Person
  def to_s
    "#{@name}(#{age})"
  end
end
matz.to_s  # => "matz(47)"
dhh.to_s   # => "dhh(32)"


  • to_s はオブジェクトの文字列表現
    • 未定義なら Object#to_s が実行される*3

インスタンスを比較してみると……

person = Marshal.load(Marshal.dump matz)

person == dhh   # => false
person == matz  # => false # おかしい


  • 深いコピー
    • Marshal.dump して Marshal.load する
  • == はメソッド
    • Person#== を定義しても良いが……
    • ==, !=, >, <, >=, <= 別々に定義するの面倒

順序を決めよう

class Person
  include Comparable
  def <=> o
    @name <=> o.name
  end
end
person == matz  # => true
person == dhh   # => false
matz > dhh      # => true


  • <=> 演算子で順序を決定
  • Comparable で ==, != , >, <, >=, <= を生成

Array#sort してみよう

  • 順序が決まれば sort できる
people = [matz, dhh]

people.sort  # => [dhh(32), matz(47)]

Array ときたら、次は ……

Hash のキーにしてみると……

値に入れてもおもしろくないので

h = {matz => "Ruby", dhh => "Rails"}

h[matz]     # => "Ruby"
h[dhh]      # => "Rails"

key = Marshal.load(Marshal.dump matz)

key == matz # => true
h[key]      # => nil # おかしい


  • 深いコピーをしたオブジェクトをキーにすると
    • 値が取り出せない!

hash 値を計算しよう

class Person
  def hash
    code = 17
    code = 37 * code + @name.hash
    code = 37 * code + @born.hash
  end
end
matz.hash   # => -22068619118
dhh.hash    # => 14923733106


  • 計算方法はハチドリ本に書いてあった*4
    • 出所は Effective Java らしい

eql? を上書きしよう

  • hash 値がぶつかっていないか調べる必要がある
  • Hash クラスは eql? でキーが正しいか判断する
class Person
  def eql? o
    return false unless @name.eql? o.name
    return false unless @born.eql? o.born
    true
  end
end
key.eql? matz   # => true
key.eql? dhh    # => false


  • a.eql? b が真の場合 a.hash == b.hash であること
    • そのように hash, eql? を定義する

Hash にアクセスしてみよう

h = {matz => "Ruby", dhh => "Rails"}

h[matz]     # => "Ruby"
h[dhh]      # => "Rails"

h[key]      # => "Ruby"


  • 最初に作った h (Hash) は Person#hash を定義する前に作成したもの
  • 格納位置がおかしいので作り直す*5

等値性のおさらい

==
内容が等しいか?
===
case 式で使用
eql?
Hash クラスが使用
equal?
同一オブジェクトか?


アクセス制御してみよう

オマケ

  • public
  • protected
  • private
class Person
  protected :born
end
matz.born
# ~> protected method `born' called for matz(47):Person (NoMethodError)


  • protected なメソッドを呼ぶと例外が発生する

今日 話さなかったこと

  • 委譲と継承
  • モジュール
  • クラス変数・クラスメソッド
  • Range の始点と終点

上記を調べて初級者を抜け出そう。

まとめ

  • クラスの作り方
    • 属性・アクセサ
    • メソッド
  • オブジェクトの等値性

以上、クラスを作る際の決まり事を紹介した。

*1:スライドだけ欲しい人は直接どうぞ。http://higaki-it.jp/ruby/55/slide.pdf

*2:コードが必要な人は gist からどうぞ。

*3:Kernel モジュールで定義されている

*4:会場の @nayutaya さんによると [@name, @born].hash とするのが一般的らしい

*5:会場の @no6v さんによると h.rehash するといいらしい

関西Ruby会議に関する重要なお知らせ

LT で話してきたのでご報告。

今年は「関西Ruby会議」を開催しません

関西にお住まいの Rubyist の皆さまにおかれましては、私たちに遠慮することなく地域Ruby会議を開催なさってください。

関西地域でRuby会議が開催されましたら、ぜひ参加させていただきたく思います。

Emacs から rbenv を使う

gist を表示する練習を兼ねて、Emacs で rbenv を使う elisp を作ってみた。

ググってみると exec-path を設定する話がやたら出てくるけど、rbenv でインストールした複数の ruby を切り替えて使う例が出てこない。

以下のように環境変数に使いたい ruby のバージョンを指定すれば切り替えられる。

(setenv "RBENV_VERSION" "1.9.3-p194")

指定した rubyrcodetools などで使えるようになっている。

RUBY_DESCRIPTION # => "ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin10.8.0]"

でも、いちいち環境変数設定するのが面倒すぎる。

(require rbenv) して、M-x rbenv すると、メッセージバッファでインストールした ruby を選択できる。

以下のように補完もできる。

Click <mouse-2> on a completion to select it.
In this buffer, type RET to select the completion near point.

Possible completions are:
1.8.7-p370 	1.9.3-p125
1.9.3-p194 	jruby-1.6.7.2
rbx-1.2.4

j TAB で jruby-1.6.7.2 を選択してみた。

RUBY_DESCRIPTION # => "jruby 1.6.7.2 (ruby-1.8.7-p357) (2012-05-01 26e08ba) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_33) [darwin-x86_64-java]"

xmp すると、ちゃんと切り替わったことが確認できる。

いろいろ言いたいこともあるだろうが、自分にとってはこれで充分。

演習問題解答例

演習問題 1

既存の Array に変更を加える方法と、新しく Array を作る方法で [0, 1, 2, 3] という Array を作ろうという問題。

a = []
b = a
b               # => []
a.object_id     # => 134275960
4.times{|i| a[i] = i}
a               # => [0, 1, 2, 3]
a.object_id     # => 134275960
b               # => [0, 1, 2, 3]
  • Array#[]= で Array オブジェクトを変更する例。
  • object_id が等しいので既存のオブジェクトの変更。
  • b の指すオブジェクトも変わってるんで、既存のオブジェクトの変更。
a = []
b = a
b               # => []
a << 0 << 1 << 2 << 3
b               # => [0, 1, 2, 3]
  • 「くく」で変更する例。
a = []
b = a
b               # => []
a[0, 4] = *0..3
b               # => [0, 1, 2, 3]
  • Array#[]= に位置とサイズを指定して代入する例。
a = []
b = a
b               # => []
a[0..3] = *0..3
b               # => [0, 1, 2, 3]
  • Array#[]= に Range を指定して代入する例。

後は新しく Array オブジェクトを作る例。

[*0..3]                         # => [0, 1, 2, 3]
(0..3).to_a                     # => [0, 1, 2, 3]
%w[0 1 2 3].map(&:to_i)         # => [0, 1, 2, 3]
"0123".split(//).map(&:to_i)    # => [0, 1, 2, 3]
Array.new(4){|i| i}             # => [0, 1, 2, 3]
4.times.to_a                    # => [0, 1, 2, 3]

たぶん、みんなも、もっと変態な例を思いついたはず。

会場でピックアップできればよかったなぁ。

演習問題 2

文字列の単語を数える。文字を数える。

まずは単語

def wc(a)
  a.inject(Hash.new{|h, k| h[k] = 0}) do |s, i|
    s[i] += 1
    s
  end
end

wc("No Ruby, No Life.".scan(/\p{Word}+/))
  .sort_by{|w, n| -n}
  .each{|i| puts "%8d %s" % i.reverse}

# >>        2 No
# >>        1 Ruby
# >>        1 Life
  • 文字列は適当。
  • String#scan で単語を抽出
  • Array#sort_by で語数の降順

つづいて文字

sum = wc("No Ruby, No Life.".split(//))
(" ".."~").to_a.each_slice(8) do |line|
  puts line.map{|c| "%s(%2d)" % [c, sum[c]]}.join(' ')
end

# >>  ( 3) !( 0) "( 0) #( 0) $( 0) %( 0) &( 0) '( 0)
# >> (( 0) )( 0) *( 0) +( 0) ,( 1) -( 0) .( 1) /( 0)
# >> 0( 0) 1( 0) 2( 0) 3( 0) 4( 0) 5( 0) 6( 0) 7( 0)
# >> 8( 0) 9( 0) :( 0) ;( 0) <( 0) =( 0) >( 0) ?( 0)
# >> @( 0) A( 0) B( 0) C( 0) D( 0) E( 0) F( 0) G( 0)
# >> H( 0) I( 0) J( 0) K( 0) L( 1) M( 0) N( 2) O( 0)
# >> P( 0) Q( 0) R( 1) S( 0) T( 0) U( 0) V( 0) W( 0)
# >> X( 0) Y( 0) Z( 0) [( 0) \( 0) ]( 0) ^( 0) _( 0)
# >> `( 0) a( 0) b( 1) c( 0) d( 0) e( 1) f( 1) g( 0)
# >> h( 0) i( 1) j( 0) k( 0) l( 0) m( 0) n( 0) o( 2)
# >> p( 0) q( 0) r( 0) s( 0) t( 0) u( 1) v( 0) w( 0)
# >> x( 0) y( 1) z( 0) {( 0) |( 0) }( 0) ~( 0)
  • String#split で文字に分解
  • Enumerable#each_slice で、8文字ごとに表示*1

もっといいのがあったら教えて!

*1:ASCII のみだけど

第54回 Ruby/Rails勉強会@関西で ust してみた

勉強会で Ustream 中継するのが面倒で、なんとかならんもんかと思っていた。

  • Webカメラの画質が悪い
    • ホワイトバランスとか調整できない
  • PC (or Mac) を占有される
    • タイプ音をひろっちゃったり
    • 自分が発表したりすると PC が足りない


そこで LiveShell ですよ。

これを使えば有線/無線LAN経由で ust できるっぽい。

手持ちのカメラではコンポジット出力すると、液晶に何も映らなくなって操作できないのでビデオカメラも購入した。*1

初期設定が済めば、電源入れるだけで ust 中継が始まる。

配信停止とか録画とかは、ダッシュボード(専用のサイト) から行うのだが、これがなかなか言うこと聞いてくれない。

LiveShellは ust すると同時に自身のステータスを cerevo のサーバに送ってる (たぶん)

ダッシュボードと LiveShell の間には cerevo のサーバがあって、そこを経由して指令を出したり、ステータスを見たりできる (たぶん)

ところが、この経路のどこかで通信が途絶えてる。

どこが問題なのか?
どうすれば問題を切り分けられるのか?
さっぱり分からん。

ust のチャネルでは中継が見えているのに、ダッシュボードでは LiveShell がオフライン状態。

本番では録画やミュートなどダッシュボードからの操作は諦めて、たれ流しにした。


前日に練習したときにもステータスが反映されず、本体の電源オフ/オンやダッシュボードのログアウト/ログイン、設定変更など試行錯誤する間にネットワークに繋がらなくなって、初期設定をやりなおしたら本体とダッシュボードの同期が取れた。*2

本当ならダッシュボードからテロップ入れたりできるみたいなんだけど残念。期待してたのに残念。

なお、今回の ust は完全に放置してたんで、途中でピントがずれたり、カメラが横向いたりして、ぐだぐだだったらしい。これは失敗。

*1:HDMI から 480p で出力できれば使えるらしい

*2:毎回初期設定が必要な訳ないよね

第54回 Ruby/Rails 勉強会@関西で初級者向けレッスンやってきた

初級者向けレッスンを担当したので、以下スライドを解説。*1

スライドだけ欲しい人は直接どうぞ。

Array とは

  • 配列クラス
  • 任意のオブジェクトを持つことができる
[1, 1, 2, 3]

[1, "two", [3, "3"], 4.0, :five]
  • まずは Array のリテラルを紹介。
  • 最初のは、Fixnum を 4つ持つ Array.
  • 次のは、Fixnum, String, Array, Float, Symbol の 5つの要素を持つ Array.
    • Ruby は変数に型がない *2 ので、ひとつの Array で別の型のオブジェクトも持てる。

Array とは (2)

a = [1, "two", [3, "3"], 4.0, :five]

a[0]      # => 1
a[-1]     # => :five
a[1] = "2nd"
a[3, 2]   # => [4.0, :five]
a[1..-2]  # => ["2nd", [3, "3"], 4.0]
a[5]      # => nil
  • 添字は 0 オリジン
  • 添字が負数なら後ろから数える
  • 代入できる
  • サイズを指定して取り出せる
  • 範囲を指定して取り出せる
  • 値がなければ nil

Array オブジェクトの作り方

["a", "b", "c"] # => ["a", "b", "c"]
("a".."c").to_a # => ["a", "b", "c"]
[*"a".."c"]     # => ["a", "b", "c"]
%w[a b c]       # => ["a", "b", "c"]
  • Range オブジェクトは to_a で Array にできる (ことがある)*3
  • * で Range を展開
  • %w 表記

Array オブジェクトの作り方 (2)

"No Ruby, No Life.".scan(/\w+/)
  # => ["No", "Ruby", "No", "Life"]

"1,1,2,3,5,8".split(/,/)
  # => ["1", "1", "2", "3", "5", "8"]
  • String から Array を作ることがある。
  • 最初の例は、String#scan で単語を抽出。*4
  • 次の例は、String#split で、なんちゃって CSV

Hash とは

  • 連想配列クラス
  • 任意のオブジェクトを持つことができる
  • 任意のオブジェクトをキーにできる
{:AAPL=>566.71, :GOOG=>605.23}

{AAPL: 566.71, GOOG: 605.23}
    # => {:AAPL=>566.71, :GOOG=>605.23}
  • つづいて Hash のリテラルを紹介。
  • キーと値は => で区切る。
    • 1.9 からは下の表記を使える。(よりデータの羅列に見える)

Hash とは (2)

h = {AAPL: 566.71, GOOG: 605.23}

h[:AAPL]      # => 566.71
h[:MSFT] = 31.16
h[:FB]        # => nil
  • Hash もアクセスには [ 角かっこ ] を使う
  • 代入できる (値の存在しないキーに対しても可)
  • 値がなければ nil

Hash オブジェクトの作り方

a = [:AAPL, 566.71, :GOOG, 605.23]

Hash[*a]
    # => {:AAPL=>566.71, :GOOG=>605.23}
順序がおかしくても気にしない
Hash[:AAPL, 566.71, :GOOG, 605.23]
  # => {:AAPL=>566.71, :GOOG=>605.23}
Hash[:AAPL, 566.71, 605.23, :GOOG]
  # => {:AAPL=>566.71, 605.23=>:GOOG}
素数が奇数なら、
Hash[:AAPL, 566.71, :GOOG, 605.23, :FB]
# ~> odd number of arguments for Hash (ArgumentError)
あまり使わないけど、使うとハマる Array の初期化

Array の初期化

Array.new(4, 0)     # => [0, 0, 0, 0]

a = Array.new(3, "ruby")
    # => ["ruby", "ruby", "ruby"]

a[0].upcase!        # => "RUBY"

a   # => ["RUBY", "RUBY", "RUBY"]
a = []

a[0] += 1
# ~> undefined method `+' for nil:NilClass (NoMethodError)
全ての要素が同一のオブジェクトを指している。 では、どうするか? そこでブロックですよ!

Array の初期化 (2)

a = Array.new(3){"ruby"}
    # => ["ruby", "ruby", "ruby"]

a[0].upcase! # => "RUBY"

a   # => ["RUBY", "ruby", "ruby"]
ブロックで初期値を指定すると、別のオブジェクトになる。 レッスンではサッと流したけど以下が重要。 次の例だと、せっかくブロック使っても無意味。
s = "ruby"
a = Array.new(3){s}
    # => ["ruby", "ruby", "ruby"]

a[0].upcase!  # => "RUBY"

a   # => ["RUBY", "RUBY", "RUBY"]
また、ブロックでは値が受け取れる。
a = Array.new(3){|i| i.to_s}
    # => ["0", "1", "2"]
Hash についても同じことがいえる。

Hash のデフォルト値

hash = Hash.new(0.0)  # => {}
hash[:AAPL]           # => 0.0

hash = Hash.new{|h, k| h[k] = ""}
                      # => {}
hash[:GOOG]           # => ""
hash[:IBM]            # => ""
※ キーの破壊

キーの破壊

なので、キーを破壊すると、破壊前の格納位置に到達できなくなる (たぶん) ただし、String をキーにしても、Hash のキーを破壊することはできない。 Ruby は String を特別に扱っている
  1. String オブジェクトをコピーして
  2. freeze する
key = "ruby"    # [あとでこわす]

h = {}
h[key] = "関西"
h   # => {"ruby"=>"関西"}

h.first                   # => ["ruby", "関西"]
h.first.first             # => "ruby"
h.first.first.eql? key    # => true   # 文字列は同じ
h.first.first.equal? key  # => false  # 別のオブジェクト
h.first.first.frozen?     # => true   # freeze されている

key.upcase!               # => "RUBY"
h   # => {"ruby"=>"関西"} # 破壊できない

繰り返し each

[0, 1, 2].each{|i| puts i}

[0, 1, 2].each do |i|
  puts i
end

# >> 0
# >> 1
# >> 2

繰り返し Enumerable

Array.ancestors
  # => [Array, Enumerable, Object, Kernel, BasicObject]
Hash.ancestors
  # => [Hash, Enumerable, Object, Kernel, BasicObject]
Enumerable という便利なモジュールがありまして、

繰り返し Enumerable (2)

a = [2, 3, 5, 7]        # => [2, 3, 5, 7]

a.map{|i| i * i}        # => [4, 9, 25, 49]
a.select{|i| i.odd?}    # => [3, 5, 7]

a.inject{|s, i| s += i} # => 17

a.all?{|n| n.prime?}    # => true
それぞれのメソッドは「るりま」を見てね。きりがないし。*5 でも、この後、しょーもない紙芝居やるよりも inject の動きとか見た方が良かったんじゃないか、とスライド作りながら思ってた。*6 inject を教科書通りに呼ぶと、こんな感じ。
a = [2, 3, 5, 7]  # => [2, 3, 5, 7]

a.inject(0){|s, i| s += i}  # => 17

a.inject(0) do |s, i|
  puts "s = #{s} + #{i}"
  s += i
end

# >> s = 0 + 2
# >> s = 2 + 3
# >> s = 5 + 5
# >> s = 10 + 7
各ループで s と i は上記の値を指している。 つづいてスライドの例
a.inject{|s, i| s += i}     # => 17

a.inject do |s, i|
  puts "s = #{s} + #{i}"
  s += i
end

# >> s = 2 + 3
# >> s = 5 + 5
# >> s = 10 + 7
s の初期値が省略されると、s = a[0]; i = a[1] から繰り返しが始まる。 しかし、上級者になると、こんな書き方をする。
a.inject(&:+)               # => 17

繰り返しと多重代入

a = [[:matz, 47], [:dhh, 32]]
a.size      # => 2

a.each{|i| puts "#{i[0]}(#{i[1]})"}

a.each{|name, age|puts "#{name}(#{age})"}

# >> matz(47)
# >> dhh(32)
ブロックの引数は、代入だと思ってみる。
i = [:matz, 47]
i       # => [:matz, 47]

name, age = [:matz, 47]
name    # => :matz
age     # => 47
さらに無理のある例、

繰り返しと多重代入 (2)

a = [[1, [:matz, 47]], [2, [:dhh, 32]]]
a.size      # => 2

a.each do |id, (name, age)|
  puts "#{id}: #{name}(#{age})"
end

# >> 1: matz(47)
# >> 2: dhh(32)
これも代入だと
i = [1, [:matz, 47]]
i       # => [1, [:matz, 47]]

id, (name, age) = [1, [:matz, 47]]
id      # => 1
name    # => :matz
age     # => 47
かっこがないと、
id, name, age = [1, [:matz, 47]]
id      # => 1
name    # => [:matz, 47]
age     # => nil

繰り返しと多重代入 (3)

h = {matz: 47, dhh: 32}

h.each{|i| puts "#{i[0]}(#{i[1]})"}

h.each{|name, age|puts "#{name}(#{age})"}

# >> matz(47)
# >> dhh(32)
Hash では、みんな自然と多重代入してた
h = {matz: 47, dhh: 32}

h.each do |i|
  puts i.class
  p i
end

# >> Array
# >> [:matz, 47]
# >> Array
# >> [:dhh, 32]
Hash#each すると、キーと値のペアが Array で渡される。 要素の数と引数の数が合わないと、どうなるのか?*7
# 引数が多い場合
i, j, k = [1, 2]
i   # => 1
j   # => 2
k   # => nil

# 引数が足りない場合
i, j = [1, 2, 3]
i   # => 1
j   # => 2

# 引数が足りなくても……
i, *j = [1, 2, 3]
i   # => 1
j   # => [2, 3]
ちょっと蘊蓄

Array のコピー

a = [1, 2, 3]

b = a

a[0] = 0

a   # => [0, 2, 3]
b   # => [0, 2, 3]

Array のコピー (2)

a = ["a", "b", "c"]

b = a.dup

a[0] = "A"

a   # => ["A", "b", "c"]
b   # => ["a", "b", "c"]

Array のコピー (3)

a = ["a", "b", "c"]

b = a.dup

a[1].upcase!

a   # => ["a", "B", "c"]
b   # => ["a", "B", "c"]

今回、話さなかったこと

[].respond_to? :each   # => true
block = Proc.new{|i| i * 2}
[*0..4].map &block   # => [0, 2, 4, 6, 8]

block[5]             # => 10
a = [2, 3, 5, 7]
i = a.each
i.next  # => 2
i.next  # => 3
i.next  # => 5
i.next  # => 7
i.next  # ~> `next': iteration reached an end (StopIteration)

まとめ

  • Array と Hash の作り方・使い方
    • 初期化・デフォルト値はブロックで
  • 繰り返しはブロックで
  • 浅いコピー・破壊に注意
演習問題は [あとでかく]

*1:はてなダイアリーを使うと用語の説明が省略できるかな、と思って。

*2:オブジェクトには型がある。

*3:離散範囲なら可能

*4:1.9 でマルチバイト文字列から単語を抽出するには /\p{Word}+/ を使う

*5:Integer#prime? は require 'prime' しないと使えない

*6:紙芝居を作ってるうちに楽しくなってきて、こんなスライドに……

*7:質問されたけど、答えを用意してなかった

*8:duplicate の略 (だよね?) なので <でゅぷ> って読んでたけど <だっぷ> と読む説がある

関西Ruby会議04で初級者向けレッスンやってきた

2011年11月11日(金)・12日(土)に関西Ruby会議04が開催された。

「会議」という感じではないが、KOF (関西オープンソース2011)というイベントの会場で開催されるということもあり、新人Rubyistを勧誘 (洗脳?) すべく初級者向けレッスンも開催した。*1

レッスン内容

  • Rubyとは?
    • 余計な記述が少なく仕事に集中できる (LL)
    • すべてがオブジェクト (OO)
  • irb の使い方
  • るりまの使い方

大クラス主義、オープンクラス、ダック・タイピングなんかも話したかったけど、時間の都合で割愛した。

課題

初心者が対象なので FizzBuzz を出題した。

解答例1

まず一般的な回答例

(1..30).each do |i|
  case
  when i % 15 == 0 then puts "FizzBuzz"
  when i %  5 == 0 then puts "Buzz"
  when i %  3 == 0 then puts "Fizz"
  else                  puts i
  end
end
解答例2

TAをしてくれたアジャイルかわばたさんがワンライナーで書きたいと言って、その場で書いたもの。*2

(1..30).each{|i|puts i%15==0? "FizzBuzz": i%3==0? "Fizz": i%5==0? "Buzz" : i}


以下むりやり考えた逸般的な解答例。

解答例3

0 から 30 までの文字列の配列を用意して、3の倍数と 5の倍数を潰す。

a = [*"0".."30"]
3.step(30, 3){|i|a[i].sub!(/\d*$/, "Fizz")}
5.step(30, 5){|i|a[i].sub!(/\d*$/, "Buzz")}
puts a[1..-1]
  • "0".. なので添字と内容が一致する
  • \d* なので数字がなくてもマッチ
  • $ なので末尾にマッチ
  • 1.. なので "0" は表示しない
解答例4

ググッて見つけたのをちょっと改造。

puts (1..30).map{|i|[[i, "Fizz"], ["Buzz", "FizzBuzz"]][i%5==0?1:0][i%3==0?1:0]}

5の倍数でないとき、あるときの配列に、3の倍数でないとき、あるときの配列が入っている。

*1:Rubyをよく知らない人も多く集まる会場なので、初級者向けレッスンの伝統をやぶって初心者を対象にした。

*2:彼は英語キーボードに不慣れらしくtypoの連続で、まるでダメプログラマかのように見えた。