HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2016
0927
ペス論理積の正しい意味と使い方9解決


ペス

リンク

2016/9/27(Tue) 21:17:25|NO.77012

F1ヘルプから抜粋。


stick a,0 ; 変数aにキー状態を読み出し if a&16 : goto *spc ; スペースが押されたか? if a&32 : goto *ent ; Enterが押されたか?
このように「変数&キー情報」で複数のボタン情報が入った数値から、 1つだけのキー情報を取り出すことができます。

----------------------------------------------------------------
stick命令のリファレンスに、上記の記載がありますが、
自分が今まで知っていた『&』または『AND』(論理積)の唯一の使い方は、次の様なものです。


if aaa < 5 & bbb < 10 : mes "ok"

意味は、aaaが5より小さい、且つbbbが10より小さいなら、"ok"を表示する
であっていると思います。

しかし、ヘルプの使い方は今まで見たことがありません。

なぜ、a&16 →スペースが押されたか?となりますか?
確かにスペースのコードは16ですから、ちょうど16なら理解できます。

しかし、スペースと、←キーで17を指定した場合、あくまで合計値なのに、なぜ取り出すことができるのでしょうか?
命令の機能や配列などのデータから取り出すならわかるのですが。

ちなみに実験でこんなコードを書いたら通ってしまいました。


*start stick key,17,1 if key&8 : dialog "ok" await goto *start
スペースキーと←キーのつもりだったんですけど。


解説できる方いますでしょうか?
めんどくさい質問でしょうが、解る方よろしくお願いします。



この記事に返信する


kanamaru

リンク

2016/9/27(Tue) 21:41:59|NO.77013

それぞれのキーのコードを見て共通点に気が付きませんか?
答えは、2の累乗になっています。
つまり、2進数の考え方です。
だから、ビット演算と相性がいいんです。
ビット演算における論理積とは、各ビット(2進数の各桁)を比べて、
同じだったら演算結果の対応するビットを1にして、
違ったら結果の対応するビットを0にします。
だから、例をそのまま使わせて貰うと、
17は、2進数で表すと、10001となり、
これと、例えば、16の論理積を取ると、
16を2進数で表すと、10000になるので、
右から5番目のビットが1になるので、
結果として、if文では、1以上の数字を条件式にすると、
真が帰っくるので、16のキーコードが取り出せるのです。



ペス

リンク

2016/9/27(Tue) 23:03:08|NO.77014

む、むずかしいです。。。
ビット演算ですか。そんなの、初めて知りました。

とても丁寧な解説、ありがとうございました。
しかし、自分は頭が悪いようなので、すんなり理解できません。

他に参考になるサイトなどご存知ではありませんか?

もしくは、もっとかみ砕いた説明をお願いしたいのですが。



cats

リンク

2016/9/27(Tue) 23:50:13|NO.77015

本当は&と&&を区別して理解すべきなのですが、HSPの仕様上

if aaa < 5 & bbb < 10
のような書き方が出来てしまうので混乱してしまいます。
上のように「AかつB」を表すときの(if文の)ANDは

if aaa < 5 && bbb < 10
のように&&を使うのが一般的です。
これについては感覚で使い方が分かるでしょう。

次に&について説明します。
「a & b」はaもbも1のときに1になります。それいがいは0です。
つまり、
0 & 0 = 0, 0 & 1 = 0, 1 & 1 = 1
のようになります。
ですがaにもbにも実際には16とかの大きい数字が入ります。
そのようなとき、aとbを二進数で表したときに、各桁の&を取っています。
例えば14(=1110)と3(=0011)の&を取ると、
1110
0011
------
0010
と各桁同士を&して、14 & 3 = 2(=0010)になります。

次にキーコードですが、stickのキーコードはkanamaruさんが仰るように
2の乗数になっています。
「左キー」=1(=0001)
「上キー」=2(=0010)
「右キー」=4(=0100)
例えば「stick key, 15」で左キーが押されたら、key = 1になります。
上キーが押されたら、key = 2になります。
ここで左キーと上キーを両方押すと、key = 3(=0011)になります。
足し算と考えてもらってもいいですが、実際には|(論理和)をとっています。
ここらへんはHSP側がやってくれるので無視しましょう。

