関西Ruby会議05でカヌレを配布してみた

去る 2013年8月31日、関西Ruby会議05 が開催された。

今年もスタッフとして参加し、おやつ係を担当した。

なぜおやつが必要なのか

1日中、会議に集中していては頭が回らなくなる。糖分が必要。

Minami.rb の勉強会では、毎回おやつの時間があって楽しい。美味しい。

そこで関西Ruby会議でも、おやつ配布することにした。

おやつはカヌレ堂カヌレに決めた。

カヌレとは

フランスのボルドー女子修道院で古くから作られていた菓子。蜜蝋を入れることと、カヌレ型と呼ばれる小さな型で焼くことが特徴である。そもそもカヌレとは、「溝のついた」という意味である。外側は黒めの焼き色が付いており固く香ばしいが、内側はしっとりとして柔らかい食感を持つ。 ─── Wikipedia より

カヌレ堂とは

大阪は桜川にあるカヌレ専門店。
フランス産まれのカヌレを日本風にアレンジ。

6種類のレギュラーメニューに加え、月替わりの 2種類、計 8種類のカヌレが楽しめる。

関西の IT系勉強会では、カヌレ堂のカヌレがよく食されている。

カヌレを 3個ずつ配布

全種類を一人で食べるのは多すぎる。しかし 1個だと物足りない。

多すぎず少なすぎず、3個がちょうどよいのではないか。

カヌレの種類を選べなくすることでワクワク感を演出。

どうしても違う味のカヌレが食べたければ、他の参加者と交換してもらう。

何が当たったか見せ合ったり、トレードしたりで、コミュニケーションの助けにもなったのではないか。

80人余りの参加者がカヌレの袋を除き込んでニコニコしている光景が、個人的にはとても楽しかった。

反省点

  • Ruby や関西Ruby会議とカヌレは、まったく無関係。
  • ドタキャンした人が 30人近くいて、カヌレが余った。
    • 余分に貰ってくれる人がいて無駄にならずに済んだ。
  • この企画に夢中になりすぎて、他の仕事がおろそかになった。*1

謝辞

ありがとうございました。

*1:すみません。実行委員の皆さん、すみません。

*2:カヌレ堂さんもツイートを見ていたらしい

Ruby初級者向けレッスン 45回 ─例外─

第57回 Ruby/Rails勉強会@関西での初級者向けレッスンの資料を公開します。*1


エラーメッセージ

require 'open-uri'
open 'http://github.com/rubykansai/workshops/wiki'
.../open-uri.rb:223:in `open_loop': redirection forbidden: http://github.com/rubykansai/workshops/wiki -> https://github.com/rubykansai/workshops/wiki (RuntimeError)
        from .../open-uri.rb:149:in `open_uri'
        from .../open-uri.rb:688:in `open'
        from .../open-uri.rb:34:in `open'
        from ex.rb:2:in `<main>'


  • 上 2行が Ruby のコード。
  • 実行すると、下のエラーメッセージが出た。
  • Ruby のエラーメッセージは、いんちき英語でも読める。
  • 「...(RuntimeError)」までが 1行
  • 後は「from ...」から行末までが 1行
  • 最初の行がエラーの出た場所。
    • 「in `open_loop'」は open_loop メソッドでエラーが出たという意味。
    • 「.../open-uri.rb:223」は open-uri.rb の 223行目でエラーが出たという意味。
      • えっ、でも open_loop なんて見覚えないよ!?
  • 最後の行が呼び出し元。
    • 「in `<main>'」は実行を開始したオブジェクト。
  • 下から 2行目「in `open'」は <main> から open メソッドが呼ばれたという意味。
    • 各行がメソッドの呼び出しを表している。*2
  • 結局、英語のメッセージを読む必要がある ><
    • 「redirection forbidden」理由は分からんが http から https へのリダイレクトが禁止されてるらしい。
  • 最初の行の行末「(RuntimeError)」は RuntimeError 例外が発生したという意味。

