mypc:~ current$ irb
irb(main):001:0> "irb完全に理解した".length
=> 10
irb(main):002:0> ~
irb(main):003:0* "irb完全に理解した".length
=> -11
irb(main):004:0>
#はじめに
ことの発端は上記。何をしているかを簡単に説明すると
1行目$ irb実行
2行目> "irb完全に理解した"という文字列に対しlengthメソッドを実行
=> "irb完全に理解した"の文字数である10が返り値として出力
4行目> ~を入力
5行目* "irb完全に理解した"という文字列に対しlengthメソッドを実行
=> -11が返り値として出力される
なぜ ~ を入力したのかはこの際問題ではない(実際ただのタイプミスだ)。
ここで生じた疑問は以下の3つである。
疑問1, ~ とはなんなのか?
疑問2, プロンプトの末尾が * になるのはなぜか?
疑問3, なぜ1,2の状態において返り値が変わるのか?
###疑問1について
~(チルダ)はIntagerクラスのオブジェクト(整数)に対してビット単位で演算を行うための演算子(ビット演算子)の一種である。
その役割はビット反転であり、~a(a = Integer)と記述することで「aを2進数に変換した際の各桁の数字(0或いは1)をそれぞれ入れ替える(0ならば1に, 1ならば0に)」というもの。
例えば、10進数における25は2進数では11001と表現されるが、それを~(チルダ)でビット反転させると00110(10進数における6)となる。
###疑問2について
プロンプトの末尾が * になっているのは、irbが「コードが完結していない」と判断しているからである。例えば
mypc:~ current$ irb
irb(main):001:0> hoge = "ホゲ"
=> "ホゲ"
irb(main):002:0> hoge =
irb(main):003:0* "ホゲ"
=> "ホゲ"
上記2~3行目の処理と4~6行目の処理は全く同一のものであると言える。
冒頭の5行目も、演算子である ~ を記述しただけの状態で改行したため、
irb側が「まだコードは完結しておらず、次の行には ~ によってビット反転される何かしらの値が入力されるはずだ」と認識したからであると思われる。
###疑問3について
疑問1,2の解答を踏まえ、もう一度冒頭のコードを確認する
mypc:~ current$ irb
irb(main):001:0> "irb完全に理解した".length
=> 10
irb(main):002:0> ~
irb(main):003:0* "irb完全に理解した".length
=> -11
irb(main):004:0>
10進数の10を2進数に変換すると1010。
これに ~ をあてがうと、0101となる。
0101を10進数に変換すると、5となる。
この結果、返り値は5に––––––––––––––––––––––––––
あれ?
#新たな疑問 ~ちゃぶ台返し~
ありえない事態が起こったなら
疑うべきは2つだけだ
前提条件が間違っているか
それともあんたの頭がいかれちまったか
〜狡噛慎也〜
そもそもの前提として私の頭はいかれていない。
これすら間違っているなら、最早この記事は出来損ないのドグラ・マグラである。
なので残る二つの前提条件、即ち疑問1,2の解答を疑うしかない。
疑うしかない。が、ともかくもまずは、情報を改めて整理しようと思う。
##~(チルダ)について
~(チルダ)について、改めて調べ直した。
Google師父に「ビット演算子 ~ チルダ」と入力し検索をかける。
するとC#におけるチルダについて質問しているページがヒットした。他言語に関するページではあるが、共通する部分もあるだろう。
曰く、「これは、ビットごとの否定とも呼ばれる ビットごとの補数 演算子です。」とのこと。
ビットごとの否定...。これは疑問1でも言及した「ビット反転」と同義と捉えて問題ないだろう。
気になるのはビットごとの補数である。ご丁寧にリンクが貼られているのでそちらを見に行く。
"The ~ operator produces a bitwise complement of its operand by reversing each bit:"
「~オペレータは、各ビットを反転させることによって、そのオペランドのビット単位の補数を生成します。」※Google翻訳
未履修言語の話なのであまり適当なことは言えないが、ニュアンスは概ね同じのように思える。
しかしまた登場した。補数。...補数。補数???補数ってなんだったっけか。
とにかく、「~aとすることでaの補数を生成する」ということらしいが...
##補数について
いきなり数学の授業である。Wikipediaを見れば全部書いてあるが、
「足すことによって桁上がりする数」
というのが補数の定義らしい。
例えば10進法における45に対する10の補数は55であり、
2進法における1101101に対する2の補数は10010である。
##~aをaに対する補数と仮定する
仮定の上で、再三にはなるが冒頭のコードを確認しよう
mypc:~ current$ irb
irb(main):001:0> "irb完全に理解した".length
=> 10
irb(main):002:0> ~
irb(main):003:0* "irb完全に理解した".length
=> -11
irb(main):004:0>
10進数10を2進数に変換すると1010
10進数-11を2進数に変換すると11111111111111111111111111110101
...下4桁、明らかに怪しい。
が、とりあえずいくつか組み合わせを試して検証してみる。
1がいっぱいである。そしてどうやら
10進数において、入力値(~は除く)+出力値=全て-1に
2進数において、入力値(~は除く)+出力値=全て11111111111111111111111111111111
になるらしいということがわかった。
#ひとまず結論
つまり、irbにおいて~(チルダ)は、11111111111111111111111111111111を基数とした上で
入力された値を2進数変換した値に対する11111111111111111111111111111111の補数を返り値として出力していたのである。
#11111111111111111111111111111111ってどこからきた?
状況は理解した。が原因がわからない。
11111111111111111111111111111111
32桁に1が収まっている。32...?
32ビットとか?関係ある?