ログ日記

作業ログと日記とメモ

引数に関数を渡したい

配列のオブジェクト全てが有効かどうかをチェックする関数があるとする。

<?php
...

function isActive(Array $objs){
    foreach ($objs as $obj){
        if ($obj->status == SLEEP){
            return false;
        }
    }
    return true;
}

function isSleep(Array $objs){
    foreach ($objs as $obj){
        if ($obj->status == ACTIVE){
            return false;
        }
    }
    return true;
}

こういう関数が沢山出来るとリファクタリングしたくなって

<?php
function checkByStatus(Array $objs, $status){
    foreach ($objs as $obj){
        if ($obj->status == $status){
            return false;
        }
    }
    return true;
}


こう書き換えるわけ。
ところが status の種類が増えると困ってくる。

<?php
function isActive(Array $objs){
    foreach ($objs as $obj){
        if ($obj->status == SLEEP ||
            $obj->status == WAIT){
            return false;
        }
    }
    return true;
}

function isSleep(Array $objs){
    foreach ($objs as $obj){
        if ($obj->status == ACTIVE ||
            $obj->status == READY){
            return false;
        }
    }
    return true;
}

これを一つの関数にまとめるには引数増やすのだけでは難しい。
ifの部分だけ切り出せたらなーと。


prototype.js でいう Enumerable のメソッドが使いたいよー。
Haskellのように柔軟に引数を渡したいよー。>>= とか書きたいよー。
って思ってarray関数を眺めてみる。


http://jp.php.net/array-filter
http://jp.php.net/array-reduce


この辺を使うと出来そうではある。
ので

<?php
class ObjOperation{
    function checkByStatus(Array $objs, $callback){
        if (array_filter($objs, self::$callback))
            return false;

        return true;
    }

    function callbackActiveCheck(MyObj $obj){
        if ($obj->status == SLEEP ||
            $obj->status == WAIT){
            return true;
        }
        return false;
    }
}

書き換え。
成功した場合にtrueを返すのかfalseを返すのか悩んでしまうので、まだもうちょっと考える必要がある。
ただ配列をバラして関数の引数にすると、型チェックが出来るようになるという効果が増えるな。
これはMyObjの配列ですよーっていう Array MyObj $objs とか書けたらいいんだけど、そこまですると全然PHPっぽくなくなるな。


foreach で配列の全てをチェックするっていう処理はあちこちで使うので、prototype.jsのEnumerableみたいなものをグローバル関数で用意しようかなぁーと思ったりしている。
ただしチェック用の関数をいちいち定義しないといけないのが嫌だね。
create_functionのエイリアスでも作るかなぁ。

<?php
function array_all(array $objs, $callback){
    return (count(array_fill($objs, $callback)) == count($objs));
}

function array_exists(array $objs, $callback){
    return (count(array_fill($objs, $callback)) > 0);
}

function cf($type, $check){
    $f = create_function($type, $check);
    return $f;
}
<?php
...
// 使い方
if (array_all($objs, cf('MyObj $obj', 'return ($obj->status == ACTIVE);'))){
    ...
}elseif (array_exists($objs, cf('MyObj $obj', 'return ($obj->status == WAIT);'))){
    ...
}
...


もう少し省略したいな。

<?php
function c($type, $check){
    $f = create_function($type, 'return (' . $check . ');');
    return $f;
}

...
if (array_exists($objs, c('MyObj $obj', '$obj->status == WAIT'))){
    ...
}

これくらいに。

# ちなみに動作未確認