エラーメッセージと例外


  • 例外が起きたら、なんらかの対処をしないと、プログラムが異常終了する。

いろいろな例外と、そのエラーメッセージを見て、例外に慣れよう。

例外いろいろ TypeError

"1" + 1

# ~> ex.rb:1:in `+': can't convert Fixnum into String (TypeError)
# ~> 	from ex.rb:1:in `<main>'


  • 下のコメントは実行した際のエラーメッセージ*3
  • String の "1" に Fixnum の 1 は足せない。

例外いろいろ NoMethodError

1.to_sym

# ~> ex.rb:1:in `<main>': undefined method `to_sym' for 1:Fixnum (NoMethodError)

  • Fixnum の 1 に to_sym というメソッドはない。

例外いろいろ NameError

n.times{puts 'Ruby!'}

# ~> ex.rb:1:in `<main>': undefined local variable or method `n' for main:Object (NameError)

  • n という変数やメソッドはない。

例外いろいろ NoMethodError (再び)

n = ARGV.first.to_i unless ARGV.empty?
n.times{puts 'Ruby!'}

# ~> ex.rb:2:in `<main>': undefined method `times' for nil:NilClass (NoMethodError)

  • コマンドライン引数を数値に変換して n に代入する。
  • 変数 n は nil になる。
    • nil は NilClass のインスタンスで、他の言語でいう NULL のこと。
    • nil はオブジェクトなのでメソッド呼び出しもできる。
    • しかし nil には times メソッドはない。

例外いろいろ Errno::ENOENT

open('nothing.txt')