さて、ではkey = 3(=0011)のとき、左キー(1)が押されているかを判定したいとします。

if key == 1
としてしまうと、複数のキーが同時押しされたときには反応しません。
ここで登場するのが&です。
先ほど左キー(1)と上キー(2)を同時に押した際にはkey = 3(=0011)になると説明しました。
ここで、3 & 1 = 0011 & 0001 = 0001 = 1です。
確かに同時押ししても、取得したいキーコードが取れています。
また、上キー(2)だけを押したときはkey = 2(=0010)になります。
ここで、2 & 1 = 0010 & 0001 = 0000 = 0です。
左キーは押していないので0になりました。
なので、「key & (取りたいキーコード)」の結果は
1.そのキーが押されていればキーコードが取得できる。
2.そのキーが押されていなければ0が取得できる。
となります。

ではなぜ

if key & 16
のような書き方ができるかというと、これは

if key & 16 == 16
を省略しているのです。
本来こういった記述は省略できないのですが、
HSPの仕様上省略できてしまいます。
そういう言語仕様的なところは説明が難しいので省略させていただきます。

とにかく、「key & (キーコード)」で
キーコードがkeyに含まれているかを調べている、と考えてください。



掘木

リンク

2016/9/28(Wed) 00:31:32|NO.77016

一応catさんの言語仕様に関した箇所の補足。

HSPにおいては、&と&&は完全に同義と扱われます。
(常にビット演算になる。コンパイラレベルで同義とされる。
 実際にHSPの実行バイナリに論理演算子は存在しない。)
ただし、ビット演算をしていること、真偽値演算であることを明確に意図付けを行うために
コード上で&と&&を使い分ける価値は大いにあります。(論理演算子と勘違いして嵌ることもありますがね)

で、比較演算子である"<="とか">"とか"="とかは、
比較結果が真であれば1、そうでなければ0となる、そういう演算を行うものです。
ですので、if ( 0 < a < 6 ) などと書くと、
( (0<a) < 6 )と扱われ、aが比較可能であれば(0<a)の実行結果が0か1なので、どうあっても真になる。

一方のビット演算子は、足し算とか掛け算とかそういうものに近いので別に0か1になるとかそんなことはない。

if文は、整数を与えると0であるかないかを判別し、if節に入るかelse節に入るかが確定します。(0ではない場合、if節)
ですので、if ( a & 16 ) に対して比較演算子を使用するのであれば、
if ( ( a & 16 ) != 0 ) とするほうが正確な表現になるでしょう。
※なお、この記述をすると、!=演算子と0を評価するコードを生成するため
 実行速度及び実行サイズが大きくなります。

オマケ:
( a & 17 ) は、( a & ( 16 | 1 ) ) から、 ( ( a & 1 )|( a & 16 ) ) と等価と考えられます。(分配法則)
この場合は( ( a & 17 ) != 0 )と( ( a & 17 ) == 17 )の表現に明確な差が出ます。



KOMARI

リンク

2016/9/28(Wed) 09:47:06|NO.77017

便乗して投稿、もっきゅりと噛み砕いてみる。(・皿・)

------------------(・ω・)--------------------

分かりやすく4キーだけで考えてみましょう。
上下左右のアローキーの押下を判定できるとします。
[上を押しているか][下を押しているか][左を押しているか][右を押しているか]
という4つの情報があればいいですね。
これを数字に置き換えると、
[0か1][0か1][0か1][0か1](0は押していない、1は押している)
と考えることができます。
例えば上と下を押していれば、
[1][1][0][0]
と表現できますね。
逆に[0][0][1][1]という数からは、左右キーのみを押している状態とわかります。

さて、"[0][0][1][1]から左右キーのみを押している状態が分かる"、と簡単に言いました。
がしかし、人間は左二つが[0],右二つが[1]であるから当たり前にわかりますが、機械はそうもいきません。
ということで、何かしらの判定を与える必要があります。

------------------(・ω・)--------------------

