phpのpreg_replace_callbackをc#で書くと・・・ #php #csharp #memo

PHPにpreg_replace_callbackという関数があります。

正規表現検索を行い、コールバック関数を使用して置換を行う
http://php.net/manual/ja/function.preg-replace-callback.php

というものです。

これをC#でやるときは、

System.Text.RegularExpressions.Regex.Replace メソッド (String, String, MatchEvaluator)
http://msdn.microsoft.com/ja-jp/library/ht1sxswy.aspx

を利用します。

第1引数が入力文字列で、
第2引数が検索のための正規表現文字列です。
第3引数のMatchEvaluatorデリゲートの中でマッチした部分文字列に対して置換処理を行います。

preg_replace_callbackのマニュアルページの例2にあるサンプルコードをcsで表すとすると以下のようになります。
参考までにPHP側のコードを冒頭にコメントで乗っけています

Regex.Replaceメソッドの使い方のほんの一例です(そもそもreplaceメソッドのオーバーロードの一つでしかない。
ので、詳細は上記MSDNで確認してください。

//  <?php
//// このテキストは 2002 に使われていたものなのですが、
//// これを 2003 年対応の日付に変更したいのです
//$text = "エイプリルフールの日付は 04/01/2002 ですn";  
//$text.= "この前のクリスマスの日付は 12/24/2001 でしたn";  
//// コールバック関数
//function next_year($matches)
//{
//  // 通常は、$matches&#91;0&#93; がマッチした全体を表します。
//  // $matches&#91;1&#93; は、マッチした中で、パターン内の最初の '(...)'
//  // にあてはまる部分を表します。それ以降も同様です。
//  return $matches&#91;1&#93;.($matches&#91;2&#93;+1);
//}
//echo preg_replace_callback(
//            "|(d{2}/d{2}/)(d{4})|",
//            "next_year",
//            $text);

//?>

            var text = @"エイプリルフールの日付は 04/01/2002 です
この前のクリスマスの日付は 12/24/2001 でした";

            
            var replacedText = Regex.Replace(
                 text,
                @"(d{2}/d{2}/)(d{4})",
                //x.Valueおよびx.Groups[0]で部分文字列全体
                //x.Groups[1以降]は上記席表現の()で括られた部分にあたる部分文字列
                 x =>
                 {
                     return x.Groups[1].Value + (Convert.ToInt32(x.Groups[2].Value) + 1);
                 });

            Console.WriteLine(replacedText);

余談ですが、VS2013を使って、debug中にunhandledな例外が発生した時ローカル変数用のウォッチウィンドウに$exceptionという名前で例外情報が表示されるのが地味に良いと思いました。

FacebookTwitterHatenaTumblr共有

#PHPで#LINQっぽい事ができるWrapperを作ってみた

LAMPな会社に来て1年半がたち、1ヶ月前にPHPデビューをしました。
で、やっぱりいろいろツライところがあったのと、風呂あがりに思い立ったのと、いろいろと触発されたところあって、PHPでLINQっぽく配列操作ができるWrapperを作ってみました。
実際に、最近恩恵に預かっているので作ってよかったなと思っています。

基本的に、where,select,reduce,groupBy,orderBy,join,zip,sum,averageが出来るようになっています。
詳細は、githubの以下のサイトを見てもらえばいいと思いますが、比較的速度を気にするようにしているので、中で処理するときのループ回数が少なくなるように設計してあります。

toVar()および、reduce()で配列が戻ってきます。

groupBy,orderBy,join,zip,sum,averageなどは、実際には一回実行して、新しくArrayWrapperでWrapしていますが、それ以外はできるだけ1回のループで処理が行われるようになっています。

ArrayWrapper for PHP

例えば、whereしてselectしてその結果を更に(意味ないけど)whereしたものを返すような書き方は以下のように出来ます。
(テストコードまんま貼り付けですが、$expectedが実行した結果だと思ってください。

    $target = new ArrayWrapper(
            array(
                array("key" => 1,"value" => 10),
                array("key" => 2,"value" => 11),
                array("key" => 3,"value" => 12),
                array("key" => 4,"value" => 13),
                array("key" => 5,"value" => 14)
            )
        );
 
    $actual = $target
            ->where(function($x){return $x["key"] > 2;})
            ->select(function($x){return array("K" => $x["key"],"V" => $x["value"] * 2);})
            ->where(function($x){return $x["K"] > 3;})
            ->toVar();
 
    $expected = array(
            array("K" => 4,"V" => 26),
            array("K" => 5,"V" => 28)
            );
        

例えば、groupByなんかは以下のように書けるようになっています。

$target = new ArrayWrapper(
        array(
            array("id" => 2,"value" => 10),
            array("id" => 2,"value" => 11),
            array("id" => 3,"value" => 12),
            array("id" => 3,"value" => 13),
            array("id" => 5,"value" => 14)  
        )
    );
 
//toVarで配列に戻す。
$actual = $target
        ->groupBy(array("id"))
        ->toVar();
 
//で結果、こうなる
$expected = array(
        array("keys" => array("id" => 2),"values" => array(
            array("id" => 2,"value" => 10),
            array("id" => 2,"value" => 11)
            )),
        array("keys" => array("id" => 3),"values" => array(
            array("id" => 3,"value" => 12),
            array("id" => 3,"value" => 13),
            )),
        array("keys" => array("id" => 5),"values" => array(
            array("id" => 5,"value" => 14)  
            ))
        );

joinもこんなふうに書きます。

    $leftArray = array(
                array("key" => 2,"name" => "Nasal Hair Cutter"),
                array("key" => 3,"name" => "scissors"),
                array("key" => 5,"name" => "knife")
            );
 
    $rightArray = array(
            array("id" => 1,"item_id" => 2,"value" => 10),
            array("id" => 2,"item_id" => 2,"value" => 20),
            array("id" => 3,"item_id" => 2,"value" => 30),
            array("id" => 4,"item_id" => 3,"value" => 40),
            array("id" => 5,"item_id" => 3,"value" => 50),
            array("id" => 6,"item_id" => 5,"value" => 60),
            array("id" => 7,"item_id" => 5,"value" => 70),
        );
 
    $target = new ArrayWrapper($leftArray);
 
    $actual = $target
            ->join($rightArray,
                    array("key"),
                    array("item_id"),
                    function ($leftValue,$rightValue)
                    {
 
                        return
                            array("item_id" => $rightValue["item_id"],
                                 "name" => $leftValue["name"],
                                 "value" => $rightValue["value"]);
                    })
            ->toVar();
 
    $expected = array(
                array("item_id" => 2,"name" => "Nasal Hair Cutter","value" => 10),
                array("item_id" => 2,"name" => "Nasal Hair Cutter","value" => 20),
                array("item_id" => 2,"name" => "Nasal Hair Cutter","value" => 30),
                array("item_id" => 3,"name" => "scissors","value" => 40),
                array("item_id" => 3,"name" => "scissors","value" => 50),
                array("item_id" => 5,"name" => "knife","value" => 60),
                array("item_id" => 5,"name" => "knife","value" => 70),
            );       

実際に、業務で使っていますが、groupBy、sum,orderByあたりはかなり重宝しています。

疲れているとこんな実装をしてしまってはまる

今日は疲れていたようで以下のようなクソみたいな間違いに気づかないで30分くらい費やしてしまいました。

public class clsssName
{
    private hoge _member = new hoge();
    public className()
    {
           hoge  _member = new hoge();
           _member.property = value;
   }
}

何にはまったか分かりますか?
メンバとして宣言されている名前と同じ名前でローカル変数を宣言してしまったという、ただそれだけの事なのですが、疲れていると30分ほど気づかないで、あーでもない、こーでもないとやってしまうのです。

というわけで、ちゃんと疲れを翌日に残さないようにしましょうという教訓でした。

デバグをするにあたって先入観を持ってはいけない

デバグをするにあたって先入観を持ってはいけない。

先入観を持った状態でデバグをすると、多分、運が良くないと簡単な不具合にも無駄に時間を書けることになる。まだ、なにが原因か分かっていないのに、最初から決めつけてしまった場合、決めつけた範囲に不具合があるかどうかは博打みたいなものである。まだ、どこを調査するかを決めつけてはいけない。

要素の数と内容が決まっている配列を作成する時、より変更に強いコードとは?

下記のような、二つのコードがあるとする。

ケース1:配列を宣言してから代入する。

[cs]
var sample = new string[3];
sample[0] = “abc”;
sample[1] = “def”;
sample[2] = “ghi”;
[/cs]

ケース2:宣言時に要素も設定する。

[cs]
var sample = new string[]
{
“abc”
,”def”
,”ghi”
};[/cs]

要素の数と内容が決まっている配列を作成する時、より変更に強いコードはケース2である。

なぜかというと、ケース1の場合は、要素の数に変更があった時、代入だけ増やして、要素数の変更を忘れる可能性があるから。

空のIF文を書きますか?私は書きません。

仕様書を書くときでも、何も処理しない場合は、あえて、「何もしない」など

と記述してますので、それがコーディングに反映している感じです。

仕様書と合わせる意味でも、わざわざ、Notを付けて、否定に書き換えたり

しないですね。

例??

If IsExists() Then

  '何もしない

Else

  fileName = String.Empty

End If

空のIF文を書きますか?

私は、空のIF文は書かないですね。

比較的許容できない方だと思います。

でも、そのプロジェクトのコーディングルールがそうなっているのであれば、従います。(仕事ですから。)

もし、仕様に残したくて、その上でそのIF文の処理が機能として独立しているのならば、メソッドアウトして、

Public Sub ほにゃ()
If IsExists() Then
Exit Sub
End If
 ''処理記述

といった形にする事があります。

コーディング規約は成長可能であるべき

プロジェクトの過程でコーディング規約を決める。その後はシステムが運用に辿り着くまで変更しない。

このような環境が多数派だと思う。

しかし現実はミスマッチとか想定外な事が少なからずあり、コーディング規約を逸脱した例外的なものが許容される事がある。その割にコーディング規約に明文化されることはなく、あくまで例外という名の運用でカバーという奴だ。もしくは、無理矢理コーディング規約に則るために、最適ではないコーディングになっていることもある。

確かに一度決めたことを変更するのは勇気がいる。もしこはそもそも、変更しようという概念自体が無いかもしれない。

だが、ルールは適用するためにあるのではなく、円滑なプロジェクト運営や保守を行うためにある。だから、コーディング規約に、こんな文章の追加をお勧めする。

「このコーディング規約は変更可能である。このコーディング規約に定義された内容を逸脱する必要がある場合、このプロジェクトにミスマッチな場合は責任者に相談しコーディング規約が変更されるのを待ってからコーディングを行うこと。また、レビューによって指摘された内容がコーディング規約に定義されていない時は速やかに規約に追記する。」

セキュリティを意識したコーディングをさせないという事

セキュリティを高めるために多分一番必要な事は、システム開発者にセキュリティを意識したコーディングをさせないという事だ。

セキュリティを意識したコーディングをするのはシステム開発者の役割ではなく、システム開発者が使うであろうフレームワークやプラットフォームを作成している者の役割なのだ。

もちろん、システム開発者がセキュリティを意識しなくてもよいという事ではないし、その知識は持っておくべきである。

ただ、システムを開発している時にそれを意識してコーディングをしなくてはいけないような環境ではいけないという事だ。

例えば、HTMLをレンダリングする時に文字列をエスケープして出力する事があると思うが、こんなことシステム開発者にやらせてはいけない。

実直にあらゆる箇所にエスケープするための処理を埋め込むなんて、むしろどっかに穴ができる可能性を作っているようなものだ。

また、実際の処理とセキュリティを確保するための処理が混在してしまう事で、わかりにくいコードになってしまう可能性がある。

上記の例のようにセキュリティを意識しなくてはいけない場合、システム開発者は常に二つのことを意識してコーディングする必要がある。

一つは仕様を実装するという事。

もう一つはセキュリティを確保するという事。

このような状況だと、システム開発者が管理する項目が多くなりバグを誘発する危険性がある。

まるで、携帯電話でメールをしながら運転するようなものである。

人間は二つのことに100%注力する事はできないので、どちらかの問題が疎かになるかもしれないし、どちらもが中途半端になる可能性がある。

別の例として、

PythonのDjangoはHTMLに出力する値は規定値でエスケープ出力される。

必要な箇所でのみ、エスケープしないという設定を行う事が可能だ。

このような環境が用意されていれば、システム開発者はセキュリティを意識したコーディングをそこら中でする必要が無くなる。

エスケープをしないと決定した場所でのみ、意識すれば良い。

上記の例のようにシステム開発者がセキュリティを意識する箇所を限定的にすることで比較的一つのこと、そう仕様を実装する事だけを意識するだけでよくなる。

一つの事に注力できるので、システム開発者に余裕ができるだろうし、中途半端になる事も無いだろう。

このような、開発者にセキュリティを意識させない開発環境こそがセキュリティに明るい優秀な開発者でチームを作る事よりも大切なのではないだろうか。

ここまで記述してきた「セキュリティを意識したコーディングをさせない。」という事を一言で言えば、セキュリティを意識する必要のある範囲をできるだけ限定的にしろという事である。

つまり、

スコープは常に最小に。

という原則に基いたものである。

匿名メソッドで再帰を実装する時

通常匿名メソッドを実装する時は、宣言と代入が同じになるパターンが多い。

例えば、下記のコードのように。

public void 匿名メソッドで再帰()
{
Func<int, bool> カウントダウン = (x) =>
{
if (x == 0)
{
return true;
}
Console.WriteLine(x);
x--;
return カウントダウン(x);
};
カウントダウン(40);
}

しかし、上記のコードだと、

未割り当てのローカル変数 'カウントダウン' が使用されました。

と言われてコンパイルエラーになる。

では、どうしたら良いかというと、先に匿名メソッドを代入する変数を宣言しておけばよい。

下記のコードならコンパイルエラーにならないで正しく動作する。

public void 匿名メソッドで再帰()
{
Func<int, bool> カウントダウン = null;
カウントダウン = (x) =>
{
if (x == 0)
{
return true;
}
Console.WriteLine(x);
x--;
return カウントダウン(x);
};
カウントダウン(40);
}

10のコーディングルール

以前、コードの10戒と題して、犬の10戒をまねしたコーディングルールを掲載しました。

しかし、若干カジュアルすぎるため仕事に適用するには文体を改修する必要があるかもしれません。

長いコーディング標準は、読むのも守るのもメンテするのも大変です。

なので、短いけど守ってほしい事と理由を犬の10戒の真似をしてまとめてみました。

コードの10戒 – NAL-6295の舌先三寸

そこで今回は、これを少しフォーマルなものにしてみました。

  • 1.識別子(名前)は分かりやすいものにすること。
    参画するメンバーにより理解してもらうために分かりやすい名前にすること。
    メソッド名等が文章になってもかまわない。
    何をしようとしているのかを明示すること。
    全ての識別子はその存在理由が明確である必要がある。
    また、その存在理由以外の目的で利用しないこと。
  • 2.”共通"とか"common"といった名前を利用しないこと。
    共通で利用するメソッドや定数やクラスも、必ず存在理由がある。
    その役割にあった場所に配置すること。
  • 3.事前条件、事後条件を利用すること。
    メソッドの処理に入る前に引数がとりえる範囲を事前条件として指定し、範囲外であれば例外を飛ばして排除すること。
    そうすることで処理中で考えなくてはいけない範囲を狭く出来る。
  • 4.スコープは最小にすること。
    複数の問題が同時に存在していると、ミスリードしてバグを誘発する可能性が高くなる。
    変数のスコープだけではなく、処理、問題等全てのスコープを最小にすること。
  • 5.「将来のため」のコードを残さないこと。
    不必要なコードを「将来のため」といった理由で作りこまないこと。
    必要になってから作ったコードは使われるが、
    「将来のため」に作ったコードは使うかもしれないし、使わないかもしれない。
  • 6.作るときにチューニングをしないこと。
    チューニングは非機能要求にそった計測後、足りていない部分においてのみ行う事。
    コードの調和の取れた構成をできるだけ崩さないようにするため。
  • 7.レビューを行うこと。
    コードは必ずレビューをすること。
    また、レビューにおいて指摘された点は速やかに修正すること。
    複数の視点で見たコードはより分かりやすくなるため。
  • 8.テストをすること。
    コードは思いどおりには動かない。
    自分が思ったとおりにコードを書いているかテストして確認すること。
    そして思い通りに動かないときは、まず自分を疑うこと。
  • 9.バージョン管理は構成管理ツールに任せること。
    コードをコメントにして残さないこと。
    修正履歴をコメントで残さないこと。
    その全てを構成管理ツールに任せること。
  • 10.保守で修正する前に、必要な範囲の構成について理解すること。
    修正する箇所だけではなく、その周辺の構成に理解したうえで、その秩序を乱さないように修正すること。