# ~> ex.rb:1:in `initialize': No such file or directory - nothing.txt (Errno::ENOENT)
# ~> 	from ex.rb:1:in `open'
# ~> 	from ex.rb:1:in `<main>'

  • 存在しないファイルを open しようとした。
  • 「in `initialize'」は File.open メソッドが Fileクラスのインスタンスを生成しようとしている。
  • Errno::Exxx の例外は OS の errno に対応した例外。

例外いろいろ SyntaxError

1 def even?(n)
2   if n % 2 == 0
3     true
4   else
5     false
6 end
7 
8 puts even?(0)
9 puts even?(1)

# ~> -:9: syntax error, unexpected end-of-input, expecting keyword_end

  • if-else-end の end を忘れている。
  • Ruby のパーサがスクリプトを最後まで読んだが、end が見つからなかった。
  • end を忘れたのは 5行目と 6行目の間だが、例外は最後で出るので注意。

Intelligence と Wisdom

雨が降ってきて…
  • Intelligence
    • 雨だ!
  • Wisdom
    • 傘を差そう
    • 雨宿りしよう


雨が降ってきたときに、

  • それが雨だと認識できる ─── Intelligence (知識)
  • 濡れないためには、傘を差すか、雨宿りしないと ─── Wisdom (知恵)

例外も同じで、

  • エラーを検知したものが例外を起こす (Intelligence)
  • 対処方法を知っているものが例外を捕捉する (Wisdom)

例えば、アプリケーションがファイルを読もうとして、

  • open できない!
  • read できない!

File ライブラリはエラーを検知できる。(Intelligence)

  • 既定値を使うから、読めなくてもよい。
  • 別のファイルから読みなおしたい。
  • etc...

これはアプリケーションの仕様。(Wisdom)
ライブラリには分かりようがない。

例外を捕捉する (コード例1)

files = %w[file.txt file1.txt file2.txt]

files.each do |fn|
  begin
    open(fn, 'w'){|f| f.puts "Ruby!!"}
    break
  rescue => ex
    $stderr.puts "#{ex} (#{ex.class})"
  end
end


やりたいこと

  • ファイルに "Ruby!!" と書き込みたい。
  • ファイル名は file.txt
    • もし書けなければ、代わりに file1.txt に書こう。
    • それでも書けなければ、file2.txt に書こう。
    • それでダメなら諦める。

コード解説

  • ファイル名 "file.txt", "file1.txt", "file2.txt" という Array を用意。
  • 各ファイル名に対して処理を繰り返す。─── files.each ...
    • begin - end で例外を捕捉する準備。
    • ファイルに "Ruby!!" を書き込む。─── open( ...
      • 例外が起きると、rescue節に処理が移る。
      • 変数 ex が例外オブジェクトを指す。
      • ここでは、標準エラー出力に例外情報を出力するだけ。
      • 最初の繰り返しに戻る。─── files.each ...
    • 次のファイル名で open & puts に再挑戦。
      • 例外が起きなければ break で繰り返しを脱出する。

やってみる

$ ls -Fdl file*
drwxr-xr-x  2 mas  staff  68  4 12 23:37 file.txt/
-r--r--r--  1 mas  staff   0  4 12 23:37 file1.txt
# file.txt はディレクトリ
# file1.txt は書き込み権なし

$ ruby retry.rb
Is a directory - file.txt (Errno::EISDIR)
Permission denied - file1.txt (Errno::EACCES)

$ ls -Fdl file*
drwxr-xr-x  2 mas  staff  68  4 12 23:37 file.txt/
-r--r--r--  1 mas  staff   0  4 12 23:37 file1.txt
-rw-r--r--  1 mas  staff   7  4 12 23:39 file2.txt
# file2.txt ができた!

$ cat file2.txt 
Ruby!!

例外を捕捉する

begin
  式1…
[rescue [型1[, 型2]…][=> 変数][then]
  式2…]…
[else
  式3…]
[ensure
  式4…]
end

  • [ ... ] 内は省略可能。
  • begin - end で例外を捕捉する準備。
  • 式1 が例外を捕捉したい処理 (複数書ける)
  • rescue節で例外を捕捉。(rescue は複数書ける)
    • 「型」に例外クラスを指定すると、特定の例外だけを捕捉できる。(型は複数書ける)
    • 式2 が例外対処のための処理 (複数書ける)
    • 発生した例外に対応する rescue節がなければ、捕捉できない。
  • ensure節は begin - end のブロックを抜ける際の後始末。
    • 例外が起きても、起きなくても実行される。
    • 式4 が後始末の処理 (複数書ける)
  • else節は、上記 式1 で例外が起きなかった際に実行される。
    • rescue節で指定しなかった例外を捕捉する処理ではない。
    • 式3 で起きた例外は、このブロックでは捕捉しない。

上記のコードを省略なしで書くと、3通りの実行パターンがある。

  • 式1→式3→式4 (例外なし)
  • 式1→式2→式4 (例外を捕捉)
  • 式1→式4 (例外が発生するが、捕捉できない)

例外を捕捉する (コード例2)

require './factorial'

def fact(n)
  n.factorial
rescue ArgumentError
  "1以上の整数を指定してください"
rescue NoMethodError
  "整数を指定してください"
end

fact 3      # => 6
fact 4      # => 24
fact 5      # => 120
fact 0      # => "1以上の整数を指定してください"
fact 2.5    # => "整数を指定してください"
fact "2"    # => "整数を指定してください"

  • メソッド定義の def - end にも rescue, else, ensure が書ける。*4

例外の種類

puts NoMethodError.ancestors
# >> NoMethodError
# >> NameError
# >> StandardError
# >> Exception
# >> Object
# >> Kernel
# >> BasicObject

  • NoMethodError は NameError のサブクラス
  • NameError は StandardError のサブクラス
  • StandardError は Exception のサブクラス
  • Exception は例外の基底クラス

rescue修飾子

require './factorial'

3.factorial   rescue 0  # => 6
4.factorial   rescue 0  # => 24
5.factorial   rescue 0  # => 120
0.factorial   rescue 0  # => 0
2.5.factorial rescue 0  # => 0
"2".factorial rescue 0  # => 0

  • rescue の後置。
    • まず rescue の左辺を評価。
    • 例外が起きると rescue の右辺を評価。
  • 例外が起きたら、結果は 0 でいい。という大雑把なコード。
  • 捕捉する例外クラスを指定できない。
  • 例外オブジェクトを指す変数を指定できない。

例外を起こす

raise "simple"

# ~> ex.rb:1:in `<main>': simple (RuntimeError)

  • 例外を起こすには Kernel#raise メソッドを使う。
  • RuntimeError が発生する。

