がるの健忘録 このページをアンテナに追加 RSSフィード

2017-04-03

[]さじ加減のむずかしさ

まぁ毎度同じみ的な内容なのですが、色々な角度から。


考察の元ネタは、こちら。

https://twitter.com/nfujita55a/status/847131544166719488

コードが面白いので、頑張って書いてみました。

Runtime r = Runtime.getRuntime();

if ( hasA == true && hasB == true && hasC == true ) {
    r.exec( "foo -a AAA -b BBB -c CCC" );
} else if ( hasA == true && hasB == true && hasC != true ) {
    r.exec( "foo -a AAA -b BBB" );
} else if ( hasA == true && hasB != true && hasC == true ) {
    r.exec( "foo -a AAA -c CCC" );
} else if ( hasA != true && hasB == true && hasC == true ) {
    r.exec( "foo -b BBB -c CCC" );
} else if ( hasA == true && hasB != true && hasC != true ) {
    r.exec( "foo -a AAA" );
} else if ( hasA != true && hasB == true && hasC != true ) {
    r.exec( "foo -b BBB" );
} else if ( hasA != true && hasB != true && hasC == true ) {
    r.exec( "foo -c CCC" );
} else if ( hasA != true && hasB != true && hasC != true ) {
    r.exec( "foo" );
} else {
    System,out.println("あり得ない");
}

付帯情報として

この方、10年選手で、余裕のある状況で書かれたものです

との、事。


さて……まぁ本音ぶっちゃけますと「現場で見たらこぶしで殴る」ような感覚をたっぷりと感じるところではあるのですが。

実際に色々と考えてみると、なかなかに「根の深い」お話、になります。


ツイートにも書いてあったのですが

「これ直してよ」と私がいったら、このソースの開発者曰く「8ケースとも単体テスト通ってOKだったから品質は問題ありません」と言われ、気が遠くなった。

このあたり。

「ちゃんと動くから問題ない」というのは、一つの観点として、ないわけではないです。

もちろん、コードとしては「循環的複雑度の観点」とか色々あるのですが。

「馬力で常に全部テストしているから何が問題なのか?」と問われた時に、どの辺で突っ込むか僅かに悩むわけですまぁ「無駄な時間つかってムダ金使うな」で一撃ではあるのですが。


この辺はおそらく、ごくわずかに思考していれば、もうちょっと簡単に書けると考えられます。

PHPで書いてしましますが、基本的には

$command = 'foo';
if (true === $hasA) {
    $command .= '  -a AAA';
}
if (true === $hasB) {
    $command .= '  -b BBB';
}
if (true === $hasC) {
    $command .= '  -c CCC';
}

ってな記法でいけるはずですし。これだと「パラメタがさらに1つ2つ追加」されても、ケース数は爆発しませんし。


技術的知見の多く*1は、こんな風に「知らないと非常に無駄であったり非効率的であったりするものを、効率的にする」ものでございます。

アルゴリズム各種とか、その最たるものですね。

で、その手の知見のもう一つ大きな特徴が「知らないと、そもそも"困っている事が理解できない"から、"必要になったら学ぶ"の、まず"必要である"が認識できない」ところ、でございます。


ヒソカ…だと思ったんですが、確認したらウイングさんの台詞ですね。

極寒の地で

全裸で凍えながら

なぜ つらいのか

わかっていない

ようなもの

「知る」ってのは、とても大事な事でございます。


ただ……じゃぁ「どこまで知ってればいいのさ?」ってのが、当然、出てきます。

その辺の加減を間違えますと、ガッツが言うところの

それともお前

何十年も修行して、達人にでもなるのを待ってから戦場に出るつもりか?

気の長げェ話だな

ってな事になってしまいます。


この辺のバランスって、本当に難しいなぁ、と、多分これは、生涯悩み続けるんだろうなぁ、とか思ってたりします。

おいちゃんのスタンスとしては「百尺竿頭進一歩にもあるように生涯日々精進しつつも、"精進中である"事を言い訳にせずに行い、新しい教えがあったら躊躇なく受け入れる」ってなあたりかなぁ、と。

