基礎プログラミング講座第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

転載禁止
転載禁止
このすれ転載禁止

>>48
まぁ実践あるのみだよな 文法書は読みまくっから基礎は押さえてるつもり

>>50
Cから入ったからいまいちオブ指のコツや感覚がわからんのよ

スレチすまん

懐かしきプログラミング

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
人によるけど俺はそうなんじゃねと思ふ
外部から知りたくなる状況ができちゃうのは好ましくないけど

>>99>>100
なるほどサンクス

結果を見ればシフト演算のほうが早いことがわかります
理由は明白です。
皆さん御存知の通りコンピュータは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みたいな

>>156>>158
カプセル化ってそもそも隠蔽のことじゃないだろ
オブジェクトとしていろんな処理やら変数をまとめること自体をカプセル化
隠蔽はカプセル化の一要素であってカプセル化 = 隠蔽ではない

>>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
うーん。ちょっと違うな

ビットマスクは使ったことないな
シビアなリソース管理が必要なときくらいじゃないと必要にならないんじゃないか

>>181
ここで間違えたおかげでリアルで恥かかなくてすむ、とプラスに考えるわ

>>188
ふえぇ……むずかしいよう
勉強しよう……

>>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まとめにはまだコメントがありません

名前:
コメント:


未完結のSSにコメントをする時は、まだSSの更新がある可能性を考慮してコメントしてください

ScrollBottom