例外を起こす (クラス指定)

raise ArgumentError, "bad argument"

# ~> ex.rb:1:in `<main>': bad argument (ArgumentError)

  • 例外クラスを指定して raise できる。

例外を起こす (オブジェクト)

raise TypeError.new("can't convert...")

# ~> ex.rb:1:in `<main>': can't convert... (TypeError)

  • 例外オブジェクトを raise することができる。*5

例外を起こす (独自の例外クラス)

class MyError < StandardError; end

raise MyError, 'original'

# ~> ex.rb:3:in `<main>': original (MyError)

  • 独自の例外クラスを作れる。*6
    • 普通は StandardError のサブクラスにする。*7

サンプルコード

スライド、コードに間違いがあれば、ご指摘ください。

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

*2:スタックトレースとかバックトレースとか言う。

*3:あれ? 今実行したらメッセージ違う!?

*4:else節の評価結果はメソッドの評価結果となるが、ensure節の評価結果はメソッドの評価結果とはならない。

*5:例外でないオブジェクトを指定すると TypeError が発生する。えっ?

*6:エンタープライズだと独自の例外じゃないと起こせないことあるよね。

*7:StandardError なら、クラス指定なしの rescue でも捕捉できるから。

解答例 ─ Ruby初級者向けレッスン 44回

第56回 Ruby/Rails勉強会@関西 での Ruby初級者向けレッスン 44回 の解答例 *1

演習問題 1

0 から 9 までの数値をもつ配列 a がある。
a = (0..9).to_a
a # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  1. 各要素を順番に表示しよう。
  2. 各要素を 2倍した値を持つ配列を作ろう。
  3. 全要素の合計値を計算しよう。
  • スライドのおさらい。
  • 改めてやってみると、意外とできないかも知れないよ。
# -*- coding: utf-8; -*-

# 0 から 9 までの数値をもつ配列 a がある。
a = (0..9).to_a         # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


# 各要素を順番に表示しよう
a.each{|i| puts i}

# >> 0
# >> 1
# >> 2
# >> 3
# >> 4
# >> 5
# >> 6
# >> 7
# >> 8
# >> 9


# 各要素を 2倍した値を持つ配列を作ろう
a.map{|i| i * 2}  # => [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]


# 全要素の合計値を計算しよう
a.inject{|s, i| s + i}      # => 45

## マニアが書くと
a.inject(:+)                # => 45

演習問題 2

0 から 9 までの数値をもつ配列 a がある。
  • 奇数の要素だけを持つ配列を作ろう。
  • ただし odd? メソッドは使用禁止。
  • パズルだと思って、いろいろやってみよう。
# -*- coding: utf-8; -*-

# 0 から 9 までの数値をもつ配列 a がある。
a = (0..9).to_a       # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


# 奇数の要素だけを持つ配列を作ろう
a.select{|i| i.odd?}            # => [1, 3, 5, 7, 9]


# ただし odd? メソッドは使用禁止


# 2 で割った余りが 1 なら奇数
a.select{|i| i % 2 == 1}        # => [1, 3, 5, 7, 9]


# 偶数でなければ、それは奇数
a.select{|i| !i.even?}          # => [1, 3, 5, 7, 9]


# rubyist は true/false の否定を嫌う
# reject は式が真の要素を捨て去る
a.reject{|i| i.even?}           # => [1, 3, 5, 7, 9]

