OneDay

40歳からプログラマとして生活しています。

curryが良くわからなかったので、ひたすら引用したら少し見えてきた -Scala, Ruby

2016年で新たな言語に取り組むと宣言していました。SwiftPythonを候補に考えていたが、Scalaを選んだ。関数型言語と呼ばれるものだ。


なぜScalaを選んだか。他の言語を通して、Rubyを深めたいのと、ちょっと変わった組み合わせができれば、それがプログラマとしての付加価値に繋がるかなと考えた。SwiftPythonは新しい書籍がバンバン出ていて、新鮮な書籍に困らないという良さがあったが、一から文字列や配列といったことを学んで拡張ライブラリを用いて何かを作っていくという過程を踏むというのが、時間という制約がある41歳からしてみれば勿体無かった。なんでScalaやりはじめました。


で、本題。なぜcurryを取り上げたか。



この書籍、ここ数日Curryという概念というが私を縛り続けている。その書籍p34にあるEXERCISE 2.3

カリー化(currying)では、引数2つの関数fが、fを部分的に適用する引数1つの関数に変換される。この実装を記述せよ。
(こたえ)

def curry[A,B,C](f: (A, B) => C): A => (B => C) =
  a => b => f(a, b)

この数式に何の意味があるのか?そもそもカリーって何だ。
カリー化 - Wikipediaによると

複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること(あるいはその関数のこと)である。

よく分からない。勉強してきたRubyを通してならば、ある程度掴めるのではないか、とググッてコードと説明を引用する。Rubyは2.1.0です。

書籍引用 パーフェクト Ruby on Rails p285 Proc#curry

add = Proc.new{|x, y| x + y} #=> #<Proc:0x007f8f9e7383d8@(pry):13>
inc = add.curry.(1) #=> #<Proc:0x007f8f9e70acd0>
inc.(2) #=> 3

inc.(2)は他にもこのようなシンタックスシュガーがある。inc.call(2), inc[2]

この引数(1,2)のadd関数を遡ってみる
①inc.(2) ②add.curry.(1).(2)
単純にincを置き換えただけだが、これもちゃんと3を返す。まるでメソッドチェーンのように左から順に計算が積み上がっていく。add.curryでadd関数がcurry化してできた関数 ▶ add.curry.(1)は左の関数に引数1を取った関数 ▶ add.curry.(1).(2)は左の関数に引数2を取った関数でadd関数の引数数と引数が揃うから、これでadd関数が返る。