薄氷を踏むような、危ういバランスですが(笑


この辺のむずかしさとか大切さを、なんとか、色々と伝えられるとよいなぁ、とか思ったりはするのですが。

伝える事もまた、難しいなぁ、とは、常々感じているものではございます(苦笑

*1:……だと思いたい

2017-04-02

[][]思案中……

身内に流そうかとも思ったのですが、いっそ「広く意見を募集しようかなぁ」などと思い、こちらに。

# いやまぁ身内にも流すんだがwww


幾分「ある程度、おいちゃんの手の内性格経歴その他を把握している前提」な文面があるので。

不明点は、コメント等で質問などいただけるとありがたいです ノ


んで、さっそく本題。


前提は「(専門学校生含む)初心者:PHP初心者にしてプログラム初心者」で、お題は「フレームワークをどうやってどのタイミングで教えるか?」。

おいちゃんが懸念している最たるものは「丸覚え丸のみで応用の利かない子が育ってしまう」事。


単純に「使い捨てを育てる」んなら、理屈とかすっ飛ばして「使い方」を教えればよいのですが。

それだと確実に「使い捨てになる」ので、おいちゃんの望むところでも目指すところでも期待するところでも、なんにもナッシング。

ちゃんと、(程度問題はあるにしても)応用の効く知識を学習してもらいたいなぁ、と思う親心でございます。


今までおいちゃんが想定していたのは

・とりあえずベタ書きのPHPを教える

・「関数ってあると楽だし便利なんだよ」を教える

・「クラスってあると楽だし便利なんだよ」を教える

フレームワークを、段階的に教える

って感じ。


一瞬、脱線すると。

フレームワークを、段階的に教える」ってのが、すでに色々と難儀ぃ、な感じで。

ある程度、キャリアとか経験とかつんでると、知らんフレームワークでも、基本的な部分については「なんとなく」理解出来たりするのですが。

それってつまり「フルスクラッチ経験してて」「色々なフレームワーク経験してて」、共通な部分を、なんとなくにしても把握できているからこそ、の芸当なので。

それを「ガチ初心者さん」に求めるのは、些か、しんどかろうなぁ、と。

んで、いわゆる「Hello, world.」から初めてようやく「ベタっと書けるようになったようななってないような」から「フルスタックなフレームワーク」だと、何段跳びなんだろう? くらいにジャンプしてしまうので。

おいちゃんとしては「ベタっと書いたPHP CGIから、フレームワークっぽい所までの道のりを学ぶ用の、学習用途フレームワーク」とか作ってみようかなぁ、とか、画策をしてたりはいたします。


閑話休題


さて…上述なのですが。正直「結構な時間がかかる」ので。

ぶっちゃけますと「専門学校で週に90分*2コマ」が1年程度では、どにもならんだろうなぁ、と思われるんですね。

で、思案していたのですが。


とある先生から、大まか、以下のような趣旨と思われる提案をいただきまして。

「まず使わせて、そのあとで"背景技術の説明"をしていく」。

ちと、現在、思案しているところでございます。


おいちゃんの旧来考えているやり方だと「フレームワーク説明するまえ、或いは途中で終わる可能性」が十二分に懸念されて。

一方で、ご提案いただいたやり方だと「背景説明が途中で終わってしまって、結局"使い方だけ丸のみで覚えた"になる可能性」が、現状の予想だと、がっつりと懸念しているところでございます。


まぁこゆのに正解とかないので。

ちと、広くご意見など賜れればなぁ、と思いました。


おいちゃんを個人的に知っている方は、Slackなりmailなりで。

その辺の連絡先が不明な方は、こちらにコメントなど残しておいていただけると嬉しいなぁ、と思います。

2017-03-22

[][]CAS実装……の前提のお話

ふと、色々と紆余曲折があった末として「MagicWeaponのdata_clumpで、cas形式の楽観的ロック、実装してみようかしらん?」というような発想がありまして。

その前提として「cas tokenど〜やって実装しよう?」から、比較的現実的な可能性があるあたりを妄想して、その辺の実装の欠片を思いついたので、おおむね、メモ用途でw


端的には「bigint(8byte) unsigned相当の整数」をランダムで持たせたら、大体、トークン足りえるんじゃないかなぁ? と、妄想をしまして。

8byteなら、index切っても、メモリ空間の圧迫が「そんなもんだろう」程度、だろうし。

渡し方については……MySQLであれば……二種類あるなぁ。


mysql> SELECT CAST(0xffffffffffffffff AS UNSIGNED);

+--------------------------------------+

| CAST(0xffffffffffffffff AS UNSIGNED) |

+--------------------------------------+

| 18446744073709551615 |

+--------------------------------------+

mysql> SELECT CONV('ffffffffffffffff', 16, 10);

+----------------------------------+

| CONV('ffffffffffffffff', 16, 10) |

+----------------------------------+

| 18446744073709551615 |

+----------------------------------+


………ん?

mysql> SELECT CAST('0xffffffffffffffff' AS UNSIGNED);

+----------------------------------------+

| CAST('0xffffffffffffffff' AS UNSIGNED) |

+----------------------------------------+

| 0 |

+----------------------------------------+

あぁ文字列にすると駄目なのか。

って事は、CONV関数だな。こっちなら「文字列」として渡せる。

まぁ、MySQLに方法があるんだ、ほかのRDBでもあるだろうw(ざつ)


んで。

だとするとあとは「8byte整数相当の値をランダムにとってくる」と、なんとなく行けるっぽくて。

後は「重複しねぇよなぁ」を、雑にテストしてみるとよいんじゃなかろうか、って思う。


Linux的に手っ取り早いのは /dev/urandom デバイスでしょ、ってんで、確認かねて、以下のコードを動かしてみた。

<?php

$data = [];
for($i = 0; $i < 100000; ++$i) {
    $h = bin2hex(file_get_contents('/dev/urandom', false, NULL, 0, 8));
    if (false === isset($data[$h])) {
        $data[$h] = 1;
    } else {
        echo "ng... \n";
    }
}

重複が発生したら教えてくれる、的な子。

とりあえず10万回。

……ま〜ま〜時間かかるのな。ほんのわずかにびっくりしたおw


何度か動かしてみましたが、(おおよそ予想通り)一度もぶつからなかったので。

多分、いけるんじゃないかなぁ、的な。


どこかで、気が向いたり体力が向いたりしたら、CASを実際に実装してみませう。

って事で、とりあえず「実装するために一番重要な部分」を、軽くテストしてみました。


突っ込みなどあったら歓迎いたしますので、是非。

2017-03-18

[]グローバルスコープ変数? filter_input関数? (それともNULL え・ん・ざ・ん・し?(はぁと))

ふいっと、filter_input関数を思い出しまして(って程度には使ってないw)。

機能的にはさほど大きな違いはないと思われるので、まずは性能をチェック。

基本は以下のコードで、コメントアウトを適宜はずしつつ測定。


<?php

//$_GET['hoge'] = 'a';

$t = microtime(true);
for($i = 0; $i < 100000; ++$i) {
    //$s = (string)@$_GET['hoge'];
    //$s = (string)filter_input(INPUT_GET, 'hoge');
    //$s = (string)($_GET['hoge'] ?? '');
    if (false === isset($_GET['hoge'])) {
        $s = '';
    } else {
        $s = (string)$_GET['hoge'];
    }
}
$t2 = microtime(true);
var_dump($t2 - $t);

結果一覧。


ある:(string)@$_GET['hoge'];

float(0.011772871017456)

float(0.017148971557617)

float(0.015825986862183)

float(0.014954805374146)

float(0.016114950180054)


ある:(string)filter_input(INPUT_GET, 'hoge');

float(0.032809972763062)

float(0.033557891845703)

float(0.02568507194519)

float(0.027527093887329)

float(0.02209997177124)


ある:null演算子

float(0.013915061950684)

float(0.016128063201904)

float(0.013824939727783)

float(0.014352083206177)

float(0.016026973724365)


ある:isset

float(0.023705959320068)

float(0.031208038330078)

float(0.013985872268677)

float(0.028561115264893)

float(0.022880077362061)


ない:(string)@$_GET['hoge'];

float(0.065753221511841)

float(0.078284025192261)

float(0.083561897277832)

float(0.048727035522461)

float(0.057971954345703)


ない:(string)filter_input(INPUT_GET, 'hoge');

float(0.014656066894531)

float(0.01726508140564)

float(0.016877889633179)

float(0.023527860641479)

float(0.022456169128418)


ない:isset

float(0.011105060577393)

float(0.0091698169708252)

float(0.0077798366546631)

float(0.0075478553771973)

float(0.0067591667175293)


ない:null演算子

float(0.010882139205933)

float(0.0078949928283691)

float(0.0078151226043701)

float(0.0083792209625244)

float(0.0070400238037109)


ふむぅ。

配列が「存在する」場合と「存在しない」場合で。

「ある」場合は、超雑な「(string)@$_GET['hoge'];」か、超丁寧な「isset」が双璧で早い感じ。

「ない」場合は、issetがさすがに「言語構造」なので最速、filter_inputがその後を追う感じ。

NULL演算子はいずれにも「隙がない」w。「PHP7以降」ってのが最大の欠点???


速度的にも「10万回ぶんまわして」一番遅いので80ミリ秒、なので。

どれでもそんなにかわらん気もするなぁ色々。


まぁ多分おいちゃん的には「基本的には"ある"前提で考える」ので、その辺の速度とか今までとかの兼ね合いを考えると

・雑に書きたいんなら相変わらず「(string)@$_GET['hoge'];」

・ただし、PHP7環境になったら光の速さでNULL演算子

って感じになるのが多そうかな?

2017-03-15

[]str_replace()の第一&第二引数は、文字列配列

結論から書くと、マニュアルに書いてある通り

search と replace が配列の場合、str_replace() は各配列から値をひとつ取り出し、 subject 上で検索と置換を行うために使用します。

なのですが……ちょっと驚いたので。


端的には、以下のコード。

<?php

$s = 'abc';

$r = str_replace(['a', 'b', 'c'], ['ca', 'cb', 'cc'], $s, $count);
var_dump($r, $count);

$r = str_replace(['c', 'a', 'b'], ['cc', 'ca', 'cb'], $s, $count);
var_dump($r, $count);

結果

string(8) "ccaccbcc"

int(5)

string(6) "cacbcc"

int(3)

配列の順番を変えると、結果が変わるんだねぇ………。

変換後の値がかぶらなければ順番は気にしなくてよいと思うのですが、変換後の値が「別の変換前の値」とかぶる場合、順番を気にする必要があるっぽです。

ただ「順番にやる」事が保証されるのであれば(マニュアルに書いてあるし)、それはそれで使い道が色々と別角度からは増えそうだなぁ、と。


で、少し疑問「だとすると、変換する配列の1組ごとに、全件一致検索毎回流しまくってるのかなぁ?」と。

ソースコード読むの面倒なんで、一端外側から叩いて、軽く確認。

<?php

$s = 'abc';

$t = microtime(true);
for($i = 0; $i < 100000; ++$i) {
    $r = str_replace(['c', 'a', 'b'], ['cc', 'ca', 'cb'], $s);
}
$t2 = microtime(true);
var_dump($t2 - $t);

$t = microtime(true);
for($i = 0; $i < 100000; ++$i) {
    $r = str_replace('c', 'cc', $s);
    $r = str_replace('a', 'ca', $r);
    $r = str_replace('b', 'cb', $r);
}
$t2 = microtime(true);
var_dump($t2 - $t);

結果

[gallu@hogehoge48 ~]$ php t.php

float(0.058500051498413)

float(0.056888103485107)

[gallu@hogehoge48 ~]$ php t.php

float(0.064057111740112)

float(0.06181788444519)

[gallu@hogehoge48 ~]$ php t.php

float(0.066102027893066)

float(0.065979957580566)

[gallu@hogehoge48 ~]$ php t.php

float(0.086111068725586)

float(0.082880973815918)

[gallu@hogehoge48 ~]$ php t.php

float(0.070822954177856)

float(0.057141065597534)

………ぶも??

予想としては「関数callのオーバヘッド分、少しだけ分割したほうが遅いけど誤差範囲」だと思ったのに……誤差範囲程度とはいえ「配列より文字列で複数回callしたほうが早い」???

あらら…てっきり「配列で渡したほうが効率よく文字列操作してくれる」と思ってたんですけどねぇ。


これは、今後少し、使い方を改めたほうがよいかしらん?