## マニアが書くと
a.reject(&:even?)               # => [1, 3, 5, 7, 9]

マニアのコードに出てきた & は、スライドに登場した、ブロックの受け渡しに使う & とは少し違う。*2

演習問題 3

Enumerable#map を自作してみよう。
module Enumerable
  def my_map
    ……
  end
end
ただしEnumerable#map とEnumerable#map! は使用禁止。

会場には「mapが使えないなら collectを使えばいいじゃない」と言う人がいた。
こういうことですね。わかります。*3

module Enumerable
  def my_map &block
    collect(&block)
  end
end

a = [*0..4]             # => [0, 1, 2, 3, 4]

a.map{|i| i * 2}        # => [0, 2, 4, 6, 8]
a.my_map{|i| i * 2}     # => [0, 2, 4, 6, 8]
a.map                   # => #<Enumerator: [0, 1, 2, 3, 4]:map>
a.my_map                # => #<Enumerator: [0, 1, 2, 3, 4]:collect>

惜しい。
多くのブロック付きメソッドはブロックを渡さないと Enumerator を返す。
my_map の方は # になっちゃってる。

# -*- coding: utf-8; -*-

# Enumerable#map を自作してみよう
# ただし Enumerable#map と Enumerable#map! は使用禁止

module Enumerable
  def my_map
    unless block_given?
      # ブロックがもらえなかったら Enumerator を返す
      to_enum __callee__
    else
      # 空の Array を用意し、
      inject([]) do |result, item|
        # ブロックの評価結果を追加する
        result << yield(item)
      end
    end
  end
end

# 試してみる
a = [*0..3]           # => [0, 1, 2, 3]

# map と my_map の比較
a.map{|i| i * 2}      # => [0, 2, 4, 6]
a.my_map{|i| i * 2}   # => [0, 2, 4, 6]

# Enumerator も比較
i = a.map             # => #<Enumerator: [0, 1, 2, 3]:map>
j = a.my_map          # => #<Enumerator: [0, 1, 2, 3]:my_map>

i.next                # => 0
i.next                # => 1
i.next                # => 2
i.next                # => 3

j.next                # => 0
j.next                # => 1
j.next                # => 2
j.next                # => 3
j.next                # => 

# ~> -:40:in `next': iteration reached an end (StopIteration)
# ~> 	from -:40:in `<main>'

詳しくは、時間がとれたら書きたい。*4

*1:There's More Than One Way To Do It. もっといいやり方があったら教えて。

*2:どう違うかはうまく説明できない。><

*3:これも反則です。

*4:コメントいただければ可能な限りお答えしたい。

Ruby初級者向けレッスン 44回 ― ブロック ―

第56回 Ruby/Rails勉強会@関西での初級者向けレッスンのスライドを公開します。*1


繰り返し

a = [0, 1, 2]

a.each do |i|
  puts i
end

a.each{|i| puts i}

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


  • ブロックの代表的な使い方は繰り返し処理。
  • do と end で囲まれたもの、{ と } で囲まれたものがブロック。
    • 上のコードと下のコードは同じ処理をしている。
  • Array のように、たくさんのオブジェクトを持っていると、全てのオブジェクトに繰り返し同じ処理をしたいことがよくある。
  • | と | で囲まれた変数 i が Array の各要素を順に指す。

便利な例

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

a.map{|i| i * i}        # => [0, 1, 4, 9]
a.select{|i| i.even?}   # => [0, 2]
a.inject{|s, i| s + i}  # => 6
a.find{|i| i.odd?}      # => 1
a.all?{|i| i.even?}     # => false
a.any?{|i| i.even?}     # => true


  • 初級者には、このスライドがおすすめ。
    • 特に inject メソッド


ブロックを渡す

  • メソッドには、ブロックをひとつ渡せる。
  • ブロックをどう使うかは、メソッド次第。
    • 繰り返し
    • ハリウッドの原理
