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