PHP ユーザー定義関数に永続変数を持たせる
やりたいことは、JavaScriptで言うクロージャを作ること。
関数外のスコープにある変数を、関数内で保持していく。
useを使えば出来ると思ったら出来なかった。
useを使えるのは無名関数のみだったのか…。
<?php (function() { $cnt = 1; function increment() use(&$cnt) { // syntax error, unexpected 'use' echo $cnt; $cnt++; } })();
正解は、静的変数を使うこと。
<?php function increment() { static $cnt = 1; echo $cnt; $cnt++; } increment(); // 1 increment(); // 2 increment(); // 3 var_dump($cnt); // null
無名関数 + use(参照渡し)
= 関数定義 + 静的変数
= JavaScriptのクロージャ
お粗末さまでした。
if文を考える
以下のような if 文をよく見かける。
if has_white_skin && is_good_looking p 'まいやん' # 色白で美人なのが、まいやん elsif has_white_skin && is_cute p 'さゆにゃん' # 色白で可愛いのが、さゆにゃん end
ありがちだが、良くない。以下はもっと良くない。
if has_white_skin if is_good_looking p 'まいやん' # 色白で美人なのが、まいやん elsif is_cute p 'さゆにゃん' # 色白で可愛いのが、さゆにゃん end end
今後、スパゲティコードとして成長していくことが目に浮かぶ。
まず大切なのは、この処理には「白石麻衣」の場合と「井上小百合」の場合、その2通りの状態があること。
白石麻衣は「色白で美人」である。だが、どのような条件で「白石麻衣」と判断するか…それはどうでもいい。
大切なのは「白石麻衣」という状態が存在することだ。
状態を判断:色白で美人(どうでもいい) ↓ 状態:白石麻衣(大切) ↓ 処理:まいやん(ニックネーム。どうでもいい)
下の処理を見てみると、状態を判断(どうでもいい)と、処理(どうでもいい)だけしか書かれていない。
if has_white_skin && is_good_looking p 'まいやん' # 色白で美人なのが、まいやん
「状態が白石麻衣」であることは、どこにも書かれていない。
これを初めて読んだ人は「は?これ何について書かれているの?」となる。
以下の書き方が正解となる。
if has_white_skin && is_good_looking who = '白石麻衣' elsif has_white_skin && is_cute who = '井上小百合' end if who == '白石麻衣' p 'まいやん' elsif who == '井上小百合' p 'さゆにゃん' end
「状態の判断」と「処理」が分離された。
状態として、「白石麻衣」と「井上小百合」の2パターンが存在することが見て分かる。
JavaScript バブリング中毒
以下、セレクトボックスが3つ並んだHTML。
全てのセレクトボックスが選択完了した時、何か処理を実行したいとする。
<div id="wrapper"> <select id="select1"> <option value="">---</option> <option value="1">aaa</option> <option value="2">bbb</option> </select> <select id="select2"> <option value="">---</option> <option value="1">aaa</option> <option value="2">bbb</option> </select> <select id="select3"> <option value="">---</option> <option value="1">aaa</option> <option value="2">bbb</option> </select> </div>
昔の自分なら、こう書き始めていたと思う。
$('#select1').on('change', function() { }) $('#select2').on('change', function() { }) $('#select3').on('change', function() { })
だが、今日知ってしまった。
changeイベントだって、上位のDOMにバブリングすることを…。
$('#wrapper').on('change', function(e) { // 全て選択されたら、CSSを変更 if ($('#select1').val() && $('#select2').val() && $('#select3').val()) { $('#wrapper').css('background-color', '#0FF') } })
もう全部バブリングでええんちゃう…?
Ruby 元のクラスを触らずに機能を拡張する
機能を追加することになった。
だが、元のクラスは触りたくない。
だってバグりそうだから。前のテストのやり直しをさせらせるかもしれない…。
自分の身を守るためにも、安全に機能を拡張する方法をまとめる。
以下クラスを拡張する。
フルネーム「白石麻衣」と表示出来れば、OK。
class Family def name '白石' end end
1.継承する
定番。ぱっと見、分かりやすい。
インスタンス変数に依存した実装になりやすい。副作用大。
class Full < Family def name super + '麻衣' end end full = Full.new p full.name # 白石麻衣
2.委譲する
委譲先から委譲元のインスタンス変数を触れないので、疎結合。良い。
class Full def name family = Family.new family.name + '麻衣' end end full = Full.new p full.name # 白石麻衣
3.特異メソッドを定義
インスタンスにメソッドを定義する。
family = Family.new def family.full_name name + '麻衣' end p family.full_name # 白石麻衣
4.モジュールをextend
module Full def full_name name + '麻衣' end end family = Family.new family.extend Full p family.full_name # 白石麻衣
5.Decoratorパターン
委譲の発展版。
インスタンス変数を内側・外側で、それぞれどう持ち回すかは謎。工夫が必要っぽい。
class Full def initialize(family) @family = family end def name @family.name + '麻衣' end end full = Full.new(Family.new) p full.name # 白石麻衣
うーん、前任者のコードを触りたくない…。
Ruby クロージャを使ってインスタンス変数を減らす
インスタンス変数は悪である。
だって、クラス内のどこからでも、値を改変できるから。
そこで、インスタンス変数を減らす手立を考えてみた。
以下、ゲッターもどき。
構造体を返す。構造体はラムダをgetメンバに持っている。
ラムダはクロージャになる。変数immutable_varは保持される。
変数の中身(yield)は、後ほどブロックで渡す。
def make_getter immutable_var = yield struct = Struct.new(:get) struct.new( -> { immutable_var } ) end
以下、ゲッター兼セッターもどき。
getメンバ、setメンバを持つ。もちろんクロージャ。
def make_accessor immutable_var = yield struct = Struct.new(:get, :set) struct.new( -> { immutable_var }, -> new_var { immutable_var = new_var } ) end
以下で変数の中身を定義する。
'白石麻衣'、'平手友梨奈'となっているブロックの中身は、がっつり処理を書いてよい。
それら変数をまとめて、こちらも構造体で返す。
def make_immutables struct = Struct.new(:nogi, :keyaki) struct.new( make_getter { '白石麻衣' }, make_accessor { '平手友梨奈' }, ) end
コンストラクタで、上記構造体をインスタンス変数に入れる。インスタンス変数を使ってしまうが、これはしょうがないと思う。
def initialize @immutables = make_immutables end
変数を呼び出す。
呼び出し方が冗長な気もするが、憎きインスタンス変数を減らせたので、良しとする。
def execute p @immutables.nogi.get.call # 白石麻衣 p @immutables.keyaki.get.call # 平手友梨奈 @immutables.keyaki.set.call('長濱ねる') # 長濱ねるをセット p @immutables.keyaki.get.call #長濱ねる end
うーん、他に良い方法あるのかなぁ…。
Ruby ブロック付きメソッドの活用
普通は、スクリプトを読みながら、どういう順序・条件で処理が進むのかを調べる。
それがつらい。処理の詳細を見ないと、実行順や分岐の条件が分からないのがつらい。
だから、実行順や分岐の条件を、処理の詳細から分離する。
OOPのTemplateパターンやStateパターンを使えば、実現できる。だが、他に良い方法は無いだろうか。
if group == '乃木坂46' func1 func2 func3 elsif group == '欅坂46' func1 func3 func4 end
上記のように書けば、実行順と分岐が分離できたことになる。
うーん、でも何と言うか…。メソッド間の結合が弱いというか。
func1 と func2 の順番を逆にすることもできるし、間に処理をスッと挟むことも出来る。簡単に改変できるのが気持ちが悪い。
そこで、以下のような形を考えた。
def f1 p 1 yield end def f2 p 2 yield end def f3 p 3 end f1 do f2 do f3 end end # 1 # 2 # 3
ブロック渡しをする。PHPやJavaScriptだと、無名関数でネストさせることになる。
副次的なメリットとして、次のメソッドに渡したい引数を、返り値に含める必要がない。
# 返り値経由で渡す result = func1 func2 result[:arg1] # 返り値に含める必要がない def func1 arg1 = 'foo' yield arg1 end func1 do |arg1| func2 arg1 end
うーん、イマイチかなぁ…。
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、勉強しといて良かったよ!