open('hello.txt')   # => #<File:hello.txt>
open('hello.txt'){|f| f.read}
                    # => "こんにちは\n"


  • ブロックの役割りは、繰り返しだけではない。
  • ブロックの有無で動作を変えるメソッドがある。

ハリウッドの原理

# open('hello.txt'){|f| f.read}

begin
  f = open('hello.txt')
  f.read
ensure
  f.close unless f.nil?
end


  • 処理のスケルトンをメソッドで用意しておき、処理の一部をブロックで切り替える。
  • ブロック付きの 1行で書いた open メソッドは、ブロックなしで書くと、だいたいこんな感じ。
  • close の部分は f.close unless f.closed? かも。

ブロックのない open


  • ファイルを扱う場合は、以下の処理が必要。
    1. open
    2. read/write など
    3. close
  • コードを書く際の負担が多い。

ブロック付き open


  • open メソッドが忘れずに close してくれる。
  • コードを書く際の負担が少ない。

値を受け取る

値を受け取る (2)

  • 受け取るか受け取らないかは、ブロック次第。
2.times{puts 'こんにちは'}
# >> こんにちは
# >> こんにちは

2.times{|i| puts i}
# >> 0
# >> 1


  • 上の例は値を受け取っていない。
  • しかし times メソッドは値を渡してくれる。(下の例)
  • ブロックは必ずしも値を受け取る必要はない。

Hash の例

people = {matz: 47, dhh: 32}
            # => {:matz=>47, :dhh=>32}

people.each{|person| p person}
# >> [:matz, 47]
# >> [:dhh, 32]


  • key と value のペアを受け取れる。
  • ふたつの値が、ひとつの Array オブジェクトに。

Hash の例 (2)

people = {matz: 47, dhh: 32}

people.each do |name, age|
  p "#{name}(#{age})"
end

# >> "matz(47)"
# >> "dhh(32)"


  • ふたつの値を、それぞれ別の変数で受け取れる。
  • | と | の間に , で区切って変数を列挙する。

each_cons の例

midosuji = ["梅田", "淀屋橋", "本町", "心斎橋", "なんば"]

midosuji.each_cons(2){|path| p path}

# >> ["梅田", "淀屋橋"]
# >> ["淀屋橋", "本町"]
# >> ["本町", "心斎橋"]
# >> ["心斎橋", "なんば"]


  • ふたつの値を、ひとつの変数で受け取ると Array オブジェクトになる。

each_cons の例 (2)

midosuji.each_cons(2) do |from, to|
  p "#{from} - #{to}"
end

# >> "梅田 - 淀屋橋"
# >> "淀屋橋 - 本町"
# >> "本町 - 心斎橋"
# >> "心斎橋 - なんば"


  • ふたつの値を、ふたつの変数で受け取る。

each_cons の例 (3)

a = [*0..3]     # => [0, 1, 2, 3]

a.each_cons(3){|i| p i}
# >> [0, 1, 2]
# >> [1, 2, 3]

a.each_cons(3){|i, j| p [i, j]}
# >> [0, 1]
# >> [1, 2]


  • 繰り返しの回数は、どちらも 2回。
  • みっつの値を、ひとつの変数で受け取ると Array オブジェクトになる。(上)
  • みっつの値を、ふたつの変数で受け取ると、みっつ目の値が受け取れない。(下)

おかしいな? と思ったら

p unknowns.first
            # >> [1, ["matz", 47]]

unknowns.each do |id, (name, age)|
  id        # => 1
  name      # => "matz"
  age       # => 47
