ビット演算って何?
コンピュータは0と1の信号で、2は理解できません。0と1しか理解できないなら、桁を増やせばいいじゃない。ということでコンピュータは膨大な桁を処理しています。だからコンピュータは2を表現する時、10(イチゼロ)と表現します。桁が上がってしまうんですね。
同じ仕組で
3=11
4=100
5=101
6=110
7=111
8=1000
9=1001
10=1010
という具合です。これを2進数といいます。コンピュータリテラシーの基本のキホンですね。
ビット演算を使うメリットと使いみち
ビット演算っていうのは、この2進数の値で計算をする、割とコンピュータよりな計算方式です。ビット演算はうまく使うと便利です。
例えば、ユーザ毎に機能の有効無効を判断する必要があったとします。スイッチは8個としましょう。
機能1・機能2〜機能8
あるユーザが、機能1〜機能8のうち、どの機能が有効でどの機能が無効か判断する時、一番簡単に思いつくのは、おそらくこんな処理でしょう
var enableFunc1 = true; var enableFunc2 = false; var enableFunc3 = true; //略
これでも正解です。しかしいちいち変数を8個用意するのが大変です。配列やオブジェクトでやってもいいんだけど、まぁいずれにせよその分の箱を用意するのが大変です。
とくにユーザの機能が有効か無効かの状態を、MySQLに保存するとなると、それだけのフィールドを用意する必要があります。
enableFunc1フィールドつくって・・・1enableFunc2フィールド作って・・・
使えるかどうかの判断はクエリを書いて、えぇと
select enableFunc1 , enableFunc2 ,enableFunc3 from user where userID = ・・・
結構たいへんです。こんなときに使えるのがビット演算です。
例えば、userStatus という変数1個に、1〜8種類のスイッチのオンオフ状態を保存できます。
仮に、userStatus = 5だとしましょう。
5は、2進数で101です。先頭のゼロを省略せずに書くと
0000 0101 です。
このとき、1のついてるところはスイッチが有効で、0のついてるところはスイッチが無効と考えると、変数1個で8種類のスイッチのオンオフ状態を判断できるようになります。
あとは、任意の桁のビットを取り出せれば、スイッチが有効か無効か判断できます。取り出すときは、2の倍数の値で、&のビット演算すればいいんです。
1桁目:1 (2進数 :0000 0001)
2桁目:2 (2進数 :0000 0010)
3桁目:4 (2進数 :0000 0100)
4桁目:8 (2進数 :0000 1000)
5桁目:16 (2進数 :0001 0000)
6桁目:32 (2進数 :0010 0000)
7桁目:64 (2進数 :0100 0000)
8桁目:128 (2進数 :1000 0000)
おしゃれに16進数で表記したり、シフト演算でスマートに書くこともできますが、まぁそれは置いといて、先の例に戻りましょう。userStatus=5です。
5は、0000 0101でしたね。
1桁目のビットを取り出すには、1桁目のマスクを使います。つまり1でマスクします。
&は、両方真の時真なので
0000 0101 ( 5です)
0000 0001 ( 1です)
0000 0001 ( 結果)
ということになります。結果が0じゃないので、if文で書くとこれは真として評価されますね
if(5 & 1 ){ //スイッチ1番が有効な処理が書ける }
あとは同じ理屈なんです。よって
if(5 & 1){ //スイッチ1が有効 } if(5 & 2){ //スイッチ2が有効 } if(5 & 4){ //スイッチ3が有効 } //略
うまく説明できてないなぁって、ブログ書きながら思ってます。ビット演算についてもっとわかりやすいサイトが沢山あるので、そちらを参照してみてください。
Angular JSでビット演算のフィルターを作る
ビット演算を使った、Angualr JSのサンプルコードです。この例では、1この変数bitが、1〜8のスイッチの状態を判断します。
無効になっているスイッチは、画面に表示しません
まずはHTMLのソース
<div class="btn-group"> <label class="btn btn-success" ng-model="radioModel" uib-btn-radio="'1'" ng-show="bit | mask:1" >スイッチ1</label> <label class="btn btn-success" ng-model="radioModel" uib-btn-radio="'2'" ng-show="bit | mask:2" >スイッチ2</label> <label class="btn btn-success" ng-model="radioModel" uib-btn-radio="'3'" ng-show="bit | mask:4" >スイッチ3</label> <label class="btn btn-success" ng-model="radioModel" uib-btn-radio="'4'" ng-show="bit | mask:8" >スイッチ4</label> <label class="btn btn-success" ng-model="radioModel" uib-btn-radio="'5'" ng-show="bit | mask:16" >スイッチ5</label> <label class="btn btn-success" ng-model="radioModel" uib-btn-radio="'6'" ng-show="bit | mask:32" >スイッチ6</label> <label class="btn btn-success" ng-model="radioModel" uib-btn-radio="'7'" ng-show="bit | mask:64" >スイッチ7</label> <label class="btn btn-success" ng-model="radioModel" uib-btn-radio="'8'" ng-show="bit | mask:128">スイッチ8</label> </div>
ng-showでフィルターを使います。変数bitの値に応じて、スイッチ1〜8を表示したり非表示にしたりします。
続いてJavaScript
.controller('sampleCtrl' , function($scope){ $scope.radioModel = '1'; $scope.bit = 5; //略
スイッチ1〜8の状態を表す変数bitには、整数の5がセットされました。
(通常この値は、サーバなりどこなりに保存されているものです)
最後に、フィルターのソース
.filter('mask' , function(){ return function(data , key){ if(data & key){ return 1; }else{ return 0; } } })
ソース自体はシンプルです。
補足すると、dataは$scope.bitの値です。つまりここでは常に5が入ってます。
keyは、それぞれのスイッチのng-sohwから渡されてきます。1,2,4,8,16,32,64,128のどれかが入ってます。
&で計算した結果、0なら当該ビットは0(つまり当該スイッチは無効)ということですので、0を返します。
0以外の場合はビットが立っているので、つまり当該スイッチは有効と判断できます。よって1を返します。