bit演算を使ったプログラミング、割と「知られてないのでは?」という感じがあったので。
ここでは少し抽象的な部分が混ざってしまうのだけど、まぁなんかの一助になるかなぁ、くらいの感じで、めも。
大まかには
・処理が「A」「B」「A+B」みたいな感じの時の処理の共通化
・いわゆるフラグ的に on/off 出来るステータスがあるようなもの
の時に便利だったりします。
前提
まず、2進数はよいよね?
2進数は、各桁が「bit」って言い方をします。
んで、1だと「立ってる」、0だと「寝てる/立ってない/落ちてる」とかって言い方をする事が、まぁ。
1の「立ってる」は割と共通な気がするなぁ。0はあちこち方言がありそうだけど。
んで。論理演算のANDは「あるbitが、立っているか寝ているか」を判定するのに割と便利につかえます。
例。
変数 $x*1の1桁目(2^0)が立っているか寝ているかを判定する場合は
if (0 !== ($x & 0b0001)) { // 立ってる } else { // 寝てる }
って判定が出来ます。まぁもうちょっと省略して
if ($x & 0b0001) { // 立ってる } else { // 寝てる }
って書く事が多いように思いますが。
同じように、2桁目(2^1)が立っているかどうかは
if ($x & 0b0010) {
3桁目(2^2)なら
if ($x & 0b0100) {
で判定が出来ます。
とりあえずわかりやすく二進数表記で書いてますが、実際には「1、2、4」といった、十進数で書いている事もちょいちょい。
これ自体は、2進数とbit演算を知っていれば「知識としては知っている」内容だと思うのですが、これを上手く使うと、プログラムが楽に出来ます。
いやどちらかというと「いにしえから伝わる技法」だと思うのですが、割と昨今でも普通に使う事が多々あるので。
ちなみに、こーゆーのを「マスクビット」とか「ビットマスク値」とかいう言い方をする事があるので、知っておくと、ググる時に楽……かも。
処理が「A」「B」「A+B」みたいな感じの時の処理の共通化
まぁ今回の元ネタがこっちなので、先にこっちで(笑
入ってくるパラメタによって「Aのみ」「Bのみ」「AとBの処理」をやる場合。
一番ざっくりと書くと
if (パラメタ==A) { パラメタAの処理; パラメタAの処理; パラメタAの処理; } else if (パラメタ==B) { パラメタBの処理; パラメタBの処理; } else if (パラメタ==A+B) { パラメタAの処理; パラメタAの処理; パラメタAの処理; パラメタBの処理; パラメタBの処理; }
となるか、と思います。
で、少し共通化すると
function func_A() { パラメタAの処理; パラメタAの処理; パラメタAの処理; } function func_B() { パラメタBの処理; パラメタBの処理; } // if (パラメタ==A) { func_A(); } else if (パラメタ==B) { func_B(); } else if (パラメタ==A+B) { func_A(); func_B(); }
ってなるか、と思うのですが。
これを、bit演算を使うと
$type = 0; if (パラメタ==A) { $type = 1; // 0b0001 } else if (パラメタ==B) { $type = 2; // 0b0010 } else if (パラメタ==A+B) { $type = 3; // 0b0011 } // if ($type & 1) { // 0b0001 パラメタAの処理; パラメタAの処理; パラメタAの処理; } if ($type & 2) { // 0b0010 パラメタBの処理; パラメタBの処理; }
って書く事ができます。
いやまぁ関数(メソッド)に切り出してもよいんですが。
「微妙に切り出しにくい(面倒な)処理」とかで、かつ「ここでしか使わない」ようなロジックの場合、こんな風に書くと
・重複した記述がいらない
・(慣れてれば)見やすい
ので、すっきり書けますよ、的な感じになります。
いわゆるフラグ的に on/off 出来るステータスがあるようなもの
この辺は、PHPの関数でも割とおなじみなように思うのですが。
ふと思い出しやすい所で、 file_put_contents( https://www.php.net/manual/ja/function.file-put-contents.php ) を例に。
第三引数のflagsが、まさにこのやり方をやっています。
<?php var_dump(FILE_USE_INCLUDE_PATH); var_dump(FILE_APPEND); var_dump(LOCK_EX);
int(1)
int(8)
int(2)
4がいませんが、まぁ2進数にすると
0b0001
0b1000
0b0010
で、ちょうどそれぞれ「1つだけbitが立っている」値になっています。
なので、例えば「FILE_APPENDかつLOCK_EXにしたい」場合は、論理和(or)を使って「 FILE_APPEND | LOCK_EX 」とやると、$flgには10(0b1010)が渡るので。
あとは、プログラムで
if ($flg & 0b1000) { // FILE_APPEND用の処理 } if ($flg & 0b0010) { // LOCK_EX用の処理 }
って書き方をするから便利なんですね。
……と、そういえば error_reporting( https://www.php.net/manual/ja/function.error-reporting.php )もこれをがっつり使ってましたねぇ。
こっちは https://www.php.net/manual/ja/errorfunc.constants.php で(とりあえず現在のバージョンの時の)「各定数の数値」まで書いてあるので、見てみるとよいか、と。
値を2進数にしてみるとわかるのですが、いずれも「1箇所だけ1、残りは0になってる2進数」になっているかと思います。
だから、error_reportingの値は、主にor(|)をつかって「複数指定」が出来るんですね*2。
この辺、多分今でも(そして多分、今後も)なにげに出てくると思われるので、知っていると便利かなぁ、と。