end


  • 要素を、ひとつ取り出して見る。
  • 例えば、こんなふうに表示されたら、値は幾つか?
    • 答えは、ふたつ! *2
      1. 数値 (1)
      2. Array
        • 中身は、文字列 ("matz') と数値 (47)
  • みっつの変数で受け取るには Array の中の Array を ( ) で表記する。

ブロックを受け取るメソッド

  • こんな感じで呼びたい
monta{puts 'block!'}

# >> block!
# >> block!
# >> 大切なことなので


  • monta というメソッドを作る。
    • ブロックを 2回評価して、
    • 最後に '大切なことなので' と出力する。

ブロックを受け取る方法は、ふたつある。

ブロックを受け取る

def monta
  yield
  yield
  puts '大切なことなので'
end


  • yield メソッドで受け取ったブロックを評価する。

ブロックを受け取る (2)

def monta &block
  block.call
  block.call
  puts '大切なことなので'
end


  • 引数でブロックを受け取る。
    • 引数に & を付ける。
  • call メソッドでブロックを評価する。

値を渡す

  • monta メソッドの仕様を変更。
  • ブロックに '大切なことなので' という文字列を渡す。
def monta
  yield '大切なことなので'
  yield '大切なことなので'
end

monta{|i| puts "#{i} block!"}

# >> 大切なことなので block!
# >> 大切なことなので block!


  • yield メソッドに文字列を渡すだけの簡単なお仕事です。

値を渡す (2)

  • また monta メソッドの仕様を変更。
  • ブロックに '大切な', 'ことなので' という、ふたつの文字列を渡す。
def monta &block
  block.call '大切な', 'ことなので'
  block.call ['大切な', 'ことなので']
end

monta{|i| puts "#{i} block!"}

# >> 大切な block!
# >> ["大切な", "ことなので"] block!


  • block.call に、ふたつの文字列を渡すだけだと……
    • ブロックがひとつの変数で待ち受けていると、ふたつ目の文字列が受け取ってもらえない。
  • ふたつ以上の値を渡すときは、Array のオブジェクトにして渡す。

ブロックは Proc

block = Proc.new do |i, j|
  puts "#{i}#{j} block!"
end

monta &block

# >> 大切なことなので block!
# >> 大切なことなので block!


  • あらかじめブロックだけ生成しておける。
  • メソッドに渡す際には & を付ける。

Q&A

Q1
ブロックをふたつ渡せないの?
A1
& なしで普通の引数としてなら渡せます。
Q2
さっきから EmacsRuby のコード実行してるけど、それなに?
A2
るびきちさんが作られた rcodetools を使ってます。Ruby を起動して実行結果を # => の後に埋め込んでくれます。(コード補完もできます) コメントなので、結果の埋め込まれたコードは、そのまま保存・実行できます。
Q3
どんなキーバインドなの?
A3
えっ、それ重要?

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

*2:こんなデータ構造が Array で渡ってきたら、データ構造の設計が間違ってる。

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

前回同様、第55回 Ruby/Rails勉強会@関西にて無人 ust を敢行。

前日の夜に手順のおさらいをすると、LiveShell のファームウェアが自動更新された。
これでダッシュボードとの同期ができるようになったみたい!
やったね。

これで勝つる!!

ところが、今回はネットワーク回線の問題で失敗したみたい。


Wi-Fi ルータには docomo の BF-01B を使ってる。*1
以前は e-mobile 使ってたけど、たまに電波の入らないことがあって買い替えた。

最初は調子良かったのが、だんだん回線品質が低下してきて、ついにはダッシュボードの同期もあやしくなってきた。

まだ半年ほど縛りはあるけど Xi とかに機種変更するか。


しかし冷静に考えてみると、docomo 3G の問題なのか、Wi-Fi の問題なのか、はっきりしない。

APが干渉なしで選べるチャネルのパターンでは、最大三つのAPしか置けないことが分かります([1、6、11]や、[2、7、12]、[3、8、13]、[4、9、14](日本のみ)のパターンしかありえない)。
無線LAN同士の干渉を考える ―― 無線にゃん

やはり Wi-Fi が問題なのかも?*2


そこでひらめいた!

BF-01B から有線でネットに接続できなかったっけ?
つまり、Internet ―― docomo ―(3G)― BF-01B ―(有線)― LiveShell という構成にすれば、どんなに Wi-Fi が干渉しても無問題 (ぉぃ

次回は BF-01B からの有線でチャレンジしたい。

*1:SP モードとか使わないから問題ない (よね?)

*2:この前、別の勉強会に参加した時も、参加者の数と Wi-Fi ルータの数が近似していた。

Minami.rb 第13回勉強会で受けた衝撃

半年ぶりくらいに Minami.rb の勉強会に参加した。

Rubyリファレンスマニュアル読み (Hashクラス編)

るりまをみんなで読もうというセッション。

Hash クラスと言えば、第54回 Ruby/Rails勉強会@関西 の初級者向けレッスンでやった。
ところが、何人かはレッスンを受講していたはずなのに、いちいち感心している。

分からなければ、グループワークで質問してるはずだ。ということは、何が分からないかも分からないままレッスン終了してたんだろう。 orz

まったく伝わってなかった。
自分の力不足を痛感した。

今後はグループワークでフォローできるようにしたい。

演習問題回答例

演習問題1

属性として身長と体重を追加しよう。体重は秘密にしよう。

仕様を決める。

  • initialize で height, weight を渡す
  • 省略時は nil
class Person
  @@variables = [:@name, :@born, :@height, :@weight]

  def initialize name, born = nil, height = nil, weight = nil
    @name, @born, @height, @weight = name, born, height, weight
  end

  attr_accessor :height
  attr_writer :weight

  def hash
    @@variables.map{|var| instance_variable_get(var)}.hash
  end

  def eql? o
    @@variables.all? do |var|
      instance_variable_get(var).eql? o.instance_variable_get(var)
    end
  end
end

属性が増えたので、それに合わせて hash, eql? も変更した。

演習問題2

BMI を計算するメソッドを追加しよう。
BMI = \frac{w}{t^2}
w = 体重[kg]
t = 身長[m]

仕様を決める。

  • height は Float で、単位は cm
  • weight は Float で、単位は kg
  • @height, @weight は Float でなくても、あるていど動くようにする
  • 計算できない場合は NaN を返す
class Person
  def bmi
    Float(@weight) / (Float(@height) / 100.0) ** 2
  rescue
    Float::NAN
  end
end

演習問題3

Person#<=> を書き直そう。
その妥当な仕様は?
p0 = Person.new('matz')
p1 = Person.new('Matz', Time.local(1965, 4, 14))

p0 <=> p1   # => ?

仕様を決める。

  • 単純に全ての属性に対して <=> を評価する
  • 比較対象が Person のインスタンスでなければ nil を返す
class Person
  def <=> o
    return nil unless o.kind_of? Person

    @@variables.each do |var|
      lhs = instance_variable_get(var)
      cmp = lhs <=> o.instance_variable_get(var)

      next if cmp == 0

      return cmp unless cmp.nil?
      return  -1 if lhs.nil?
      return   1
    end

    0
  end
end

演習問題4

クラス Person には、いくつかのバグがある。 それを見つけ出して修正しよう。

バグを探す。

  • @born に strftime メソッドがないと age で例外が発生する
    • 計算できなければ nil を返す
  • to_s が age を使用しているので、以下同文
    • age が nil なら、@name のみとする
class Person
  def age
    (Time.now.strftime('%Y%m%d').to_i -
     @born.strftime('%Y%m%d').to_i) / 1_00_00
  rescue
    nil
  end

  def to_s
    if a = age
      "#{name}(#{a})"
    else
      name
    end
  end
end

こればバグか?

matz = Person.new('matz')
matz.freeze
matz.name                       # => "matz"

matz.name.upcase!
matz.name                       # => "MATZ"

グループワークのときに質問してみたら、みんなこれは放置しているそうだ。

あえて対応するなら、

class Person
  def name
    @name.dup
  end
end

matz.name.upcase!       # => "MATZ"
matz.name               # => "matz"

間違いがあれば、指摘くださると助かります。