青木峰郎 日本 ruby の会

Post on 12-Jan-2016

124 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

ふつうの Ruby プログラマに贈る Ruby プログラミング講座. 13-B-3. 青木峰郎 日本 Ruby の会. 自己紹介. 青木峰郎 (日本 Ruby の会). Ruby がらみで やってること. Ruby 関係のおしごと. 書籍 『Rubyist Magazine 出張版 』 MYCOM 書籍 『Ruby レシピブック第二版 』 SBC r 書籍 『Ruby ソースコード完全解説 』 Impress 書籍 『Ruby を 256 倍使う本 無道編 』 ASCII リファレンスマニュアル刷新計画 隊長 - PowerPoint PPT Presentation

TRANSCRIPT

青木峰郎青木峰郎日本日本 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?

top related