[?1][?2][?3][?4]という中身がわからないものを渡されたとき、どのキーが押されているのかを調べる方法を考えてみましょう。
例えば下キーが押されているかは、[?2]が1であるかを調べればいいですね。
つまり、[?2]以外の[?1],[?3],[?4]には関心がありません。
関心がない場所は0とすると、[0][1][0][0]という数字を考えることができます。
これを[?1][?2][?3][?4]と"組み合わせ"れば、何かしら結果を得られるのではないでしょうか。
("組み合わせ"は結論から言えば"&演算"である)

[?1][?2][?3][?4]
[0] [1] [0] [0]

と縦に並べてみましょう。(小学校の時の筆算のように)
まず[?4]と[0]を"計算"してみましょう。
[?4]がなんであれ、その数字には別に関心がないので結果は[0]としておきます。
これは[?1]や[?3]にも言えることです。
つまり、

[?1][?2][?3][?4]
[0] [1] [0] [0]
----------------
[0] [ ] [0] [0]

ここまでは計算結果が確定します。
[?2]と[1]の計算は、[?2]が0ならキーを押してないので[0]、そうでないなら[1]としたいです。

[?1][?2][?3][?4]
[0] [1] [0] [0]
----------------
[0] [?2] [0] [0]

筆算の結果は"[0] [?2] [0] [0]"となりました。めでたしめでたし。

------------------(・ω・)--------------------

では、具体例として、?1〜?4に実際数字を入れてみましょう。
[0][1][1][1](上キー以外押している)
これに対して左キーを押しているか?と問いたい時は、

[0][1][1][1]
[0][0][1][0]
------------
[0][0][1][0]

計算結果は、[0][0][1][0]です。
[左を押しているか]の部分が[1]となったので押しているようだ、ということがわかりますね。

では、"上キーと右キーをどちらも押しているか"はどうなるでしょうか。
実際には"上キーと左キーを押している"状態であると仮定します。
上と左を押している状態は、[1][0][1][0]と表せますね。
"上右を押している"の判別に使うコードは、[1][0][0][1]となります。

[1][0][1][0]
[1][0][0][1]
------------
[1][0][0][0]

結果が出ました。[1][0][0][0]は上のみが押されている、ですね。
[1][0][0][0]は、[1][0][0][1]と"一致しない"ので上右は同時に押されていないとわかります。

------------------(・ω・)--------------------

で、実際これらをプログラム上での話に置き換えていきます。

まずは、先に書いた通り、筆算が"&演算"という話から。
[0] | [0] | [1] | [1]
[0] | [1] | [0] | [1]
--- | --- | --- | ---
[0] | [0] | [0] | [1]
結局、筆算にはこの4種類の組み合わせしかでてこないですよね。
結果はご存知の通り、[0],[0],[0],[1]です。
つまりどちらかが0なら0,両方1なら1になるのです。これが"&演算"というやつです。
プログラミング風に書くと、
0&0 → 0
0&1 → 0
1&0 → 0
1&1 → 1
とまとめることができます。

------------------(・ω・)--------------------

もう一つは[0][0][1][0]の[]区切りの数字の話。
当たり前ですが、[]には0か1かしか入りません。そう決めたのだから当たり前か(・ω・)
これはいわゆる2進数というやつですね。

[]の中に0〜9が入るのが、普段使っている10進数というやつなのです。
"一万二千五百十九"は12519です。あたりまえの話ですが。
でもこれって実際は、
1*10000+2*1000+5*100+1*10+9*1の結果なんですよねえ。
ここで、位が一つ左にずれると、掛かる数字に10を掛けることが見てわかります。(ややこしいですが)
(例えば100の位の次は1000の位、これは100*10の結果と考えられる)

さてさて、2進数の例として、下右同時押しの[0][1][1][0]を考えてみましょう。
一番右は0*1ですねえ。その左の1はどうなる?
答えは1*2です。なんたって"2"進数ですからね。
同様に考えると、
0*(2*2*2)+1*(2*2)+1*(2)+0*(1)
が、下右同時押しを表す"数"を10進数にしたものになります。
結果としてはこれは4+2=6ですね。
さて、この数字に対して右キーを押しているか?と聞きたい時はどうしましょう。
もちろん[0][0][1][0]を用意しますね、これは10進数では"4"です。

0110(2) & 0010(2) の結果が 0010(2) なら右キー押下

