統計を計算するクラスを実装してみた[PHP][標準偏差][偏差値]
PECLの統計クラスが使えないので自分で実装してみた
<?php $target = mt_rand(0, 100); // generate Test Data $arr = array(); for($i=0; $i<10; $i++){ array_push($arr, mt_rand(0, 100)); } echo " ターゲット値: $target <br />"; echo "偏差値: " . Statistics::standardScore($target, $arr); class Statistics { public static function average(array $values) { return (float) (array_sum($values) / count($values)); } public static function variance(array $values) { // 平均値を求める $ave = self::average($values); $variance = 0.0; foreach ($values as $val) { $variance += pow($val - $ave, 2); } return (float) ($variance / count($values)); } public static function standardDeviation(array $values) { // 分散を求める $variance = self::variance($values); // 分散の平方根 return (float) sqrt($variance); } //偏差値を求める public static function standardScore( $target, array $arr) { return ( $target - self::average($arr) ) / self::standardDeviation($arr) * 10 + 50; } } ?>
ぼくのかんがえたさいきょうのPHP
PHPの改善案(不満)を日々挙げていく
「PHPあるある」のまとめ - Togetterまとめ
を読んでたら意外と勉強になってしまっていた
なんとなく面白い勉強法だと思ったのでやってみる
ずっと更新していくと自分の好みの言語が自然と浮かび上がってくるんじゃないか?
[不満点]
・「 -> 」は嫌い「 . 」にして欲しい。
・「 $ 」を付けなくてもいいと思う。変数は一種類しかないし
・関数の名前をRubyっぽくして欲しい。キャメルケースで頼む
・文字列と配列はオブジェクトにして欲しい。
随時更新予定
PHP初歩解説記事その1[swith][制御構文]
Twitterだとさすがにソースコードの解説は厳しいからこっちに書くことにした
基本ここ参照で
http://php.net/manual/ja/control-structures.switch.php
で、始めるよー
修正前
<?php $agent = $_SERVER['HTTP_USER_AGENT']; //switch文はここまで複雑なことはできない switch(true){//ここには条件判定したい変数をいれる //case: は //例えば //case: 0 や //case: "iPhone"みたいな使い方しかできない case(ereg("DoCoMo",$agent)): header("Location: http://〜"); break; case(ereg("Vodafone|J-PHONE|SoftBank|MOT-",$agent)): header("Location: http://〜"); break; case(ereg("UP.Browser",$agent)): header("Location: http://〜"); break; case(ereg("iPhone",$agent)): header("Location: http://〜"); break; default: header("Location: http://〜"); } ?>
修正後
<?php $agent = $_SERVER['HTTP_USER_AGENT']; if(ereg("DoCoMo",$agent) !== false){ header("Location: http://〜"); }else if(ereg("Vodafone|J-PHONE|SoftBank|MOT-",$agent) !== false){ header("Location: http://〜"); }else if(ereg("UP.Browser",$agent) !== false){ header("Location: http://〜"); }else if(ereg("iPhone",$agent) !== false){ header("Location: http://〜"); }else{ header("Location: http://〜"); } ?>
デバッグはしてないからエラーでるかもー><
CakePHPのヘルパーをStrategyパターンで実装する方法[1.3][Helper][デザインパターン]
Webアプリケーションを開発する際、認証機能も実装する事は今やほぼ必須です。
CakePHPでは簡単に認証機能を実装できるAuthコンポーネントをデフォルトでサポートしています。
このため、CakePHPにおいて、開発者はほとんど負担なく認証機能を実装することができます。
しかし、問題はViewレイヤーです。
ログイン状態の出力をViewファイル中に埋め込んだPHPでIF文でいちいち制御するのは最高に面倒です。
LoginHelperとLogoutHelperと2ファイルに分けて使い分けるのも手ではありますが、美しい解決ではありません。
私はこの問題の解法としてデザインパターンの1つ、「Strategyパターン」を用いる方法を紹介したいと思います。
ちょっと歪な設計になりましたが。。。
引用
Strategyパターンとは?
Strategyパターンの目的は、GoF本では次のように定義されています。
アルゴリズムの集合を定義し、各アルゴリズムをカプセル化して、それらを交換可能にする。
Strategyパターンを利用することで、アルゴリズムを、それを利用するクライアントからは
独立に変更することができるようになる。Strategy パターンは、オブジェクトの振る舞いに注目したパターンです。
Strategyパターンでは、それぞれの処理をクラスとして定義します。その際、クライアントにアクセスさせるための共通APIを用意しておくのがポイントです。これにより、処理クラスを利用する側は具体的な実装を意識することなく、共通のAPIで処理を実行できます。
また、処理の実行を処理クラスのオブジェクトに委譲することで、処理の切り替えができるようにしています。
Strategyパターンの構造
Strategyパターンのクラス図と構成要素は、次のとおりです。
Strategyクラス
それぞれの処理に共通のAPIを定義します。Contextクラスからは、Strategyクラスで定義されたAPIを通じて、ConcreteStrategyクラスで提供される具体的な処理を呼び出します。
ConcreteStrategyクラス
Strategyクラスのサブクラスで、Strategyクラスで定義されたAPIを実装したクラスです。このクラスに具体的な処理内容を記述します。
Contextクラス
Strategy型のオブジェクトを内部に保持し、具体的な処理をそのオブジェクトに委譲します。こうすることで、ConcreteStrategyクラスに依存することがなくなりますので、ConcreteStrategyクラスを切り替えることができます
Strategyパターンのメリット
Strategyパターンのメリットとしては、以下のものが挙げられます。
処理毎にまとめることができる
それぞれの処理がクラスにまとめられて実装されており、コードは処理内容に専念することができます。これにより、保守性が高まります。
また、新しい処理が追加された場合も、既存のコードに手を入れることなく、新しいクラスを作成するだけで済みます。
異なる処理を選択するための条件文がなくなる
1つのクラスやメソッドに異なる処理を記述した場合、if文やswitch文を使って処理を分岐することになります。これは、コードの可読性を落とすため、保守性・拡張性が下がります。Strategyパターンを適用すると、処理がクラス単位にまとめて実装されます。この結果、if文やswitch文を使うことがなくなり、非常にすっきりしたコードになります。
異なる処理を動的に切り替えることができる
クラス単位に処理がまとめて実装されているので、クライアントは使いたいConcreteStrategyクラスのインスタンスをContextオブジェクトに渡すだけで、処理を動的に切り替えることができます。
実際の実装
まずはAuthHelperクラスです。このクラスはStrategyクラスに相当します。
Strategyパターンではこのクラスを抽象クラスとして定義されます。
しかし、CakePHPのHelperクラスは親クラスのAppHelperクラスからの継承が必須であるため、ここではインターフェースとして定義しています。
これによって擬似的に抽象クラスの多重継承が実現します。
<?php interface AuthStrategyHelper { /* * ヘルパーで使いたいメソッドを抽象メソッドとして定義しておきます */ public function putUserName(); public function putUserCreated(); public function putSubTitle(); }
次はAuthStrategyHelperのサブクラスです。
今回はLoginHelperとLogoutHelperの2つがあります。
引用元の解説の通り、切り替えたい選択肢ごとにクラスを作成しています。
ここではログイン時とログアウト時がそれに当たります。
LoginHelper
<?php // コントローラで読み込んでいませんのでここでインポートします App::import('Helper', 'Authstrategy'); class LoginHelper extends AppHelper implements AuthStrategyHelper { //Authヘルパーから渡されたログインユーザのデータを保持するプロパティ private $user; /* * AuthStrategyHelperクラスで定義された抽象メソッド群を実装します */ public function putUserName() { return $this->user["Member"]["user_name"]; } public function putUserCreated() { return $this->user["Member"]["id"]; } public function putSubTitle() { return $this->putUserName() . "さん"; } /* * Authヘルパーからユーザのデータ渡してもらうためのSetメソッド */ public function setConponents($user) { $this->user = $user; } }
LogoutHelper
<?php // コントローラで読み込んでいませんのでここでインポートします App::import('Helper', 'Authstrategy'); class LogoutHelper extends AppHelper implements AuthStrategyHelper { /* * AuthStrategyHelperクラスで定義された抽象メソッド群を実装します */ public function putUserName() { return "ゲストさん"; } public function putUserCreated() { return "ログインすると表示されます"; } public function putSubTitle() { return "<a href='http://sigisi.sakura.ne.jp/cakephp/members/twitter'>Twitterからログイン</a>"; } }
さらに、Contextクラスに相当するクラス、AuthHelperクラスです。
<?php class AuthHelper extends AppHelper { //ヘルパーを読み込みます。 var $helpers = array("Login", "Logout"); //コンポーネントのインスタンスを保持するプロパティ private $conponents; /* * Viewの処理の前に実行されます。 * ViewインスタンスへAuthStrategyHelperクラスの子クラスのインスタンスを渡します。 */ public function beforeRender() { $view = ClassRegistry::getObject('view'); $this->conponents = $view->getConponents(); $view->Auth = $this->getInstance(); } /* * Sessionコンポーネントへの参照を使って認証を判定しています。 * 判定によって参照を返すヘルパーを変えています。 */ private function getInstance() { $user = $this->conponents["Session"]->read("Auth"); if(isset($user['Member']['user_id'])){ $this->Login->setConponents($this->conponents["Session"]->read("Auth")); return $this->Login; } else{ return $this->Logout; } } }
最後に、クライアント側のコードを見てください。
適当なViewの.ctpファイルです。
分り易くindex.ctpを使っています。
index.ctp
<div><?php echo $this->Auth->putUserName(); ?></div> <div><?php echo $this->Auth->putUserCreated(); ?></div> //結果 <div>Mr.HogeHoge</div> <div>2011-04-03 21:02:33</div>
引用
Strategyパターンのオブジェクト指向的要素
Strategyパターンは「継承」と「ポリモーフィズム」を活用しているパターンです。
StrategyクラスとConcreteStrategyクラスは、継承の関係にあります。親クラスであるStrategyクラスで処理内容が変わる部分を抽象メソッドとして定義します。一方、サブクラスであるConcreteStrategyクラスでは、抽象メソッドを実装し、具体的な処理を記述します。こうすることで、同じAPIを持ち、かつ具体的な処理が異なるクラス群を用意できます。
また、Contextクラスは、Strategy型のインスタンスを内部に保持します。このインスタンスは、具体的にはStrategyクラスを継承したサブクラスのインスタンスです。Contextクラスは、クライアントからの処理要求を受け取ると、保持したインスタンスに具体的な処理を委譲します。この時、処理を委譲する部分を、処理側の親クラスであるStrategyクラスのAPIだけを使ってプログラミングを行っておくことがポイントです。こうすることで、Strategy型のインスタンスがどの様な処理を行うものであれ、正しく動作することになります。
この結果、ConcreteStrategyクラスを簡単に差し替えたり、追加したりできるのです。Strategyパターンは、委譲を使って処理内容全体を切り替えるパターンと言えます。
なお、このような処理を切り替えるパターンとしては、Strategyパターン以外にTemplate Methodパターンがあります。Template Methodパターンでは、継承を使って処理内容の一部を切り替えています。
いかがでしたでしょうか
CakePHPのヘルパーというフレームワーク上の制約がある中で、出来る限りオブジェクト指向な設計にしてみました。
後半からは、ほとんど意地になって「Strategyパターン」を墨守した書き方をしています。
ソースからそんな気持ちが出てるかもしれません。
とはいえ、やはり汚いコードはプログラマの恥です。
悪い設計はそれ自体がバグです。
ソフトフェアは保守性そのものといえます。
プログラマは時間や技術力を言い訳にせず良い設計、美しいコードにするよう精進すべきであると考えています。
※この実装はCakePHP1.3でヘルパーからコンポーネントを使えるようにする方法に依存しています。
CakePHP1.3でヘルパーからコンポーネントを使えるようにする方法
まえがき
ヘルパーからコンポーネントを使えるようにする方法です。
Authコンポーネントのデータをヘルパーが自動で読み込んで表示したり出来たら便利そうです。
ただ、デフォルトでコンポーネントの参照をヘルパーに渡していないところを見ると、CakePHPの設計思想上問題があるように見えます。
まあやってしまった以上もとに戻すのも面倒ですから、この点に関しては見て見ぬ振りをしておきましょう
実現方法ヘルパーからコンポーネントを使えるようにするには、
1,Viewクラスを継承したAppViewクラスのインスタンスにコンポーネントの参照をセットした後、
2,コントローラにAppViewを使うようにセット、
3,ヘルパーからViewインスタンスへの参照を得る、
という流れで実現します。
これはCakePHPは1.3になってから、この方法が使えなくなったためです。
まずは、Viewにコンポーネントへの参照をセットします。
Viewクラスを継承したAppViewクラスを作成します。
<?php class AppView extends View { //コンポーネントのインスタンスを保持する private $conponents; function __construct(&$controller, $register = true) { //コントローラのインスタンスから参照を受け取る $this->conponents = $controller->Component; parent::__construct($controller); } //getメソッド public function &getConponents() { return $this->conponents->_loaded; } } ?>
次に、コントローラにAppViewを使うようにセットします。
ここでは一括でセットするためにAppControllerにセットしています。
<?php class AppController extends Controller { //ここに指定します var $view = "App"; function index() { return; } } ?>
最後にヘルパー内でViewインスタンスをセットします
コールバックメソッドの挙動の詳しい説明は「ヘルパーのコールバックメソッドを調査しました」へ
<?php class Use_Conponent_From_Helper extends AppHelper { //コンポーネントのインスタンスを保持する private $conponents; //.ctpファイルの実行前にViewインスタンスを取得してコンポーネントへの参照をセットしておく function beforeRender() { //Viewのインスタンスを取得 $view = ClassRegistry::getObject('view'); //Viewが保持するコンポーネントの参照をプライベートプロパティに保持します $this->conponents = $view->getConponents(); } }
以上でヘルパーからコンポーネントを使うようになりました
ヘルパーのコールバックメソッドを調べてみました [CakePHP1.3][Helper][callback method]
CakePHPの公式ドキュメントにヘルパーのコールバックメソッドの説明がありませんでした。
仕方ないからさくっと調べてみました。
まずCakePHPコア内のヘルパー定義ファイルを見つけます
/cakephp/cake/libs/view/helper.php
<?php class Helper extends Overloadable { /** * Before render callback. beforeRender is called before the view file is rendered. * * Overridden in subclasses. * * @return void * @access public */ function beforeRender() { } /** * After render callback. afterRender is called after the view file is rendered * but before the layout has been rendered. * * Overridden in subclasses. * * @return void * @access public */ function afterRender() { } /** * Before layout callback. beforeLayout is called before the layout is rendered. * * Overridden in subclasses. * * @return void * @access public */ function beforeLayout() { } /** * After layout callback. afterLayout is called after the layout has rendered. * * Overridden in subclasses. * * @return void * @access public */ function afterLayout() { } } ?>
これを適当にヘルパーを作ってオーバーライドします
<?php class I_want_to_you_official_manual_of_cakephp_Helper extends Helper { function beforeRender() { echo __FUNCTION__; } function afterRender() { echo __FUNCTION__; } function beforeLayout() { echo __FUNCTION__; } function afterLayout() { echo __FUNCTION__; } } ?>
実行結果はこんな感じです
beforeRender()がdefault.ctpの外に出力
↓
beforeLayout()がdefault.ctpの外に出力
↓
afterLayout()がdefault.ctpの外に出力
↓
afterRender()がviewファイルの中に出力