基礎プログラミング講座第8回「ビットマスク」 (216)
みなさんこんばんは。
第8回プログラミング講座ではビットマスクをやっていきます。
ビットマスクとはなんでしょうか?
まずはイメージから入ります。
皆さん、マスキングテープをご存知ですか?
塗装などで特定箇所を塗りたいときに塗る箇所以外に貼ってそこに色がつくのを防ぐテープですね。
ビットマスクもこれと同様のことを行っています。
ビットマスキングテープとでも呼べるようなものを用意し、それを対象のビットの集まりに貼ってあげます。
こうすることで特定のビットに操作を行うことができます。
例えば以下のような4ビットのビット列があるとします。
1101
このビット列は左から順番に
「脱衣所の電気」
「台所の電気」
「トイレの電気」
「リビングの電気」
の状態を保持しているとしましょう。
(当然ながら1が「オン」、0が「オフ」です)
台所の電気が付いているか調べたい場合はどうすればいいでしょうか?
ここで「マスク」を使います。
ここで登場するマスキングテープは
0100
です
AND論理演算はご存知ですね。
今回は「1101」にマスキングテープ「0100」をANDでマスクしてみます
するとどうでしょうか?
1101
0100 (AND)
0100 (答え)
ノイズ「あ?なんか文句あんのか!?」
パリティ「ふえぇ……」
台所の状態が「1」であることが取り出せました。
どうようにトイレの状態も調べましょう
トイレのマスキングテープは「0010」です
1101
0010 (AND)
0000 (答え)
トイレの状態は「0」つまり電気はついていないということですね。
もうお気づきですね。
「あるビットの状態を調べたいとき」はそのビットを「1」、他のビットを「0」のビット列を用意し、ANDでマスクしてやればいい訳です。
これを使えば何ができるのか。実践してみましょう。
せんせー逆についてないことを検出したい場合はどうnotてんの?
マスキングテープは読み取りではなく書き込みに使うものだよ
あるアンケートがあるとします。
アンケートの内容は以下のとおりです
Q1. あなたの性別は?(男:0、女:1)
Q2. あなたは肥満ですか?(いいえ:0、はい:1)
Q3. あなたは幸せですか?(いいえ:0、はい:1)
Q4. 彼女/彼氏はいますか?(いいえ:0、はい:1)
いつの時代のテクニックだよ
>>18
「特定のビットの状態を調べる」わけですか同じようにマスクしてやって
結果が1なら、そのビットは1、0ならそのビットは0とわかるわけです。
トイレの例を見てください。
vipで建てると毎度変なのが湧くな
>>21
きそのきそでしょう
アンケートの回答をビット列に整形してみます。
第1ビットがQ1、第2ビットがQ2、…のようになっています。
ここでマスクするためのビット列に名前をつけてやります。
SEX 1000
OBESITY 0100
HAPPINESS 0010
PARTER 0001
と定義します。
アンケートの結果 result ビット列にたいして、以下のように書くと結果が取り出せます。(C言語の場合)
&は先程の論理演算ANDをしめしています.
if (result & SEX) {
//女の場合
} else {
//男の場合
}
if (result & OBESITY) {
//肥満の場合
}
if (result & HAPPINESS) {
//幸せの場合
}
if (result & PARTNER) {
//彼氏、彼女がいる場合
}
さらに応用してみます。
if (result & (HAPPINESS | PARTNER))
{
}
これはどのような場合でしょうか?
| は論理演算ORを表しています
幸せな場合、彼氏がいる場合、両方の場合か
0以外ならTrueってのはこういう時便利そうだけど
C言語以外でもそうなってるの?
間違えました
if ((result & HAPPINESS) && (result & PARTNER))
{
}
こうでした
これは「幸せ」かつ「パートナー」がいる場合ですね。
これを調べれば「パートナーがいることと幸せなことの関連性」を調べることもできますね。
if ((result & OBESITY) && (result & PARTNER))
{
}
これだと肥満とパートナーの有無の関連性を調べることもできます。
では実際のプログラミングではどのように使えるのでしょうか?
ある画面に何かオブジェクトが4つ描画されているとします。
メニュー、キャラクターのHP、装備してる武器、装備してる防具
みたいなものだとします。
これを選択的に消したいとしましょう。
1つずつ消す場合、メニューと武器を消したい場合はどうでしょう
delete_menu()
delete_weapon()
のように2つ関数を実行する必要があります。
JAVAから入ったからCにbooleanなくて困った
booleanって一般的には珍しい型なの?
誰かオブジェクト指向を伝授してくださいなんでもしますから!
ビットマスクを使いましょう。
MENU 1000
HP 0100
WEAPON 0010
ARMOR 0001
そして消去関数 delete を用意します。
ビットマスクを使うと消したい対象のビットを組み合わせたビット列をdeleteに送るだけでよくなります。
ユーザー側はビット列を意識する必要はありません。
どういうことでしょうか
>>45
java書いてんだろ?
適当にしてたらそれがオブジェクト指向だ
delete関数の中身は以下のようになっています
void delete(bitfield bits)
{
if (bit & MENU) {
//メニュー消去
}
if (bit & HP) {
//HP消去
}
if (bit & WEAPON) {
//武器消去
}
if (bit & ARMOR) {
//防具消去
}
}
まーたIT速報の奴が自演しながらやってんのか
いい加減、作業場としてココを使うなよ
アフィ嫌いなVIPPERの連中が怒っても知らんぞ
この関数を使うユーザーがどのように引数を指定してやればいいんでしょうか?
簡単です。消したい対象を OR で渡してやるだけです。
HPと武器が消したいなら
delete(HP | WEAPON);
です。
では解説していきます。
>>53
転載禁止
転載禁止
このすれ転載禁止
懐かしきプログラミング
HP | WEAPON
これから生成されるビット列は
0100
0010 (OR)
0110 (答え)
です。これがdelete関数に渡されます。
渡されたビット列は各ifでビットマスク処理が行われます。
>>45
金庫ならカギかけるじゃん?
馬なら走るじゃん?
金庫は走らないし馬はカギかからないじゃん?
じゃあ型とそれに使う関数には関係があるから
属性値と関数をまとめて型にすればいいじゃん?
オブジェクトじゃん?
すまん俺も初心者だから知らん
>>59
Yes
0110はどのifに引っかかるのでしょうか
if (bit & MENU) → 0110 & 1000 → 0
if (bit & HP) → 0110 & 0100 → 1→処理実行
if (bit & WEAPON)→0110 & 0010→1→処理実行
if (bit & ARMOR)→0110 & 0001→0
狙った通りの動作をしてくれています。
これってわざわざ8ビットとか使わなくても オブジェクト化すればよくね?
やっと多様性と継承辺りを理解したところだけど
〇〇を××するのが手続き型で
〇〇に××させるのがオブジェクト指向だってさ
これがビットマスクです
しかしこれだとビットを意識したプログラミングが必要になります。
やや面倒です。
もっと扱いやすい整数型を使って実現していきましょう。
ここで登場するのが「シフト演算子」です。
おそらくプログラミングを始めたばかりの人は全く使ったことがないのではないでしょうか?
x << 1
この「<<」がシフト演算子です
HPと武器(and)を渡すのに
HP | WEAPON(or)なのは気をつけないといけないな
>>66
速度は知らないけど
メモリサイズは変わってくるんじゃね
>>67
これわかりやすいな
シフト演算子とはなんでしょうか?
「ビット列を右または左にずらす」
これがビットシフトです。ビットシフトを行うために使うのがシフト演算子です。
例えば x = 00000001 というビット列があったとします。
これを左に1つシフトさせます( x << 1)
すると x = 00000010 というビットになります
2つシフトさせてやれば
x = 00000100
です
もうお分かりですね。
単にビットをずらしているだけです。
シフト演算子で値はどのように変わるのでしょうか?
2進数ではなく馴染み深い10進数になおして見ましょう
00000001
これは10進数では「1」です
1つ左にシフトさせます
00000010
これは10進数では「2」です
さらに1つ左にシフトさせます
00000100
これは10進数では「4」です
さらに1つ左にシフトさせます
00001000
これは10進数では「8」です
もうお気づきではないでしょうか?
>>58
じゃあC++風に雑にやると
手続き型指向
AddData(int a , int b)
{
int c;
c = a+b;
return c;
}
オブジェクト指向
class Data
{
int a,b;
int c;
void Add()
{
c = a+b;
}
};
よくある例え話で猫クラスがニャンニャンしてるのは余計に混乱するだけ
マイクロソフトのサンプルとか覗いてみると面白いよ
左に1つシフトすると「値は2倍になる」
左にnつシフトすると「値は2^n倍になる」
「1」を考えます
左に1つシフトした「2」これは「1 * 2 = 2」で2倍ですね
さらに左に1つシフトした「4」これは「2 * 2 = 4」2倍ですね
「1」から考えればこれは左に2つシフトしてることになります
つまり「1 * 2^2 = 4」
ということがわかります
javaだとよくget○○ってそのインスタンスの変数を得るだけのメソッド使うじゃん
あれもオブジェクト?
ではオプションをわかりやすくしてみます
次は食べる関数に食べ物を渡します
int apple = 1 << 0;
int orange = 1 << 1;
int banana = 1 << 2;
int grape = 1 << 3;
eat(apple | orange);
void eat(int fruits)
{
if (fruits & apple) {
//りんごを食べる処理
}
if (fruits & orange) {
//みかんを食べる処理
}
if (fruits & banana) {
//バナナを食べる処理
}
if (fruits & grape) {
//ぶどうを食べる処理
}
}
特殊な型を必要としていません
整数型だけで実現できています
>>87
オブジェクトってのはインスタンスもしくはオブジェクト指向の概念そのものを指すのでは
getInt()みたいなのはそのクラスから出来上がったインスタンスに対して作用するメソッドの一種(インスタンスメソッド)だよね
さらにビット演算、シフト演算は通常の計算より高速であることがメリットしてあげられます
2倍するプログラムとシフト演算の速度を比較してみましょう
ブラックボックス化もオブジェクト指向の特徴じゃん
関数のモジュール化なら手続き型でもできるけど
多態性で更に謎が深まってる感じ
>>92
すまん外部からインスタンス変数を知りたいときに
変数を隠蔽してわざわざインスタンスメソッドでgetさせるのは
オブジェクト指向?って質問したかった
>>97
なるほど カプセル化ってやつか ぼくがみたさんこうしょにはたしかかぷせるかもおぶじぇくとしこうのとくちょうだとかいてありましたねはい
>>97
外部からインスタンス変数を知りたい←これがまずオブジェクト指向じゃない
#include <stdio.h>
#include <time.h>
#define LIMIT 1000000000
int main(void)
{
clock_t start, end;
start = clock();
for (int i = 0; i < LIMIT; ++i)
{
int count = 1;
count *= 2;
}
end = clock();
printf("[通常の掛け算]%f秒かかりました¥n",(double)(end - start)/CLOCKS_PER_SEC);
start = clock();
for (int i = 0; i < LIMIT; ++i)
{
int count = 1;
count <<= 2;
}
end = clock();
printf("[シフト演算]%f秒かかりました¥n",(double)(end - start)/CLOCKS_PER_SEC);
return 0;
}
>>97
人によるけど俺はそうなんじゃねと思ふ
外部から知りたくなる状況ができちゃうのは好ましくないけど
結果を見ればシフト演算のほうが早いことがわかります
理由は明白です。
皆さん御存知の通りコンピュータは2進数で動いています。
シフト演算子はよりプリミティブな操作に近いのです
オーバーヘッドが少ない利点があります。
(しかし現在の高性能PCでは意識するほどの誤差ではありません)
>>102
そうだよね図しっかり描いて考えてみる
変数を弄る時にsetっていうメソッド?を噛ます事には意味があると思う
だから公開するよりはsetgetでやった方がいいはず
でも>>100なのかぁ
>>111
初めにInitメソッドだかで渡すもの渡したら最後まで中身に触れる必要なしってのが理想かと
以上で第8回「ビットマスク」は終了です
ご清聴ありがとうございました。
>>115
乙
>>115
おつおつ
インスタンス変数はとりあえず private で宣言
参照が必要なら ゲッタ だけ宣言
隠蔽をベースにスタートするのがモダンプログラミングだと思う
とりあえず乙
>>111
OOP的には、そもそもgetter/setterで取ってくる先が何なのか気にしなきゃいけないのが間違いなのよ
インスタンス変数を取得するgetterでもいいし、
DB問い合わせを行うgetterでもいいし、
祈祷師に値を教えて貰って返すgetterでもいい
兎に角、getter/setterこそが真であるのがOOP
もう一回強度ケツ合度から出直します
大学で必修の実習でプログラミングをファイルポインタと自分で関数を作るとこまでやって興味わいたんだけど
自学で次に何やればいい?
>>111
OOPってのはオブジェクトが中心ってことだからね
インスタンス変数にアクセスしようとすることが悪じゃない
>>127
CUIで動くRPGゲーム
これでセンター数学満点取れる様になる?
>>123
「インスタンス変数が知りたい」じゃなくて
「このデータが知りたい」(まぁそのデータは内部的にはインスタンス変数なんだけどね)
なら全く問題ないということでOK?
>>138
PC使えば余裕余裕
>>138
ならない
なるほど
中の変数が~~って言ってる時点でだめなのか
おーい>>1よ
ビット演算の変数は符号無しでやらないとダメだぞ
理由は演算オーバーフロー防止の為
>>139
OK
例えばあるオブジェクトにファイルがロードされているか調べたい
昔はファイル名の変数調べてnullかどうかを調べるみたいにしてた
今は isLoaded() 関数にアクセスして調べるみたいな
isLoaded関数の中ではファイル名の変数調べてnullならfalse、そうじゃないならtrueを返すみたいな処理してる
つまるところやってることは昔と同じ
ただそれが内部的に行われているのかどうかがポイント
>>97
単純なgetter/setter書かされてるから勘違いしてると思うんだけど、
getterとインスタンス変数は別に1対1じゃなくていいんやで
class Temparture {
private double celsius;
/* コンストラクタとかは省略 */
double getCelsius() { return celsius; }
double getKelvin() { return celsius + 273.15; }
}
上記のように温度を表すクラスがあるとして、getCelsiusはセルシウス温度くれ!って要求な訳じゃん
今たまたま内部表現がセルシウス温度だからそのまま返してるけど、
getCelsiusを呼ぶ側はTempartureクラスのメンバ変数celsiusの値が欲しい訳じゃなくて、
あるTempartureクラスのインスタンスが表している温度をセルシウス温度として要求したいだけなんだ
呼ぶ側がクラスの中身について知る必要はないんよ
>>147
お前をこの世からオーバーフローしたい
まあでもこの手の回答って分かる人にはわかるけどわからない人には分からないままだよな
結局プログラミングの数こなしてなんとなく見につけていくしかない
>>155
カプセル化をより正確に言い表すとどうなるんですかねぇ
class Temp{
private int a;
private int b;
public int setANum( int num ) { a = num; }
public int setBNum( int num ) { b = num; }
public int sum{ return a + b; }
}
とかでも別にいい
セッターゲッターは必ずペアで作らなきゃいけないわけじゃないし
どっちかだけでもいいし内部的に何をされてようがこの場合は
setANumとsetBNum呼び出して値をセットしてからsumを呼び出すと
setANumでセットしたのとsetBNumでセットしたのが加算されて返ってくるっていうことだけでいい
出来うるだけ上位レイヤーで余計なことを考えずに済むようにするのがクラスの基本
だから、クラスごとにそれぞれ出来ることを限定化する設計とか色々ある
今はオブジェクト指向が正しいっていわれているだけでオブジェクト指向が正義なわけでもない
>>156
getter/setterでアクセスすること、のみでいいんじゃね?
>>157
>setANumとsetBNum呼び出して値をセットしてからsumを呼び出すと
>setANumでセットしたのとsetBNumでセットしたのが加算されて返ってくるっていうことだけでいい
そのこととクラスの内容が完全に一致してるじゃん
この場合仕様を知り尽くしてることと同義じゃん
さっきのは理解はしているが身についてないという奴だった
オブジェクト指向初心者はこれ作れっていうお題無いのかな
FizzBuzzみたいな
>>150
なるほどなるほど
celciusって内部変数はしらないけど
getCelsiusすれば摂氏温度が、getKelvinすれば華氏温度が得られることさえわかってればいいのね
じゃあもしも呼び出し側が温度自体を必要としてなくて
「熱いか寒いか」の判断だけがほしければ
わざわざgetCelsiusして呼び出し側でifで判断しなくても
Temperatureクラスに寒暖を判断するメソッドつくってその値だけ、
つまり呼び出し側で使うデータだけ返してやればいいんだよね?
>>161
車クラスを作る
>>167
イエス
カプセル化はさっきから言ってるように
中身がすり替わってもバレないってのも重要ではないかと
>>167
その通りだが、その場合は寒暖判断クラスを別途作るべきだな
2次元ベクトル2つ作って内積求めろ
非OOP
int x1, y1, x2, y2;
x1= 2;
y1 = 3;
x2 = 4;
y2 = 5;
printf("%d", x1*x2 + y1*y2);
OOP
Vector v1 = new Vector(2, 3);
Vector v2 = new Vector(4, 5);
printf("%d", v1.innnerProduct(v2));
>>167
言いたい事がうまく伝わったようで良かった
でもgetKelvinで返ってくるのは華氏じゃなくてケルビン……(華氏はファーレンハイト)
>>177
そこまで必要なん?
デザインパターンは5個くらいしか覚えてない
Cから入った友人がclassの存在でつまずいてたなあ
>>172ありがとうなんとなくわかってきた気がする
気がするって時点でお察しだけど
getter/setterは1対1じゃなくていいっていうのは
たとえば環境クラスに温度、湿度、騒音なんかの変数があって
それぞれのセッタはあるけどゲッタはそれらから計算される快適指数だけ
みたいなイメージでいい?
>>179
Cでもこうなるんだけど
Vector v1 = {2,3};
Vector v2 = {4,5};
printf("%d", innnerProduct(v1,v2));
>>185
うーん。ちょっと違うな
ビットマスクは使ったことないな
シビアなリソース管理が必要なときくらいじゃないと必要にならないんじゃないか
>>189
C++だとパラメータ指定とかでドンドコ使うお
>>182
温度と寒暖は別の概念だろ?
「人間にとっての寒暖を判定するクラス」
「タンポポの種にとっての寒暖を判定するクラス」
「フィンランド人にとっての寒暖を判定するクラス」
とかいろいろあるが、温度は「様々な温度単位を相互変換するクラス」だけあればいい
基礎プログラミング講座おまけ「デザインパターン"メディエーター"」
おまけ講座です。
デザインパターンの意義とその一つである「メディエーター」の講座です。
>>193
おおなるほど
寒暖のクラスってイメージが浮かばなかったけど視点で分けるのね
デザインパターンとはなんでしょう?
文字通り、デザインのパターンです。
なんのデザインでしょうか?答えはクラス設計のデザインです。
このパターンでクラスを設計すると便利だよ、能率的だよ、というのがデザインパターンです。
あくまでデザインパターンはデザインパターンです。
コードは登場しません。
抽象的で概念的な説明に尽きますが、クラス設計をする上でとても重要になっていきます。
今回はその一つである「メディエーター」パターンを説明していきたいと思います。
>>193
扱う概念?によってクラスを分けていけばいいのかな
温度なら数値とその数値の単位変換ぐらい
寒暖判断は「判断をする」という機能だけを持ったクラスにわけるべきなのね
メディエーター(mediator)を辞書で引くと「仲介者」と出ます。
ではどういうパターンなのでしょうか?
一つ例を挙げます。
オブジェクトが100個あるとします。
これらのオブジェクトはすべて互いに関連しあっています。
オブジェクト1はオブジェクト2~オブジェクト100まですべてのオブジェクトと関連しており、
オブジェクト2はオブジェクト1、3~100まですべてのオブジェクトと関連しています。
オブジェクト2~100はオブジェクト1の「hoge()」という関数を利用しているとします。
ここでオブジェクト1の仕様が変わり、例えば関数名が変わるとか、処理が分けられるとかが起こったとします。
オブジェクト2~100はhoge()という関数を利用しているためオブジェクト1の仕様が変わるととたんに動かなくなります。
エラーを吐けばまだいいですが、下手に動いてしまうことさえあります。
これは致命的な欠陥です。
メディエーターパターンはオブジェクトの関連についてのパターンといえます。
オブジェクトの関連について仲介をするクラスを用意する。
これがメディエーターパターンです。
[オブジェクト1] [メディエーター]←オブジェクト1のデータください←[オブジェクト2~100]
[オブジェクト1]←あなたのデータください←[メディエーター] [オブジェクト2~100]
[オブジェクト1]→どうぞ→[メディエーター] [オブジェクト2~100]
[オブジェクト1] [メディエーター]→どうぞ→[オブジェクト2~100]
これがメディエーターパターンです
これの何がいいのでしょうか?
例えばメディエーターに「getObject1Data()」のような関数があるとします。
これはオブジェクト1のhoge()を実行して返り値をそのまま返すものだとします
つまり
foo getObject1Data()
{
return object1.hoge();
}
これだけの関数です。
この場合、オブジェクト1の仕様が変更になり、以前と同様のデータを得るためにはhoge()で返ってきた値をfuga()関数に入れてやらないといけなくなったとします。
以前だと2~100のすべてのコードが変わりましたがメディエーターパターンだとメディエーターの中身を変えるだけでよくなります
foo getObject1Data()
{
bar = object1.hoge
return object1.fuga(bar);
}
2~100は依然として「getObject1Data()」を使い続けます。
これがメディエーターパターンです。
インターフェイス部と実装部をクラスで分けるってことか
デザインパターンはクラス設計する上で利便性・能率性・保守性などがアップするようなパターンをまとめたものになります
興味がある方はぜひ調べてみてください
>>209
もしかしてメディエーターパターンの話?
なるほど
あと上でもちょっと話題になってたけど、クラスの機能ってのは基本的に細分化するものなの?
解釈次第でどうとでも"分類"できると思うんだけど、良しとされている傾向みたいなものがあるのか、
それとも本人の好みで好きなようにクラスをつくっちゃっていいのか、はたまたケースバイケースなのか
>>213
プログラムを組む上で一番便利な位置で細分化する
同じような機能でもプロジェクトAではこの機能が4つに分割されてるけどプロジェクトBでは分割されてないみたいなことはある
ケースバイケースが答え
このSSまとめへのコメント
このSSまとめにはまだコメントがありません