devsum08-aoki

ふつうのRubyプログラマに贈る
Rubyプログラミング講座
13-B-3
青木峰郎
日本Rubyの会
自己紹介
青木峰郎
(日本Rubyの会)
Rubyがらみで
やってること
Ruby関係のおしごと
•
•
•
•
•
•
•
書籍『Rubyist Magazine出張版』 MYCOM
書籍『Rubyレシピブック第二版』 SBCr
書籍『Rubyソースコード完全解説』 Impress
書籍『Rubyを256倍使う本 無道編』 ASCII
リファレンスマニュアル刷新計画 隊長
標準ライブラリいくつか (net/http, fileutils, ...)
それ以外のアプリ各種 (BitChannel,Racc, ..)
まとめ:
いろいろやってます。
今日の内容
agenda 1/2
第一部:Rubyプログラミングのツボ
1. protected
2. pとpp
3. unlessとuntil
4. クラスと名前空間
5. ファイル名とクラス名
6. 大クラス主義
agenda 2/2
第二部:Rubyプログラミングの、押しすぎるとヤ
バいツボのうちのいくつか
1. Visitorパターンめどい
2. 1.8と1.9でメソッド違いすぎwww
1.protected
Q: protected
使ってますか?
protectedは通常
必要ありません!
protectedの
95%は間違い
Railsも
使いかたを
間違ってた
Rubyのprotectedは
Java/C++のprotected
とは違う
Javaの可視性
可視性
意味
public
制限なくアクセスできる
protected サブクラスと、パッケージ内のクラ
スからアクセスできる
private
同じクラスからのみアクセスできる
Rubyの可視性
可視性
意味
public
制限なくアクセスできる
protected そのクラスとサブクラスで、オブ
ジェクトの外からアクセスできる
private
同じオブジェクト内からアクセスで
きる(クラスは関係ない)
Java/C++はクラス単位
Rubyはオブジェクト単位
違いの出る場面
Rubyではprivateメソッドも継承する
class A
def m() puts “OK” end
private :m
end
class B < A
def call_m
m
end
フツーに呼べる
end
protectedの使いどころ
Rubyでprotectedが活用できる場面
class SomeObject
attr_reader :prop
protected :prop
外から
(レシーバをつけた
形式で)呼べる
def ==(other)
@prop == other.prop
end
end
ぶっちゃけ
protectedなんて
使わない
結論:
Rubyでは
publicとprivateだけ
使ってればOK
2. pとpp
Q:
pメソッド
使ってますか?
p Object.new
printfデバッグ
(pデバッグ?)
に便利
Q:
ppメソッド
使ってますか?
require 'pp'
pp Object.new
表示が見やすい p
C:\>ruby -e "p File.stat('.')"
#<File::Stat dev=0x2, ino=0,
mode=040755, nlink=1, uid=0, gid=0,
rdev=0x2, size=0, blksize=nil,
blocks=nil, atime=Wed Feb 13 06:55:59
+0900 2008, mtime=Sun Feb 13 21:03:44
+0900 2008, ctime=Thu Jun 29 16:52:04
+0900 2006>
C:\>ruby -rpp -e "pp File.stat('.')"
#<File::Stat
dev=0x2,
ino=0,
mode=040755 (directory rwxr-xr-x),
nlink=1,
uid=0,
gid=0,
rdev=0x2 (0, 0),
size=0,
blksize=nil,
blocks=nil,
atime=Wed Feb 13 06:55:59 +0900 2008 (1202853359),
mtime=Sun Feb 03 21:03:44 +0900 2008 (1202040224),
ctime=Thu Jun 29 16:52:04 +0900 2006 (1151567524)>
pのしくみ
def p(obj)
puts obj.inspect
end
出力先はstdout
誰もがstderrに
出力されることを期待し
そして裏切られる
inspectを
自分で定義すれば
表示を変更できる
to_sとinspectの違い
メソッド
用途
to_s
オブジェクトを文字列に変換するとき。
inspect
オブジェクトをデバッグのために表示すると
き。
inspectは
デバッグ用途にだけ
使おう!
3. unlessと
until
Q:
unless
使ってますか?
Q:
until
使ってますか?
unless = if not
until = while not
制御構造は
えり好みせず使おう
ただしredoを除く
多様な意図を
表現するには
多様な制御構造を
使ったほうがいい
「if/whileで
書けるから
いらない」?
あらゆる制御構造は
gotoで実現できます
プリミティブ症候群に
陥らないこと
unlessの使いどころ
前提条件の
チェックに使うと
いいかんじ
def m(idx)
raise IndexError, "out of range" \
unless idx >= 0
unless を使うときは、
「引数が満たすべき条件 (前提条件)」
を書けばよい
untilの使いどころ
終了条件をはっきり
書きたいときに
使う
until s.eos?
tok = s.scan(/\w+/)
「スキャン中の文字列が
尽きるまで(EOSになるまで)
~~する」
4. クラスと
名前空間
モジュールの
ネスト、してますか?
module Cflat
class Node
....
end
class IfNode < Node
....
end
class WhileNode < Node
....
end
end
モジュール・クラスは
クラスの名前空間を
兼ねている
module Cflat
class Node
....
end
end
module MyCompiler
class Node
....
end
end
別のモジュールに
ネストしていれば
クラス名が
同じでも大丈夫
Javaで言うパッケージ
C++で言うnamespace
module Cflat
class Node
....
end
end
p Cflat::Node.new
ネスト構造の指針
第1レベル:
アプリケーション名と
同名のモジュール
例: Cflat
第2レベル:
クラス名
例: Compiler, Parser,
Node, ...
以上
ネストは
深くしすぎないこと
(せいぜい3段)
深くしすぎると…
module Cflat
module Frontend
module AST
class Node
....
end
end
end
end
単純に、
見た目が
よろしくない!
5. ファイル名と
クラス名
Rubyでの
ファイル名
(ライブラリ名)
の決めかた
ファイル中で
最も代表的な
クラスの名前を
元に決める
class Node
....
class IfNode < Node
....
class WhileNode < Node
....
代表的なクラスは
Nodeクラス
ファイル名は
クラス名.downcase
Nodeクラス
downcase
node.rb
クラスが
ネストしている場合
module Cflat
class Node
....
class IfNode < Node
....
class WhileNode < Node
....
クラス階層を
ディレクトリ階層に
反映させる
Cflat::Nodeクラス
downcase
cflat/node.rb
そのほかの指針
1. 関連するクラスは
すべて1つの
ファイルにまとめる
Javaじゃ
ないんだから
2. ネストは
あまり深くしない(再)
Javaじゃ
ないので
6. 大クラス主義
大クラス主義…
いろいろな仕事を
1つのクラスに行わせる
設計ポリシーのこと
例1
RubyのArrayは
配列とリストと
スタックとキューを
兼ねている
# 配列として使う
array = []
array[0] = 3
array[1] = 6
p array[0] # => 3
p array[1]
# => 6
# スタックとして使う
stack = []
stack.push 1
stack.push 2
p stack.pop # => 2
p stack.pop
# => 1
# キューとして使う
queue = []
queue.push 1
queue.push 2
p queue.shift # => 1
p queue.shift
# => 2
例2
RubyのStringは
StringBuffer/
StringBuilderを
兼ねている
# 文字列をバッファ的に使う
buf = ''
buf << "line 1\n"
buf << "another line\n"
buf << "extra line\n"
puts buf
それで
いいのか?
ぜんぜんOK
だって
うまくいってるじゃん
マイクロカーネル
VS
モノリシックカーネル
論争に類似?
コンポーネント
(クラス)が増えると
コンポーネント間の
複雑性が増す
結局はバランス
第一部 完
第二部
agenda 2/2(真)
第二部:Rubyプログラミングの、押しすぎるとヤ
バいツボのうちのいくつか
1. sendを使ってクラス分岐を書く
2. リフレクションを使ってRubyのバージョン間
の差を埋める
1. sendを使って
クラス分岐を書く
問題:たくさんの
クラスがあって、
クラスごとに
処理を変えたい
クラスごとに
処理を変えると言えば
とりあえず多態
(polymorphism)
class 鳥
def 鳴け() puts "ぽー" end
end
class 牛
def 鳴け() puts "もー" end
end
class 豚
def 鳴け() puts "ぶー" end
end
def 鳴かせる(animal)
animal.鳴け
end
鳴かせる(鳥.new)
鳴かせる(牛.new)
1つの処理が
複数クラスに
細切れにされてしまう
1つの処理は
1つのクラスに
まとめたい!
# こんな感じにまとめたい
class
def
def
def
def
end
動物を鳴かせる
鳴かせる(animal) ここのコードが問題 end
鳥を鳴かす() puts "ぽー" end
牛を鳴かす() puts "もー" end
豚を鳴かす() puts "ぶー" end
動物を鳴かせる.new.鳴かせる(鳥.new)
動物を鳴かせる.new.鳴かせる(牛.new)
Visitorパターン?
Visitorパターンは
実装がめんどくさい
そこで
send
Object#sendは
「メソッドを呼ぶ
メソッド」
obj.send(メソッド名, 本来の引数)
p "str".size
send化
p "str".send(:size)
sendを使って
クラス分岐が書ける
class 動物を鳴かせる
def 鳴かせる(animal)
send "#{animal.class}を鳴かす"
end
def 鳥を鳴かす() puts "ぽー" end
def 牛を鳴かす() puts "もー" end
def 豚を鳴かす() puts "ぶー" end
end
動物を鳴かせる.new.鳴かせる(鳥.new)
動物を鳴かせる.new.鳴かせる(牛.new)
2. Rubyのバージョン
間の差を埋める
1.9.0リリース
しばらくは
1.8と1.9の
混在状態になる
対応方針は2つ
(1) 1.9は無視する
現実的だが、
現実的すぎて
なんか腹立たしい
(2) 両方のバージョン
で動くプログラム
を書く
Rubyの恐るべき
非互換性の罠が
待ち受ける
例:sendの場合
sendの
もう1つの用途:
privateメソッドを呼ぶ
# Ruby 1.8の場合
class C
def m() puts "OK" end
private :m
end
C.new.m
C.new.send(:m)
# これはダメ
# これなら通る!
…ただし、
Ruby 1.8までなら。
Ruby 1.9(の一部)では
funcallを使わないと
privateメソッドが
呼べない
# ありし日のRuby 1.9の場合
class C
def m() puts "OK" end
private :m
end
C.new.m
C.new.send(:m)
C.new.funcall(:m)
# これはダメ
# これもダメ
# これなら通る!
まとめ
ver.
Ruby
1.8
send
funcall
privateメソッドを呼び 存在しない
出すことができる
Ruby
1.9
privateメソッドは呼
び出せない
privateメソッドを呼び出
すことができる
もうだめだ…
そんなあなたに
ダイナミック
プログラミング
「メソッドがないなら
定義すれば
いいじゃない」
unless Object.method_defined?(:funcall)
class Object
alias send funcall
end
end
ポイント1
if文やunless文の
なかにclass文が
書ける
while文の中にも
class文を書けるけど
あんまり意味ない
ポイント2
プログラムの実行時に、
(既存の)クラスに
メソッドを追加できる
オープンクラス
(いつでも定義の
追加が可能なクラス)
第二部 完
つづきはウェブで
今日のまとめ
agenda 1/2
第一部:Rubyプログラミングのツボ
1. protected
2. pとpp
3. unlessとuntil
4. クラスと名前空間
5. ファイル名とクラス名
6. 大クラス主義
agenda 2/2(真)
第二部:Rubyプログラミングの、押しすぎるとヤ
バいツボのうちのいくつか
1. sendを使ってクラス分岐を書く
2. リフレクションを使ってRubyのバージョン間
の差を埋める
ご静聴
ありがとう
ございました
Q?