6(10) & 4(10) の結果が 4(10) なら右キー押下

と書き換えることが出来ました。

------------------(・ω・)--------------------

さて、いよいよ

stick key,17,1 if key&8 : dialog "ok"
の話に移ります。

まず、stickの第2引数に17を設定していますが、これは
>>スペースキーと←キーのつもりだったんですけど。
という意図とは、残念ながらずれています。リファレンスを読み直しましょう。
ここに値を設定したって、keyには1〜1024の"組み合わせ"が入ります。

------------------(・ω・)--------------------

さてさて、やっとできる本題の"if key&8"の話。
上の方の例で"6(10) & 4(10) の結果が 4(10) なら右キー押下"と言いましたよね。
これを実際に記述すると、
if (key&4) == 4 に相当します。
だから今回は、
if (key&8) == 8
とでも記述するべきだったのですが、"== 8"がどっかいってしまいました。
(ちなみに(key&8)と()を付けているのは、こちらの演算が先であるとわかりやすくするため)

実は"(key&8) == 8"というのは、
(key&8)が8でないなら0、(key&8)が8なら1
をとる値となるのです。
つまりif文で実際に判定されるときには、
if 0かif 1のどちらかの形になっているのです。
ifの後のdialog "ok"が実行される条件っていうのは、
if 0ではない場合、という風に内部で決められています。

訳が分かんないよ!というのなら、

mes str(8==-1) mes str(8==0) mes str(8==5) mes str(8==8) if -1 : mes "ok:-1" if 0 : mes "ok:0" if 1 : mes "ok:1" if 5 : mes "ok:5"
こんなソースを実行してみて下さい。
気持ち悪いかもしれませんが、普通に通りますし動きます( ・ω・)(・ω・ )
これを動かしてみれば、いろいろと察すると思います。

------------------(・ω・)--------------------

ここまでの話から、"==8"が省略できる理由、なんとなくわかりましたか?
key&8の結果は0か8にしかなりません。
つまり、if (key&8)は、if 0かif 8にしかなりません。
前者ならdialog "ok"は実行されないし、後者なら実行される。
そういうことです。if 1でもif 8でも動くならどっちでもいいやと、そんなノリなのです(´・ω・`)

------------------(・ω・)--------------------

ヲシマイ。長々とすいませんでした。
お偉いさんの方々からいろいろ突っ込まれそうですけど、少し目をつむってくださいな。
拙い文章ですが、少しは参考になってくれればうれしいです。



にゃんちゃん

リンク

2016/9/28(Wed) 15:37:12|NO.77018

横からですが。こんなに分かりやすいビット演算の説明は初めて見ました。それだけです、すいません(笑)



ペス

リンク

2016/9/28(Wed) 20:33:05|NO.77019

暇ができたので来てみれば。回答の山でビックリ。
どなたさまも丁寧にかみ砕いてある解説本当に感謝です!
ビット演算って、本っ当に奥が深いですね!

まだ正直、完全には理解できていませんが、皆さんのおかげで疑問部分は解決できました。
ビット演算の存在自体知らなかったもので。
ありがとうございました!


ところで、どうでもいい事ですが、みなさんのハンドルネーム、猫っぽくてかわいいですよね!



GENKI

リンク

2016/9/28(Wed) 22:58:23|NO.77020

皆さんレス早いなー。

> 他に参考になるサイトなどご存知ではありませんか?

「ビット演算 HSP」で検索するとすぐ出ますが書いておきます。
この話題が出ると毎回出しているアドレス。
http://wiki.hsp.moe/HSP%E8%AC%9B%E5%BA%A7%EF%BC%8F%E3%83%93%E3%83%83%E3%83%88%E6%BC%94%E7%AE%97.html
http://wiki.hsp.moe/%E5%B0%8F%E3%83%AF%E3%82%B6%EF%BC%8F%E3%83%93%E3%83%83%E3%83%88%E6%93%8D%E4%BD%9C.html



USER

リンク

2016/10/14(Fri) 20:13:36|NO.77120

HSPはC系の論理積がないんですよね
論理積のつもりで使ったらビット演算されて期待外の動きされます



ONION software Copyright 1997-2023(c) All rights reserved.