ためすう
Ruby で %Q を使ってみる
2019-07-05やったこと
Ruby で %Q を使ってみます。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
調査
ダブルクオートの文字列と同等です。
%Q を使ったもの
$ irb
irb(main):001:0> method = %Q(
irb(main):002:0" def hoge
irb(main):003:0" print __method__
irb(main):004:0" end
irb(main):005:0"
irb(main):006:0" )
=> "\n def hoge\n print __method__\n end\n\n"
irb(main):007:0> print method
def hoge
print __method__
end
=> nil
% を使ったもの
$ irb
...
irb(main):009:0> method2 = %(ddddd)
=> "ddddd"
irb(main):010:0> method2 = %(ddddd
irb(main):011:0"
irb(main):012:0"
irb(main):013:0" )
=> "ddddd\n\n\n"
irb(main):014:0> print method2
ddddd
=> nil
参考
第18章 Convention over Configuration
2019-07-05「Rubyによるデザインパターン」の読書メモです。
CoC パターンの主な動機は、設定の負荷を軽減することです。
test.rb
require 'uri'
class Message
attr_accessor :from, :to, :body
def initialize(from, to, body)
@from = from
@to = URI.parse(to)
@body = body
end
end
class MessageGateway
def initialize
load_adapters
end
def process_message(message)
adapter = adapter_for(message)
adapter.send_message(message)
end
def adapter_for(message)
protocol = message.to.scheme
adapter_class = protocol.capitalize + 'Adapter'
adapter_class = self.class.const_get(adapter_class)
adapter_class.new
end
def load_adapters
lib_dir = File.dirname(__FILE__)
full_pattern = File.join(lib_dir, 'adapter', '*.rb')
Dir.glob(full_pattern).each {|file| require file }
end
end
m = Message.new('from', 'ftp://xxxxx?ggg=2', 'bodybody')
mg = MessageGateway.new
mg.process_message(m)
出力結果
$ ruby test.rb
"FtpAdapter:send_message #<Message:0x00007fe951195658>"
注意点
規約に基づいたシステムの構築で危険なことの1つは、利用する規約の出来が不完全な場合、その結果としてシステムにできることの幅を制限するかもしれないということです。
Ruby で itself を使ってみる
2019-07-04やったこと
Ruby で itself を使ってみます。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
調査
itself は self 自身を返却します。
$ irb
irb(main):001:0> string = 'abcd'
=> "abcd"
irb(main):002:0> string.itself
=> "abcd"
irb(main):003:0> array = [3, 2, 1, 3, 2, 1]
=> [3, 2, 1, 3, 2, 1]
irb(main):004:0> array.group_by(&:itself)
=> {3=>[3, 3], 2=>[2, 2], 1=>[1, 1]}
nil とか false で試しても、 self が返却されました。
irb(main):001:0> nil.itself
=> nil
irb(main):002:0> false.itself
=> false
参考
第17章 カスタムオブジェクトを作る: メタプログラミング
2019-07-04「Rubyによるデザインパターン」の読書メモです。
test.rb
class CompositeBase
attr_reader :name
def initialize(name)
@name = name
end
def self.member_of(composite_name)
code = %Q{
attr_accessor :parent_#{composite_name}
}
class_eval(code)
end
def self.composite_of(composite_name)
member_of composite_name
code = %Q{
def sub_#{composite_name}s
@sub_#{composite_name}s = [] unless @sub_#{composite_name}s
@sub_#{composite_name}s
end
def add_sub_#{composite_name}(child)
return if sub_#{composite_name}s.include?(child)
sub_#{composite_name}s << child
child.parent_#{composite_name} = self
end
def delete_sub_#{composite_name}(child)
return unless sub_#{composite_name}s.include?(child)
sub_#{composite_name}s.delete(child)
child.parent_#{composite_name} = nil
end
}
class_eval(code)
end
end
class Tiger < CompositeBase
member_of(:population)
member_of(:classification)
end
class Jungle < CompositeBase
composite_of(:population)
end
class Species < CompositeBase
composite_of(:classification)
end
tony_tiger = Tiger.new('tony')
se_jungle = Jungle.new('southeastern jungle tigers')
se_jungle.add_sub_population(tony_tiger)
p tony_tiger.parent_population.class
species = Species.new('P. tigers')
species.add_sub_classification(tony_tiger)
p tony_tiger.parent_classification.class
出力結果
$ ruby test.rb
Jungle
Species
まとめ
メタプログラミングは、必要なコードを取得するためのもっとも簡単な方法になりうるアイディアで、キーボードでコードをすべて書くのではなく、実行時にプログラムに基づいて作り出すというやり方によるものです。
Ruby で %i を使ってみる
2019-07-04やったこと
%i を使って、シンボルを作ってみます。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
調査
%i を使ってシンボルが作れます。
$ irb
irb(main):001:0> %i(a b c)
=> [:a, :b, :c]
irb(main):002:0> %i(a\ b c)
=> [:"a b", :c]
参考
Ruby で collect を使ってみる
2019-07-02やったこと
collect メソッドを使ってみます。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
調査
mapメソッドは、collectの別名です。
$ irb
...
irb(main):002:0> p (1..8).collect {|n| n**3}
[1, 8, 27, 64, 125, 216, 343, 512]
=> [1, 8, 27, 64, 125, 216, 343, 512]
irb(main):003:0> p (1..8).map {|n| n**3}
[1, 8, 27, 64, 125, 216, 343, 512]
=> [1, 8, 27, 64, 125, 216, 343, 512]
collect でも、 map でも同じ結果が返って来てます。
参考
第16章 オリジナル言語を作る: Domain-Specific Languages
2019-07-02「Rubyによるデザインパターン」の読書メモです。
「内部」DSL
命令文をDSLの中に取り込んで、それをRubyプログラムとして解釈するアイディア
backup.pr
backup '/home/russ/documents'
backup '/home/russ/music', file_name('*.mp3') & file_name('*.wav')
backup '/home/russ/images', except(file_name('*.tmp'))
to '/external_drive/backups'
interval 60
test.rb
require 'finder'
require 'find'
class Expression
def |(other)
Or.new(self, other)
end
def &(other)
And.new(self, other)
end
end
class All < Expression
def evaluate(dir)
results = []
Find.find(dir) do |p|
next unless File.file?(p)
results << p
end
results
end
end
class And < Expression
def initialize(expression1, expression2)
@expression1 = expression1
@expression2 = expression2
end
def evaluate(dir)
result1 = @expression1.evaluate(dir)
result2 = @expression2.evaluate(dir)
(result1 & result2)
end
end
class Not < Expression
def initialize(expression)
@expression = expression
end
def evaluate(dir)
All.new.evaluate(dir) - @expression.evaluate(dir)
end
end
class FileName < Expression
def initialize(pattern)
@pattern = pattern
end
def evaluate(dir)
results = []
Find.find(dir) do |p|
next unless File.file?(p)
name = File.basename(p)
results << p if File.fnmatch(@pattern, name)
end
results
end
end
def backup(dir, find_expression=All.new)
puts "Backup called, source dir=#{dir} find expr=#{find_expression}"
end
def to(backup_directory)
puts "To called, backup dir=#{backup_directory}"
end
def interval(minutes)
puts "Interval called, interval = #{minutes} minutes"
end
def except(expression)
Not.new(expression)
end
def file_name(pattern)
FileName.new(pattern)
end
eval(File.read('backup.pr'))
まとめ
内部DSLの背景にある考え方は、とても簡単です。Rubyの構文ルールに合うようにあなたがDSLを定義します。そして、DSLで書かれた、やるべきことが記述されているプログラムに必要な基盤もあなたが定義します。
Ruby で defined? を使ってみる
2019-07-01やったこと
defined? を使ってみます。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
調査
$ irb
irb(main):001:0> defined? print
=> "method"
irb(main):002:0> defined? Hoge
=> nil
irb(main):003:0> class Hoge end
=> nil
irb(main):004:0> defined? Hoge
=> "constant"
存在しない Hoge クラスを引数に与えると、nil でしたが、
クラス作成後は constant が返ってきました。
参考
第15章 専用の言語で組み立てる: Interpreter
2019-07-01「Rubyによるデザインパターン」の読書メモです。
Interpreter の例
require 'find'
class Expression
def |(other)
Or.new(self, other)
end
def &(other)
And.new(self, other)
end
end
class All < Expression
def evaluate(dir)
results = []
Find.find(dir) do |p|
next unless File.file?(p)
results << p
end
results
end
end
class FileName < Expression
def initialize(pattern)
@pattern = pattern
end
def evaluate(dir)
results = []
Find.find(dir) do |p|
next unless File.file?(p)
name = File.basename(p)
results << p if File.fnmatch(@pattern, name)
end
results
end
end
class Bigger < Expression
def initialize(size)
@size = size
end
def evaluate(dir)
results = []
Find.find(dir) do |p|
next unless File.file?(p)
results << p if (File.size(p) > @size)
end
results
end
end
class Writable < Expression
def evaluate(dir)
results = []
Find.find(dir) do |p|
next unless File.file?(p)
results << p if (File.writable?(p))
end
results
end
end
class Not < Expression
def initialize(expression)
@expression = expression
end
def evaluate(dir)
All.new.evaluate(dir) - @expression.evaluate(dir)
end
end
class Or < Expression
def initialize(expression1, expression2)
@expression1 = expression1
@expression2 = expression2
end
def evaluate(dir)
result1 = @expression1.evaluate(dir)
result2 = @expression2.evaluate(dir)
(result1 + result2).sort.uniq
end
end
class And < Expression
def initialize(expression1, expression2)
@expression1 = expression1
@expression2 = expression2
end
def evaluate(dir)
result1 = @expression1.evaluate(dir)
result2 = @expression2.evaluate(dir)
(result1 & result2)
end
end
ins = (Bigger.new(2000) & Not.new(Writable.new)) | FileName.new('*.mp3')
puts ins.evaluate('./')
まとめ
インタープリタを使うと処理速度が遅くなる傾向があり、また高速化することは困難なため、Interpreter パターンの使用は高い性能を必要としない領域に制限するのがよいでしょう。また Interpreter パターンは確実にかなりの基盤を必要とするため、複雑さが生じます。
train_test_split でデータを分割する
2019-06-30やったこと
訓練データを学習データ、性能評価用のデータに分割します。
確認環境
$ ipython --version
6.1.0
$ jupyter --version
4.3.0
$ python --version
Python 3.6.2 :: Anaconda custom (64-bit)
import sklearn
print(sklearn.__version__)
0.19.0
調査
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
from sklearn import datasets
X.shape: (150, 4)
y.shape: (150,)
X_train.shape: (120, 4)
X_test.shape: (30, 4)
y_train.shape: (120,)
y_test.shape: (30,)
全体150個のデータのうち、20% の 30個がテストデータになっていることが分かります。