「関数型Ruby」という病(3) - カリー化(Proc#curry, Proc#flip) - ( ꒪⌓꒪) ゆるよろ日記から引用

[80] pry(main)> f = lambda{|a,b,s| s.to_s.gsub(a,b) }
=> #<Proc:0x007fa042026068@(pry):94 (lambda)>
[87] pry(main)> arr = [:foo, :bar,:oooppai, :nooo, :baz]
=> [:foo, :bar, :oooppai, :nooo, :baz]

[88] pry(main)> arr.map{|s| f.(/oo/, "aa", s) } #ブロックを使用した場合
=> ["faa", "bar", "aaoppai", "naao", "baz"]

[89] pry(main)> g = f.curry.(/oo/).("aa") #カリーを使用
=> #<Proc:0x007fa0420f31f8 (lambda)>

[91] pry(main)> arr.map(&g)
=> ["faa", "bar", "aaoppai", "naao", "baz"]

[92] pry(main)> arr.map(&f.curry.(/oo/).("aa"))
=> ["faa", "bar", "aaoppai", "naao", "baz"]

カリー化を使えば、ブロックを書かなくても目的を達成できる。上記のブロック内では、第1引数と第2引数が固定で、第3引数のみがarrの各要素に置き換えられるわけだ。であれば、最初から第1引数と第2引数を部分適用済みの関数があればよい。関数fをカリー化して、第1引数と第2引数に/oo/と"aa"を部分適用させた関数gを用意した。この関数gは1引数の関数なので、mapに渡すことができる。結果、arrの各要素を第3引数として適用した結果を得ることが可能だ。なお、カリー化はmapに渡す際に行っても問題ない。このように、多引数の関数をカリー化した上で部分適用し、1引数関数を取る高階関数に渡すのが、カリー化の主な用途だ。

Ruby lambdasから引用Ruby lambdas · GitHub

# Example 1
l = lambda { |x, y, z| x + y + z }
l.curry[1][2][3] # => 6

# Example 2
a = l.curry[1] # => <Proc:0x007fc759a22920 (lambda)>
b = a[2]       # => <Proc:0x007fc759a68b00 (lambda)> 
b[3]           # => 6

# Better real world example
apply_math = -> fn, a, b { a.send fn, b }
add = apply_math.curry.(:+)
add.(1, 2) # => 3
increment = add.curry.(1)
increment.(1) # => 2
increment.(5) # => 6

curryの引数は、数値だけでなく関数も可。Example 2のb[3]は遡れば、a[2][3] となり、さらにl.curry[1][2][3]となるからEcample 1と同じ。l.curry[1][2][3]はl.curry.(1).(2).(3)と同じ。

Procを制する者がRubyを制す(嘘)から引用 http://melborne.github.io/2014/04/28/proc-is-the-path-to-understand-ruby/

join = ->suffix,body{ body + suffix }.curry #Procオブジェクトをカリー化する

# カリー化されたProcオブジェクトに一部の引数を部分適用する。
# これにより残りの引数を受けるProcオブジェクトが生成される。
join['ist'] # => #<Proc:0x007fdf011ce1b0 (lambda)>

%w(real social ruby).map(&join['ist']) # => ["realist", "socialist", "rubyist"]
%w(sell climb haskell).map(&join['er']) # => ["seller", "climber", "haskeller"]
%w(music physic Janis).map(&join['ian']) # => ["musician", "physician", "Janisian"]

joinというオブジェクトに代入したProcオブジェクトの引数の順序に注意。先にsuffixがある。curry化は関数で設定された引数を順番に呼んでいく。


チルダがRubyカレーをもっと好きにさせるから引用 http://melborne.github.io/2012/06/30/tilde-makes-curry-better/

multi = ->x,y{ x * y }.curry # => #<Proc:0x0000010109ae00 (lambda)>

multi_by2 = multi[2] # => #<Proc:0x0000010109a928 (lambda)>
[*1..10].map { |i| multi_by2[i] } # => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

lambdaブロックを直接curry化している。最初の引数を固定して新たな関数を作る。5を引数にしたら配列の要素に5を掛ける関数となる。


RubyAPI(2.2.0)から引用 http://docs.ruby-lang.org/en/2.2.0/Method.html#method-i-curry

def foo(a,b,c)
  [a,b,c]
end

proc  = self.method(:foo).curry
proc2 = proc.call(1, 2)          #=> #<Proc>
proc2.call(3)                    #=> [1,2,3]

def vararg(*args)
  args
end

proc = self.method(:vararg).curry(4)
proc2 = proc.call(:x)      #=> #<Proc>
proc3 = proc2.call(:y, :z) #=> #<Proc>
proc3.call(:a)             #=> [:x, :y, :z, :a]

curry(arity)->proc curryは引数をとると関数の引数数を決められる。curry(4)とすれば4が引数ということだ。

着眼すべきは、Object#methodを用いて、methodをオブジェクト化している。Ruby2.2以降はこの対応によって、ブロックだけでなくメソッドもcurry化できる。以前のバージョンはNoMethodErrorが返った。何気にすごい。メソッドからcurry化ができて、さらに引数の数が合わないからってArgumentErrorにならない。

proc  = self.method(:foo).curry
NoMethodError: undefined method `curry' for #<Method: Object#foo> #2.1.0ではできない




いろいろと引用したりしたけれど、今時点でcurryを使って「おー」と思ったことが一つある。
result = h(g(f(x)))
ちょっと前までこんな数式見ただけで、スルーしていたが、これがcurryでやるとスゴく分かりやすくなる。Rubyを用いる。まずはこの数式のような例。やっていることは簡単。2倍する/ 10加算する/ 2乗するという3つの関数をlambdaを使ってオブジェクトにし、10を引数にして h(g(f(x)))という関数を作ってみる。

[59] pry(main)> calc1 =->x{x * 2}
=> #<Proc:0x007fb9f6cdf1d8@(pry):64 (lambda)>
[60] pry(main)> calc2 =->x{x + 10}
=> #<Proc:0x007fb9f6cf61f8@(pry):65 (lambda)>
[61] pry(main)> calc3 =->x{x ** 2 }
=> #<Proc:0x007fb9f78b1240@(pry):66 (lambda)>

[64] pry(main)> result1 = calc3.(calc2.(calc1.(10)))
=> 900

配列の要素にこの関数を当てはめてみる。

[65] pry(main)> ary=[*1..10]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[67] pry(main)> ary.map{|x|calc3.(calc2.(calc1.(x)))}
=> [144, 196, 256, 324, 400, 484, 576, 676, 784, 900]

この関数が長くなってしまったので簡潔にする。

[79] pry(main)> calc_123 = ->(x){calc3.(calc2.(calc1.(x)))}
=> #<Proc:0x007fb9f6dce968@(pry):84 (lambda)>
[81] pry(main)> ary.map{|x| calc_123.(x)}
=> [144, 196, 256, 324, 400, 484, 576, 676, 784, 900]

lambdaやprocを用いて関数をオブジェクト化して、関数を組み合わせると括弧が増えコードが読みづらい。ここからcurryしてみる。

[59] pry(main)> calc1 =->x{x * 2}
=> #<Proc:0x007fb9f6cdf1d8@(pry):64 (lambda)>
[60] pry(main)> calc2 =->x{x + 10}
=> #<Proc:0x007fb9f6cf61f8@(pry):65 (lambda)>
[61] pry(main)> calc3 =->x{x ** 2 }
=> #<Proc:0x007fb9f78b1240@(pry):66 (lambda)>

[68] pry(main)> calcfn = ->(fn1, fn2, fn3, a){fn1.(fn2.(fn3.(a)))}
=> #<Proc:0x007fb9f79820e8@(pry):73 (lambda)>
[69] pry(main)> calcfn_curry=calcfn.curry
=> #<Proc:0x007fb9f79a9b98 (lambda)>

[75] pry(main)> result = calcfn_curry.(calc3).(calc2).(calc1).(10)
=> 900

[77] pry(main)> calcfn_3 =calcfn_curry.(calc3).(calc2).(calc1)
=> #<Proc:0x007fb9f7a23420 (lambda)>
[78] pry(main)> ary.map{|x| calcfn_3.(x)}
=> [144, 196, 256, 324, 400, 484, 576, 676, 784, 900]

calcfn_curry.(calc3).(calc2).(calc1).(10)の何が「おー」と思ったかというと

  • 関数が横並びしてように見えて可視性があがったように見える。
  • 右端から順に計算していくことができる。(calc1).(10)で20が返り、(calc2).(20)で30が返り、(calc3).(30)で900が返る。
  • 左端からは途中でメソッドを切ってもcurry化されたブロックが返る。 calcfn_curry.(calc3).(calc2)で切って変数に代入できる。

(何か凄そうだけど、まだ良く分からない。)



最後に再びScalaRuby、同じような関数をcurryを用いる。

scala> val func= (x: Int, y: Int, z: Int)=> Array(x*10 , y*100, z*1000)
func: (Int, Int, Int) => Array[Int] = <function3>

scala> val func_curry = func.curried
func_curry: Int => (Int => (Int => Array[Int])) = <function1>

scala> func_curry(5)(5)(5)
res13: Array[Int] = Array(50, 500, 5000)
[87] pry(main)> func = ->(x, y, z){ [x * 10, y * 100, z * 1000] }
=> #<Proc:0x007fb9f6c07030@(pry):92 (lambda)>
[88] pry(main)> func_curry = func.curry
=> #<Proc:0x007fb9f6bf6780 (lambda)>
[89] pry(main)> func_curry[5][5][5]
=> [50, 500, 5000]
[90] pry(main)> func_curry.(5).(5).(5)
=> [50, 500, 5000]

銘菓 なんばん往来 が東日本でも買えました

 

【九州銘菓】さかえ屋 なんばん往来 8個入り

 なんばん往来という洋菓子。二十数年前に出会ってから、福岡のお菓子の中では一番好き。福岡に帰省してたとき、なんばん往来をお土産として選んだ記憶がある。空港に売っていない時代はさかえ屋の店舗やスーパーで買って、スーツケースに詰め込んだものだ。一つ一つ紙箱で個装され、高級感がある。もちろん美味しい。

 

お土産としての欠点を上げれば、嵩張ること、持ち運び方法にもよるが箱が潰れやすいこと。しかし、それにも拘らず、これをお土産として選んだのは、これが一番美味しくて格好いいと思っていたからだ。

 

先日、松本市にあるシャトレーゼの店舗に行った。正月のお菓子を買うためだったが、そこで衝撃的な事実が発覚した。なんばん往来が売っていたのだ

 

早速調べる。

さかえ屋 - Wikipedia

粉飾決算、経営再建、シャトレーゼの出資(株式の3分の2だから子会社化している)といろいろとあったみたい。創業者はもう経営に関与していない。シャトレーゼ甲府の会社だから、その販売店ではなんばん往来が買えるのだ。

ということで、福岡帰省時のお土産リストからこのお菓子は無くなった。こちらでも買えるという嬉しさもあるが、やはり悲しい。

Rubyクラス変数 @@var

あけましておめでとうございます。

新たな年になりましたので、葬送Webサービスを展開するNoGraveの管理人として本年の抱負を述べます。

  1. Webサービスを新規で2つやります。
  2. 積極的に技術の勉強会に参加します。
  3. Ruby/Ruby on Railsに加え、新たな言語習得します。


新たなWebサービスについて構想は出来ています。
https://nograve.netの IT x 葬送というコンセプトを中心におき、葬送を考える家族において、家族の団結を強めるようなWebサービス作ります。もう一つは、IT x 介護で一つ作ります。ここまで6月までにドメインとって、掛かる費用は出し惜しみせず、一つはHerokuでやってみようか思います。
2つ目の勉強会は昨年11月から参加したのですが、上級者との会話は刺激になりますし、方向性の確認になります。生活する長野での勉強会は少ないので週末を利用し、東京の勉強会に参加する予定です。
最後は新たな言語習得。複数の言語を知っていたほうがやれることは増えるし、プログラミングの造詣が深くなる。Rubyを中心に置きつつも、違う言語習得を行う。候補はSwiftか、Pythonかな。
今年は、頭を使って、今までの勉強の成果を発揮したいと思います。よろしくお願いします。


さて、Rubyのクラス変数。Rubyのしくみ -Ruby Under a Microscope-という書籍で、ようやく理解できました。(出来たと思っています。)復習を兼ねてクラス変数のコード例を記載します。


たとえば、チームでの成果をクラス変数に集計できる。

class Lupin3
	@@treasure = 0

	def job
		@@treasure += 1
	end

	def self.treasure
		@@treasure
	end
end

goemon = Lupin3.new
fujiko = Lupin3.new

goemon.job
fujiko.job

p Lupin3.treasure #2が返る

このクラスから生成されるオブジェクトはドロボウで、ドロボウは仕事をしたら財宝が増えるという簡単なコード。@@treasureは各オブジェクトが仕事をした集計が反映。

このクラス変数、スーパークラスでも値を引き継ぐ。@@treasureはLupin一族の財宝の集計として使える。

class Lupin1
	@@treasure =0

	def self.job
		@@treasure += 1
	end

	def self.treasure
		@@treasure
	end

end

class Lupin2 < Lupin1; end

class Lupin3 < Lupin2
	def self.job
		@@treasure += 2
	end

	def team_job
		@@treasure += 3
	end
end

Lupin1.job
Lupin2.job
Lupin3.job

p Lupin1.treasure #4が返る
p Lupin2.treasure #4が返る
p Lupin3.treasure #4が返る

gigen = Lupin3.new
goemon = Lupin3.new
fujiko  = Lupin3.new

gigen.team_job
goemon.team_job
fujiko.team_job

p Lupin1.treasure #13が返る
p Lupin2.treasure #13が返る
p Lupin3.treasure #13が返る

Lupin1、Lupin2、Lupin3のクラスそれぞれに仕事をさせるとLupin1とLupin2は+1され、Lupin3は+2される。よって、それぞれのクラスが一度ずつjobメソッドを実施すると@@treasureの値は4になる。Lupin3はteam_jobというメソッドがあり、オブジェクトに仕事をさせると+3加算される。gigenとgoemonとfujikoにteam_jobさせたから、3 x 3加算される。@@treasureは4+9で13となる。treasureメソッドを用いてどのクラスからでも同じ値を得る。そうサブクラスを含めて同じ値を共有するのだ。うーん、どこかで使えそう。

しかし、そう単純ではない。Lupinの例では使用したメソッドがすべて加算だったから、単純に見えただけで、これが文字列の置き換えだったらどうなるか示す。

class Klass
	@@class_val = "class_val in Klass"

	def class_val
		@@class_val
	end
end

class ChildKlass < Klass
	@@class_val = "class_val in ChildKlass"
end

p Klass.new.class_val #"class_val in ChildKlass"が返る

class GrandChildKlass < ChildKlass
	@@class_val = "class_val in GrandChildKlass"
end

p Klass.new.class_val #"class_val in GrandChildKlass"が返る

class ChildKlass < Klass
	@@class_val = "class_val AGAIN in ChildKlass"
end

p Klass.new.class_val #"class_val AGAIN in ChildKlass"が返る

Klass クラスはクラス変数@@class_valを持っており、その値を返すclass_valメソッドを持つ。Klassを継承したChildKlassで@@class_valを定義するとChildKlassで定義したものがclass_valメソッドで返る。GrandChildKlassでも同じ挙動。継承で一番底辺にいるクラス変数を見るのか...? 違う。再度ChildKlassで@@class_valを再定義すると、その再定義した文字列が返る。つまり、クラス変数は一番新しいものに上書きされる。

今読んでいるRubyのしくみ -Ruby Under a Microscope-にはこの動きをこう書いている。

Rubyはクラスをまたいでそのサブクラスすべてでクラス変数を共有する
Rubyは対象クラスとそのスーパークラスのいずれかにクラス変数が既に存在していないかをチェックする
Rubyは最も上位のスーパークラスで見つかったクラス変数のコピーを使用する

Rubyのしくみ -Ruby Under a Microscope-はちょっと難しいが、Rubyのうごきを知ることができて面白い。

0から始めるRuby というイベントに行ってきた 長野県松本市

educode.doorkeeper.jp

長野県でRubyのイベント。先月に引き続き行ってきた。前回は受講者が私だけだったのでマンツーマンでレッスンを受けることが出来たが、本日は受講者が私以外に3人いた。一人はWordPress専門のフリーランスの方で、残りの二名は初心者であった。初心者の一人は小学五年生。

もちろん、初学者向けの内容になった。始めて受講する講師によるRubyのプレゼン。もうすぐプログラミング歴2年になる私にとっては当然といえば当然という内容だったが、本やネットではない、リアルなプレゼンに聞き入ってしまった。ちゃんと身についているんだと自分の理解度を測るうえでも面白かった。

講師のプレゼンの内容は、Rubyとはから始まり、難易度的にはたのしいRuby 第4版 の第一章程度だった。Rubyを始めた当初(およそ2年前)も、この第一章程度の内容は理解したつもりでいた。しかし、プレゼンを拝聴しながらチェックしていくと、本当に理解できたのはここ最近のような気がしている。2年前と何が違うか。自分で何かを作った。コードを読む機会が増えた。最近Rubyメタプログラミングで頭をいじめている。多分この差だ。

2年前に今日のプレゼンを聞いたとしても分かったつもりになっただけだったろう。まぁ、でも最初はこんなものなんだろう。勉強でもスポーツでもそうである程度日数が必要なんだと思う。そういう意味では初心者のお二方はこれからなので楽しんでやって欲しい。

プレゼン後、一つの演習が出た。「1から20の数を表示させてください。ただし、3の倍数のときはFizzを5の倍数のときはBuzzを数字の代わりに置き換えて下さい。」というものだ。私は3分くらいで解けたが、初心者のお二人には難しそう。ここで貴重にも小学生にプログラミングを教える経験が出来た。毎日PCを触っている子なのでタイピングとかは早いし、頭の回転が速そう。小学生に分かりやすい言葉を選んで説明するが、なかなかこれが難しい。イテレータの説明、if文の説明、小学生に分かってもらうのは難しい。頭の良い子だったので、なんとなくは理解できて、興味をもったような気がする。

プログラミング、楽しい、こんなこと出来るんだというのを少し伝えたくて、出来たコードに20の代わりに100とか1000とか入れて実行してみて、と伝えると一瞬でターミナルに表示される出力にびっくりしてた。じゃぁ10,000入れたら、1,000,000入れたらと乗って来てくれた。やってごらん。さすがに1,000,000になると出力に時間が掛かるようになった。それを見て喜んでいる。自分の息子に教える機会を想像して嬉しくなってしまった。(うちの子まだ3歳と0歳だけど。。。)


Rubyやって2年経つのに何で0からのイベントに参加したのか。

WordPressの方からも少しお話を聞いて刺激を受けたり楽しかった。来月も企画されたら参加しよう。

knowers.jp
こちらで実施されています。

Ruby歴半年くらいの自分へ HelloWorld (後編)

nograve.hatenadiary.jp
の続き。attr_writer, attr_accessorについて。

#sample8
class HappyBirthday
	attr_reader :name, :birthday_month, :birthday_date
	def initialize(name="Alice", birthday_month=1, birthday_date= 1)
		@name = name
		@birthday_month = birthday_month
		@birthday_date = birthday_date
	end
end

このコード例でattr_writerについて書いていく。pryでやっていく。

[1] pry(main)> load './mynote.rb' #ファイル名がmynote.rb
=> true
[2] pry(main)> alice = HappyBirthday.new
=> #<HappyBirthday:0x007fc5d3dd0220 @birthday_date=1, @birthday_month=1, @name="Alice">
[3] pry(main)> alice.birthday_date
=> 1
[4] pry(main)> alice.birthday_date =2
NoMethodError: undefined method `birthday_date=' for #<HappyBirthday:0x007fc5d3dd0220>
from (pry):4:in `__pry__'

HappyBirthday.newを叩くと引数に何も指定していないため、initializeメソッドで設定した内容が返る。それをaliceというオブジェクトに代入している。しかし、誕生日を1月2日に変更したいという場合、どうしたら良いか。sample8の例ではbirthday_dateに書き込みできるメソッドがないため、alice.birthday_date = 2と打つとundefined method 'birthday_date='と出る。このエラーを解消するには、まさにbirthday_date=というメソッドを書けばよい。

[5] pry(main)> class HappyBirthday
[5] pry(main)*   def birthday_date=(integer)
[5] pry(main)*     @birthday_date=integer
[5] pry(main)*   end
[5] pry(main)* end
=> :birthday_date=
[6] pry(main)> a.birthday_date =2
=> 2
[7] pry(main)> a
=> #<HappyBirthday:0x007fc5d3dd0220 @birthday_date=2, @birthday_month=1, @name="Alice">

これでbirthday_dateに書き込みが可能となった。しかし、nameやbirthday_monthも変更することがあるかもしれない。その場合はname=メソッド、birthday_month=メソッドを定義すれば良いが、同名のインスタンス変数(@nameや@birthday_month)に値を入れるだけのメソッドを書くのは無駄に思える。この無駄を省くため、attr_readerと同様にattr_writerというアクセサが用意されている。下記のようにattr_writerをセットする。

[9] pry(main)> class HappyBirthday
[9] pry(main)*   attr_writer :name, :birthday_month, :birthday_date
[9] pry(main)* end
=> nil
[10] pry(main)> alice.name = 'Iris'
=> "Iris"
[11] pry(main)> alice.birthday_month =2
=> 2
[12] pry(main)> alice
=> #<HappyBirthday:0x007fc5d3dd0220 @birthday_date=2, @birthday_month=2, @name="Iris">

attr_writerでインスタンス変数の書き込みができることが分かった。attr_readerと合わせれば読み書きができそうである。

class HappyBirthday
	attr_reader :name, :birthday_month, :birthday_date
	attr_writer :name, :birthday_month, :birthday_date
end

これでもプログラムは動くが、冗長であるため、attr_accessorというものが用意されている。

class HappyBirthday
	attr_accessor :name, :birthday_month, :birthday_date
end


最後にinitializeメソッドについてだが、代表的なRubyの標準クラスでinitializeメソッドを持つものを上げるとこんな感じ

[5] pry(main)> String.new
=> ""
[6] pry(main)> Array.new
=> []
[7] pry(main)> Hash.new
=> {}

これらのクラスは箱を用意してくれる。Arrayクラスなんかは初期値を下記のように設定することもできる。

[1] pry(main)> a = Array.new
=> []
[2] pry(main)> b = Array.new(3, 2)
=> [2, 2, 2]
[3] pry(main)> c = [1, 2, 3, 4]
=> [1, 2, 3, 4]

では、attr_accessorだけのclassはnewメソッドは何が返るか、実験してみよう。

#sample9
class HappyBirthday
	attr_accessor :name, :birthday_month, :birthday_date
end
[1] pry(main)> load './mynote.rb'
=> true
[2] pry(main)> a = HappyBirthday.new
=> #<HappyBirthday:0x007f894591f188>
[3] pry(main)> a.name = "Alice"
=> "Alice"
[4] pry(main)> a.birthday_month = 1
=> 1
[5] pry(main)> a.birthday_date =1
=> 1
[6] pry(main)> a
=> #<HappyBirthday:0x007f894591f188 @birthday_date=1, @birthday_month=1, @name="Alice">

HappyBirthday.newの値をaに代入している。もちろんインスタンス変数の設定はしていないので、aオブジェクトの中身は空同然だ。aオブジェクトはHappyBirthdayクラスのattr_accessorで定義されたメソッドが使える。a.name = "Alice", a.birthday_month = 1, a.birthday_date =1と打っていけばすべてのインスタンス変数を埋めたaオブジェクトの完成である。しかし面倒くさい。

一度に設定可能なsettingメソッドを定義してみよう。

[7] pry(main)> class HappyBirthday
[7] pry(main)*   def setting(name, birthday_month, birthday_date)
[7] pry(main)*     @name = name
[7] pry(main)*     @birthday_month = birthday_month
[7] pry(main)*     @birthday_date = birthday_date
[7] pry(main)*   end
[7] pry(main)* end
=> :setting
[8] pry(main)> b = HappyBirthday.new
=> #<HappyBirthday:0x007f8945d3c478>
[9] pry(main)> b.setting('Bob', 2, 2)
=> 2
[10] pry(main)> b
=> #<HappyBirthday:0x007f8945d3c478 @birthday_date=2, @birthday_month=2, @name="Bob">

これで一度にインスタンス変数の値を更新することができたが、このメソッドはinitializeメソッドと変わりない。しかも最初にオブジェクトを生成し、オブジェクトのメソッドとしてsettingメソッドで設定する必要があり二度手間だ。initializeならばオブジェクトを生成する際に同時に各インスタンス変数の値を設定できる。オブジェクトにインスタンス変数の値をもたせる場合は、initializeメソッドを書かない積極的な理由はないと思われる。

Ruby歴半年くらいの自分へ HelloWorld (前編)

#sample1
class HelloWorld
	attr_accessor :name   # 外からアクセス出来る?くらいの認識だった
	def initialize(name = "Alice")
		@name = name  #インスタンス変数に代入しているのは分かる
	end

	def hello
		puts "Hello, #{name}" #なんで@nameじゃないの?
	end
end

alice=HelloWorld.new
alice.hello #=> "Hello, Alice" 

Rubyを始めて半年くらい経ったとき、どうしてもこのコードが理解できずにいたが、久しぶりにたのしいRubyを開いてみて、今なら当時の自分にちゃんと説明できそうな気がする。

当時の疑問

helloメソッドで#{name}とあるがなんで@nameじゃいけないの?


回答→@nameでも良い。helloメソッドの#{}の中身を@nameにして実行しても"Hello, John"は返る。理解を深めるためにattr_accessorを無効にしてみよう。下の2つのコードにおいて、sample2はhelloメソッドに#{@name}を、sample3はhelloメソッドに#{name}を設定してみる。結果はsample2だけ"Hello John"が返る。

#sample2
class HelloWorld
	#attr_accessor :name
	def initialize(name = "John")
		@name = name
	end

	def hello
		puts "Hello, #{@name}"
	end
end

a = HelloWorld.new
a.hello # =>Hello, Johnが返る
#sample3
class HelloWorld
	#attr_accessor :name
	def initialize(name = "John")
		@name = name
	end

	def hello
		puts "Hello, #{name}"
	end
end

a = HelloWorld.new
a.hello # =>NameError, undefined local variable or method `name'

解説:sample2は、helloメソッドに#{@name}を設定しているが、@nameとはインスタンス変数で同じレベル(ここではHelloWorldクラス)で値を持ち運ぶことが出来る。initializeメソッドの@nameの値は、helloメソッドでも使えるということだ。
一方sample3を見てみると、helloメソッドでnameが定義されていないとというエラーとなる。nameといれただけではnameって何?とrubyは理解出来ない。なぜエラーになったのか、local variableとmethodを用いてエラーを解消するコードを書いてみる。

#sample4
class HelloWorld
	#attr_accessor :name
	def initialize(name = "John")
		@name = name
	end

	def hello
		name = @name
		puts "Hello, #{name}"
	end
end

a = HelloWorld.new
a.hello # =>Hello, Johnが返る

解説: sample4ではhelloメソッド内にnameという変数を設定した。そこに@nameを代入している。これでinitializeメソッドで設定した@nameをhelloメソッドに引き渡せたわけだ。

#sample5
class HelloWorld
	#attr_accessor :name
	def initialize(name = "John")
		@name = name
	end

	def name
		@name
	end

	def hello
		puts "Hello, #{name}"
	end
end

a = HelloWorld.new
a.hello # =>Hello, Johnが返る

解説:sample5ではnameメソッドというものを追加した。initializeメソッドの@nameをnameメソッドに引き渡せすことができる。helloメソッドから#{name}でnameメソッドで設定した式が返される。nameメソッドが出たのでアクセサのことに触れる。このnameメソッドの3行ははattr_reader :nameで置き換えることができる。つまりattr_reader:nameはnameメソッドと同じ働きをする。置き換えた後のコードをsample6で確認してみよう。アクセサがattr_readerに変わった以外はsample1とほぼ同じだ。

#sample6
class HelloWorld
	attr_reader :name
	def initialize(name = 'John')
		@name = name
	end

	def hello
		puts "Hello, #{name}"
	end
end

a = HelloWorld.new
a.hello # =>Hello, Johnが返る

当時の私の声が聞こえる。「sample5にあったnameメソッドを用いたほうが分かりやすい気がする」→わかりやすさという点では慣れ。アクセサを使用する大きな利点は使用するインスタンス変数が増えたとき。例えばbirthday, gender, ageなどの属性をこのclassに追加した場合、アクセサだと attr_reader :name, :birthday, :gender, :ageとし、必要に応じてinitializeメソッドインスタンス変数に初期値を入れれば良いが、アクセサを用いなければ、def name, def birthday, def gender, def ageとインスタンスを返すだけのメソッド定義を4つもしなければいけない。


再び当時の私の声が聞こえる。。「そもそもnameメソッドというのがピンと来ない...」
うーん、それではここまでを踏まえて、違うclassで例を挙げてみる。HappyBirthdayクラスで、そのクラスはname, birthday_month, birthday_dateの個人情報を持っていて、本日誕生日の人にメッセージを出力するmessageメソッドを実装することにする。

#sample7
require 'date'
class HappyBirthday
	attr_reader :name, :birthday_month, :birthday_date
	def initialize(name="Alice", birthday_month=1, birthday_date= 1)
		@name = name
		@birthday_month = birthday_month
		@birthday_date = birthday_date
	end

	def message
		if Date.today.month == self.birthday_month && Date.today.day == self.birthday_date
			puts "Happy Birthday! ,#{name}."
		else
			puts "Hello, #{name}"
		end
	end
end

birthday = Date.today
alice = HappyBirthday.new('Alice', (birthday-2).month, (birthday-2).day)
bob = HappyBirthday.new('Bob', (birthday-1).month, (birthday-1).day)
charlie = HappyBirthday.new('Charlie', (birthday-0).month, (birthday-0).day)
dick = HappyBirthday.new('Dick', (birthday-1).month, (birthday-1).day)

[alice, bob, charlie, dick].each do |p|
	p.message
end

# =>Hello, Alice
# =>Hello, Bob
# =>Happy Birthday! ,Charlie.
# =>Hello, Dick が返る

sample7のHappyBirthday クラスで定義しているのはmessageメソッドだけではない。attr_readerによって、nameメソッド、birthday_monthメソッド、birthday_dateメソッドが定義されている。繰り返しになるが、attr_reader :name, :birthday_month, :birthday_dateによって以下がセットされていることに等しい。

def name
  @name
end

def birthday_month
  @birthday_month
end

def birthday_date
  @birthday_date
end

nameメソッド、birthday_monthメソッド、birthday_dateメソッドだが、それぞれのインスタンス変数の値をmessageメソッドに引き渡すだけのメソッドではない。これらのメソッドもオブジェクトに対して機能する。

#sample8
require 'date'
class HappyBirthday
	attr_reader :name, :birthday_month, :birthday_date
	def initialize(name="Alice", birthday_month=1, birthday_date= 1)
		@name = name
		@birthday_month = birthday_month
		@birthday_date = birthday_date
	end
end

birthday = Date.today
alice = HappyBirthday.new('Alice', (birthday-2).month, (birthday-2).day)
bob = HappyBirthday.new('Bob', (birthday-1).month, (birthday-1).day)
charlie = HappyBirthday.new('Charlie', (birthday-0).month, (birthday-0).day)
dick = HappyBirthday.new('Dick', (birthday-1).month, (birthday-1).day)


puts alice.name # => Alice が返る
puts alice.birthday_month # =>11 が返る
puts alice.birthday_date # =>28 が返る

[alice, bob, charlie, dick].each do |p|
	puts "#{p.name}'s birthday is #{p.birthday_month}/#{p.birthday_date}."
end

 
# =>Alice's birthday is 11/28.
# =>Bob's birthday is 11/29.
# =>Charlie's birthday is 11/30.
# =>Dick's birthday is 11/29. が返る

今後Railsを用いてMySQLのdatabaseを取り扱う場合、まさにこのalice.nameといった使い方でaliceの名前を抽出する。
たい焼きの型をクラス、生成されるたい焼きをインスタンスとして説明がある。あえて、インスタンス変数で設定する種のものを表現するとあんこの種類(こしあん、白あん、クリームなど)、価格、砂糖の量、といった要素を表すといえようか。a, b, cというたい焼きを作るとそれぞれを構成する分量がインスタンス変数の中に格納される。{a.anko = 'クリーム', a.price=90, a.sugar=20}, {b.anko ='こしあん', a.price=80, a.sugar=18}みたいな。たい焼きの型からは様々な要素をもつたい焼きがどんどん生成される。
attr_writer,attr_accessor,initializeについては続編で書こう。

久々にCodeIQの問題にチャレンジした[Ruby]

codeiq.jp

先日、0から始めるRuby というプログラミングスクールに行ってきた - ノグレブ管理人のblogというのに参加し、講師の方からRailsのスキルを上げるためにはRubyを勉強したほうが良いというアドバイスをいただきました。Railsは何か実装したいと思ったら、Googleで調べるとなんらか有効なGemがありそれを組み入れることで開発工数は劇的にあがります。時間が掛かるのは、CSS周り。デザインには答えがないので、際限なく時間がかかる傾向にあります。

Railsの思想「設定よりも規約」に従うことでアウトプットという点ではものすごく恩恵に預かっています。自分が作ったアプリケーションのModelなんて最低限のvalidationだけだったり、Controllerもほぼindex/show/new/create/edit/update/destroyで収まっています。Viewもしかりである。そんなに多くないコード量でWebアプリケーションが作れるRailsは凄いと思わざるを得ないです。

しかし、コードの記述を簡潔にしたい場合などはRuby力があった方がいいし、配列やハッシュでのちょっと上手な使い方もRubyで演習積んでメソッドなどを覚えていったほうがRailsで行えることの幅が広がると思います。ということでRailsの開発と同時にRuby力向上のためRubyの書籍読んだり、CodeIQの問題解いたりしていきます。

冒頭のCodeIQ問題にチャレンジしてみました。想定時間30分。えぇーと一日半くらい費やしました。しかも自動で採点されるのですが間違っていました。自分が作ったコードはたかだか30行です。この問題考えるのに配列と正規表現をテキストを開きました。懐かしのたのしいRuby 第4版です。お風呂の中やお布団の中でもどうしたらよいか、考えました。アプリケーション作っていく時にこういう過程は大切だと思います。


この問題を通して役に立ったことを2つメモに残しておきます。


repeated_permutation
[1,0,-1]という3つの数字があって、3桁の組み合わせをすべて出したいとき、permutationというメソッドがあります。

[1] pry(main)> [1,0,-1].permutation(3).to_a
=> [[1, 0, -1], [1, -1, 0], [0, 1, -1], [0, -1, 1], [-1, 1, 0], [-1, 0, 1]]
[2] pry(main)> [1,0,-1].combination(3).to_a
=> [[1, 0, -1]]

ちなみにpermutationで出力するものは順列を数え上げるというようです。combinationは組合せを数え上げるです。参照元Array - Rubyリファレンス

今回欲しかったのは、[1,0,-1]という数字を使って4桁の組み合わせをすべて出したかったのです。[-1,0,1,0],[1,0,-1,0]など入るべき数字の組み合わせと順序も全部。配列の一番目で[1,0,-1]、二番目で[1,0,-1]のように繰り返しを使って、すべての組み合わせを出そうとしたけど、10桁だと自動的に10回繰り返し出来るメソッドを作れなかったため、何か良い方法がないかArray - Rubyリファレンスで探しました。

repeated_combination : 重複組合せを数え上げる。Ruby 1.9.2
repeated_permutation : 重複順列を数え上げる。Ruby 1.9.2

というものがありましたので、pryで試してみます。

[15] pry(main)> [1,0,-1].repeated_permutation(4).to_a.to_s
=> "[[1, 1, 1, 1], [1, 1, 1, 0], [1, 1, 1, -1], [1, 1, 0, 1], [1, 1, 0, 0], [1, 1, 0, -1], [1, 1, -1, 1], [1, 1, -1, 0], [1, 1, -1, -1], [1, 0, 1, 1], [1, 0, 1, 0], [1, 0, 1, -1], [1, 0, 0, 1], [1, 0, 0, 0], [1, 0, 0, -1], [1, 0, -1, 1], [1, 0, -1, 0], [1, 0, -1, -1], [1, -1, 1, 1], [1, -1, 1, 0], [1, -1, 1, -1], [1, -1, 0, 1], [1, -1, 0, 0], [1, -1, 0, -1], [1, -1, -1, 1], [1, -1, -1, 0], [1, -1, -1, -1], [0, 1, 1, 1], [0, 1, 1, 0], [0, 1, 1, -1], [0, 1, 0, 1], [0, 1, 0, 0], [0, 1, 0, -1], [0, 1, -1, 1], [0, 1, -1, 0], [0, 1, -1, -1], [0, 0, 1, 1], [0, 0, 1, 0], [0, 0, 1, -1], [0, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, -1], [0, 0, -1, 1], [0, 0, -1, 0], [0, 0, -1, -1], [0, -1, 1, 1], [0, -1, 1, 0], [0, -1, 1, -1], [0, -1, 0, 1], [0, -1, 0, 0], [0, -1, 0, -1], [0, -1, -1, 1], [0, -1, -1, 0], [0, -1, -1, -1], [-1, 1, 1, 1], [-1, 1, 1, 0], [-1, 1, 1, -1], [-1, 1, 0, 1], [-1, 1, 0, 0], [-1, 1, 0, -1], [-1, 1, -1, 1], [-1, 1, -1, 0], [-1, 1, -1, -1], [-1, 0, 1, 1], [-1, 0, 1, 0], [-1, 0, 1, -1], [-1, 0, 0, 1], [-1, 0, 0, 0], [-1, 0, 0, -1], [-1, 0, -1, 1], [-1, 0, -1, 0], [-1, 0, -1, -1], [-1, -1, 1, 1], [-1, -1, 1, 0], [-1, -1, 1, -1], [-1, -1, 0, 1], [-1, -1, 0, 0], [-1, -1, 0, -1], [-1, -1, -1, 1], [-1, -1, -1, 0], [-1, -1, -1, -1]]"
[16] pry(main)> [1,0,-1].repeated_permutation(4).to_a.size
=> 81
[17] pry(main)> [1,0,-1].repeated_combination(4).to_a.to_s
=> "[[1, 1, 1, 1], [1, 1, 1, 0], [1, 1, 1, -1], [1, 1, 0, 0], [1, 1, 0, -1], [1, 1, -1, -1], [1, 0, 0, 0], [1, 0, 0, -1], [1, 0, -1, -1], [1, -1, -1, -1], [0, 0, 0, 0], [0, 0, 0, -1], [0, 0, -1, -1], [0, -1, -1, -1], [-1, -1, -1, -1]]"
[18] pry(main)> [1,0,-1].repeated_combination(4).to_a.size
=> 15

[1,0,-1]の3つの数字で4桁作った場合の重複順列は3 x 3 x 3 x 3の81通りあるということですね。

このrepeated_***は4つのうち2つを選んでといった使い方ももちろんできます。

[22] pry(main)> [1,2,3,4].repeated_permutation(2).to_a
=> [[1, 1], [1, 2], [1, 3], [1, 4], [2, 1], [2, 2], [2, 3], [2, 4], [3, 1], [3, 2], [3, 3], [3, 4], [4, 1], [4, 2], [4, 3], [4, 4]]
[23] pry(main)> [1,2,3,4].repeated_combination(2).to_a
=> [[1, 1], [1, 2], [1, 3], [1, 4], [2, 2], [2, 3], [2, 4], [3, 3], [3, 4], [4, 4]]

正規表現
つぎにものすごく苦労したのは、さきほどの組み合わせから配列の中で[-1,1]という並びのものを見つけ出すというもの。-1のあとは必ず1があって、この条件を満たさないものは除外したいのです。当初は配列を1つずつ取り出す

ary.size.times do |i|
  if ary[i] == -1 && ary[i+1]
(略)

みたいな感じで構築しようとしました。しかし、[0,-1,1,0,0,1,0,-1,0,1]のように最初の[-1,1]の並びはOKだが後半の[1,0]はNGなので除外したい場合、配列のイリテーターではなかなか期待通りのメソッドが作れませんでした。これを正規表現でやったら結構うまくいきました。さきほどの[1,0,-1].repeated_permutation(4).to_aから[-1,1]のならびでないものを削り落とします。

result =[]
[1,0,-1].repeated_permutation(4).to_a.each do |ary|
    unless /-1[0-]/ === ary.join('')
      result << ary
    end
end

ary.join('')でArrayをStringにして"-1"のあとが'0'と'-'の場合はfalseとなり、'1'の場合だけ取り込まれます。最終的にはメソッドにして、いろんな条件を正規表現で入れ込むことが出来ました。正規表現を使うと配列で実現できなかった条件設定が柔軟に出来るようになった気がします。

def filtering(n)
  result = []
  [1,0,-1].repeated_permutation(n).to_a.each do |ary|
    if ary.inject(:+) == 0                          #配列の要素を足すとゼロになる
      if /^0*-1|^0*$/ === ary.join('')        #-1もしくは0で始まった後-1または全部ゼロならばOK
        unless /-1[0-]/ === ary.join('')      #-1のあと1だけを拾う。-1のあと0と-の要素はNG
          unless /-10*$/ === ary.join('')    # 最後が-1で終わる、もしくは-1のあと0が来て終わる要素はNG
            result << ary
         end
        end
      end
  end
  result
end


とスゴく勉強になった記事を書いたのですが、肝心のテストは「左右反転は同じものとしてカウントします」ということを考慮しておらず落第しています。あとこのコードでは処理が遅くてNGとなりました。本当にプログラミングは奥が深いですが、知っていくとやれることが広がるので面白いです。パーフェクトRuby読み返します。

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)