CoffeeScript Privateメンバ
= で定義すれば、private
: で定義すれば、public
となる。
Privateメンバは、コンストラクタ関数内のローカル変数として、
クロージャによって保持されている…と考えることが出来る。
class Sony # public pubVar: 'nogi46' pubFunc: -> console.log '乃木坂46' # private priVar = 'keyaki46' priFunc = -> console.log '欅坂46'
sony = new Sony() console.log sony.pubVar # nogi46 sony.pubFunc() # 乃木坂46 console.log sony.priVar # undefined sony.priFunc() # エラー
AltJS、ECMA6といっても、JavaScriptの根本的な概念は変わっていないので…。
ECMA5、勉強しといて良かったよ!
今さらCoffeeScriptでクロージャ
仕事でCoffeeScriptを使っている。
練習も兼ねて、クロージャを書く。
CoffeeScript、Rubyと似ている…。
素のJavaScriptと比べて、色々と省略できすぎるため、初見では何を書いているかさっぱり分からない。
引数を2倍にする関数、3倍にする関数を返す。
factory = (type, num) -> { double: -> num = num * 2 console.log num triple: -> num = num * 3 console.log num }[type]
numは、関数スコープの外側に保持される。
double関数, triple関数はそれぞれクロージャとなる。
素のJavaScriptに書き換えると、以下になる。
var factory = function(type, num) { return { double: function() { num = num * 2 console.log(num) }, triple: function() { num = num * 3 console.log(num) } }[type] }
クロージャをそれぞれ実行。
変数numが保持されていることが分かる。
double = factory('double', 1) double() # 2 double() # 4 double() # 8 triple = factory('triple', 3) triple() # 9 triple() # 27 triple() # 81
elseif・switchは撲滅して連想配列
前回に引き続き、今回も同じテーマで。
以下、憎くて仕方ないelseif文。
id = 'nogi' if id == 'nogi' then name = '乃木坂46' elsif id == 'keyaki' then name = '欅坂46' else name = 'ソニーミュージック' end p name # 乃木坂46
本当にキモい。
たいした処理でもないのに、なぜ、こんなに行数を使うのか理解できない。
そこで、今日、良い記法を思いついた。
[ Ruby ]
id = :keyaki name = { nogi: '乃木坂46', keyaki: '欅坂46', }[id] || 'ソニーミュージック' p name # 欅坂46
存在しない id を指定すると、左辺が nil になる。
結果、右辺が実行される。短絡評価。
PHPで書くと、
<?php $id = 'keyaki'; $name = [ 'nogi' => '乃木坂46', 'keyaki' => '欅坂46', ][$id] ?? 'ソニーミュージック'; echo $name; // 欅坂46
PHP7の ??(Null合体演算子)を使うとスマート。
うーん、かなりイケてると思うんだが…。
elseifやswitch文は連想配列に置き換えてポリモーフィズム
elseifやswtich文は出来るだけ避けたい。
一連のスクリプトから状態を排除したいし、後々の改修で誰かがネストしまくるのが想像できる。
置き換えるには、Stateパターン等を使う必要があると思っていたが、実は連想配列で解決できることに気付いた。
以下、よく見るコード。Rubyで書く。
who = 'shizuka' if who == 'dora' then p '僕ドラえもん' elsif who == 'nobi' then p '僕のび太' elsif who == 'shizuka' then p '私しずか' end
とにかくキモい。このメソッド内で、dora / nobita / shizuka の分岐が、他に何度も繰り返されるのが目に浮かぶ。
これを連想配列にする。
hash = { dora: ->{ p '僕ドラえもん' }, nobi: ->{ p '僕のび太' }, shizuka: ->{ p '私しずか' }, } who = :nobi hash[who].call
ポリモーフィズムにもなっていて、結構イケてると思うんだけど…。
この、簡単に手を入れられない(深いネストを書けない)ところが良い。
実はelseif文は、連想配列に書き換えられる、という気付きでした。ちゃんちゃん。
Ruby 委譲
Rubyの便利な委譲な仕組み。
一つ目。Forwardableモジュール。
class Sony extend Forwardable def_delegators :@keyaki, :discord def initialize(keyaki) @keyaki = keyaki end end class Keyaki def discord print '僕は嫌だ!' end end sony = Sony.new(Keyaki.new) sony.discord # 僕は嫌だ!
def_delegatorsメソッドの引数に、委譲先のオブジェクト、委譲するメソッドを指定。
Sonyはdiscordメソッドを持っていないにもかかわらず、実行できている。
内部では、Keyakiオブジェクトが、discordを実行している。
二つ目。method_missing。
これは、PHPのマジックメソッド__call()と似たようなもの。
class Sony def initialize(keyaki) @keyaki = keyaki end def method_missing(name, *args) @keyaki.send name, *args end end class Keyaki def discord print '僕は嫌だ!' end end sony = Sony.new(Keyaki.new) sony.discord # 僕は嫌だ!
メソッド名を指定しなくていい分、こっちの方が楽かも。
何とかここらへんをうまく使って、Proxyパターンを楽に実装していきたい。
Rubyでクロージャ
JavaScriptでよく見るクロージャを、Rubyでもやってみる。
JavaScript 関数スコープ
・内側から外側の変数は利用できる。
・外側から内側の変数は利用できない。
JavaScriptの場合、関数スコープを利用して、クロージャを実装する。
Rubyの場合、ブロックスコープを利用して、クロージャを実装する。
Procオブジェクトを返す関数を定義する場合。
def createCounter num = 0 Proc.new do num += 1 p num end end counter = createCounter counter.call # 1 counter.call # 2 counter.call # 3 counter.call # 4 counter.call # 5
ProcオブジェクトからProcオブジェクトを返す場合。
createCounter = Proc.new do num = 0 Proc.new do num += 1 p num end end counter = createCounter.call counter.call # 1 counter.call # 2 counter.call # 3 counter.call # 4 counter.call # 5
クロージャをあまり実務で使ったことない。もっと積極的に使おう。
Ruby Procを触る
引数にブロックを取る関数。
def doSomething(something, &toDo) toDo.call(something) end
PHP = 無名関数
Ruby = ブロック
という考え方で良いと思う。
以下、do から end がブロック。
Rubyでは、ブロックも引数の一種に考えるイメージだろうか。
doSomething 'メロン' do |food| p food << 'を食べる' end
メロンを食べる
ではそのブロック(無名関数)を変数に入れたい場合はどうするか。
Procオブジェクトとしてラッピングする必要がある。
def doSomething(something, toDo) toDo.call(something) end eatFood = Proc.new do |food| p food << 'を食べる' end doSomething 'リンゴ', eatFood
リンゴを食べる
こっちは、doSomethingの引数toDoに「&」が無い。
# ブロックを渡す場合 def doSomething(something, &toDo) # Procオブジェクトを渡す場合 def doSomething(something, toDo)
この「&」は、普通のブロックをProcオブジェクトに変換している…らしい。
Procオブジェクト。まだよく分からないが、ここらへんを理解できると、後々楽になりそうな気がする。