Rubyでプログラミング入門 RSSフィード

2006-12-19

ブログバトラー

これ

http://blogbattler.com/


今現在コイツに夢中です。

とても夢の広がるサービスだと思います。とにかく楽しい。

こんなときは、JavaScriptの使えないはてなダイアリーがちょっとだけ悔やまれますね。


Rubyの記事書くのはサボっておいて、こんなのだけ反応してすいません……

また必ず続き書きますよ、というのを含めてブログバトラーのブックマークレット置いておきます。


ブックマークレットid:amachangさんとこから頂いてきました。

いつもいつも、視点が素晴らしいと思います。


javascript:var d=document.createElement('div');d.innerHTML='<object data="http://blogbattler.com/bbparts.swf" type="application/x-shockwave-flash" height="288" width="140"><param name="movie" value="http://blogbattler.com/bbparts.swf"><param name="wmode" value="transparent"><param name="FlashVars" value="id=201">BlogBattler.</object>';document.body.appendChild(d);var s=d.style;s.position="fixed";s.top="100px";s.left="100px";s.zIndex=1000;void(0);

2006-11-07

[]結婚式を挙げました

私事もいいとこで恐縮です。日曜日に挙式してきました。

勉強どころじゃない日々でしたが、おかげさまでとても素晴らしい日になりました。

あれだけ多くの方に祝福され、多くの方に感謝する日は二度とないような気がします。

ひととおり良い思いをさせて頂いたので、いい加減気持ちを切り替えて頑張ります。


あ、あと。

はてなパーカー欲しい!

[]クラスとメソッド

メソッド < オブジェクト < クラス

前回演算子式を学んだ際、気になる部分を残していました。

ほとんどの演算子は特別な形式のメソッド呼び出しですが、一部のものは言語に組み込みで、再定義できません。

さて、演算子の正体は特別な形式のメソッドだと云われています。そのあたりについて突っ込んでみてみましょう。


まずは、メソッドとはそもそもなんだったでしょうか。

メソッドとは 【method】 - 意味・解説 : IT用語辞典 e-Words

 オブジェクト指向プログラミングにおいて、各オブジェクトが持っている自身に対する操作。オブジェクトは「データ」と「手続き」から成っているが、その「手続き」の部分に当たる。プログラミング言語によっては「メンバ関数」と呼ばれることもある。

 オブジェクト指向では、オブジェクトの持つデータを操作する方法はオブジェクト自身がメソッドとして内蔵しており、これを外部から呼び出すことによって操作を行なう。こうすることにより、操作の詳細をオブジェクト内部に隠蔽することができ、プログラムの再利用性や生産性を高めやすくなると言われている。

むむ。

オブジェクトが持っている自身に対する操作』だそうですね。

それなら“オブジェクト”とは何なのでしょうか。

オブジェクトとは 【object】 - 意味・解説 : IT用語辞典 e-Words

 オブジェクト指向プログラミングにおいては、ソフトウェアが扱おうとしている現実世界に存在する物理的あるいは抽象的な実体を、属性(データ)と操作(メソッド)の集合としてモデル化し、コンピュータ上に再現したもの。オブジェクトを定義するモデルはクラスと呼ばれる。クラスに基づいて実際にコンピュータのメモリ上に展開されたオブジェクトのことをインスタンス(実体)と言うが、このインスタンスの意味でオブジェクトと呼ぶ場合も多い。

くらくらしてきました。カタカナと漢字の多い解説で涙が出ます。

順番に考えてみます。


  1. オブジェクトとは、現実世界に存在する実体をモデル化し、コンピュータ上に再現したもの
  2. そのモデルはクラスと呼ばれる
  3. クラスに基づいて実際に展開されたオブジェクトインスタンス(実体)と云う
  4. オブジェクトインスタンス

……という感じでしょうか。

取り敢えず“クラス”とやらも調べてみてから考えましょう……

クラスとは 【class】 - 意味・解説 : IT用語辞典 e-Words

 オブジェクト指向プログラミングにおいて、データとその操作手順であるメソッドをまとめたオブジェクトの雛型を定義したもの。これを定義することで、同種のオブジェクトをまとめて扱うことができるようになる。クラスに対して、具体的なデータを持つ個々のオブジェクトは「インスタンス」と呼ばれる。なお、クラスの定義を他のクラスに受け継がせることを「継承」と言う。その際、元になるクラスを「スーパークラス」(super class)、あるいは「基底クラス」「基本クラス」(base class)などと呼び、新たに定義されたクラスを「サブクラス」(subclass)、あるいは「派生クラス」(derived class)と呼ぶ。

orz


これらの説明から、なんとか糸口を見つけなければなりません。

ぼーっと眺めてみると、3つの説明文に共通する記述があることに気が付きました。

・メソッドの説明文

オブジェクトは「データ」と「手続き」から成っている


オブジェクトの説明文

属性(データ)と操作(メソッド)の集合としてモデル化


・クラスの説明文

データとその操作手順であるメソッドをまとめたオブジェクトの雛型


……と。

言い回しは少しずつ違いますが、

  • データ(属性)
  • メソッド(操作)

という2点が繰り返し出てきていますので、これが重要なポイントなのではないかと思いました。


データとメソッド

そもそもプログラミングというものは、実際に存在している解決すべき問題を“プログラミング言語”によって表現し、コンピュータにその解決を依頼する行為だと云えます。

その際には、現実世界に存在する(物理的・抽象的な)モノが関わってきます。

このような“モノ”のことを、オブジェクトインスタンス)と呼んでいるようです。

オブジェクトはクラスと呼ばれるモデルに基づいて作られる実体、でした。逆に言うと、クラスはオブジェクトの雛型です。

つまり、オブジェクトがどのような特性を持っているのかは、クラスが知っているということです。

それではクラスはどのような存在かというと、『データと、その操作手順(メソッド)をまとめて定義したもの』だという話でした。


実例で考えてみましょう。せっかくなので、以前利用した自動販売機さんに再度登場願いましょう。


自動販売機プログラム

自動販売機シミュレートするプログラムを考えてみます。まずは、その中に登場するモノを検討します。

簡単に思いつくのはこんなとこでした。

これらは実際に存在するもの(実体)ですので、オブジェクトインスタンス)ということになりそうです。

ということは、これらを定義するクラスが必要だ、ということです。

ということで良いのでしょうか。*1


さて、クラスは「データとその操作手順(メソッド)」を持つのでした。

それでは自動販売機クラスを例にとって、データとメソッドを検討してみます。

まず簡単にわかりそうなデータは、

  • 飲み物(複数)

ではないでしょうか。

自動販売機ですから、飲み物を持っててくれないと困ります。

続いて自動販売機の操作ですが、

  • お金を入れる(というか入れられる?)
  • ボタンを押す(押される?)
  • 飲み物を出す
  • お金を返す

とかこんな感じでしょうか。自動販売機の立場に立って考えるところなのかどうかわからなくて、言い回しが微妙です。


とりあえず、ここまで検討した内容でシミュレートしてみます。

f:id:mailishmaid:20061107154955j:image

  1. まず、お金を入れます
  2. 次に、欲しい飲み物のボタンを押……

いきなり躓きました。何と見通しの甘いことでしょう。

冷静に考えてみると、ちょっと情報が少なすぎるような気がします。

  • 今現在、いくら自動販売機に入れてあるのかわからない
  • どの飲み物を買える状態にあるのかわからない

これでは使い勝手が悪すぎます。この二つの情報を自動販売機が管理していなければいけません。

今一度考え直してみると、自動販売機クラスが持つデータは、

  • 飲み物(複数)
  • 現在投入済みのお金

となります。


あれ、「どの飲み物が買えるかどうか」はデータとして持たなくて良いのでしょうか?


色々と考えてみたのですが、どの飲み物が買えるかという情報は、既に自動販売機が持っている「飲み物(複数)」と「現在投入済みのお金」から自動的に計算されるような気がしたのです。

必要であれば計算で求められるので、わざわざ計算結果を自動販売機が常に持っていなくてもよさそうです。

そのかわり、「現在買える飲み物はどれかを知らせる機能」を自動販売機に持たせてあげようと思います。


ということで自動販売機の機能(操作、メソッド)は、

  • お金を入れられる
  • ボタンを押される
  • 飲み物を出す
  • お金を返す
  • 現在投入済みの金額を表示する
  • 現在購入可能な飲み物を知らせる

という感じに増えました。

投入済み金額をデータとして管理することにしたので、それを表示する機能もついでに追加しています。


f:id:mailishmaid:20061107154952j:image

うーん、これだけ用意してあればなんとなくそれなりのシミュレーションはできそうです。


まとめ

[]リテラルの正体を暴く

演算子はメソッドの呼び出しですよ、というところからはじまって、オブジェクトやクラスの話になってしまいました。

もう一度演算子のところに戻ってみましょう。

11 + 5

たとえばこんな何の変哲も無い足し算すら、メソッドの呼び出しである、という主張でした。

今の私は、メソッドがあるからには、そこにはその『メソッドを持っているオブジェクトがある』はずだ、ということを知っています。

そうは云っても、現在『 + 』のほかの登場人物といえば『 11 』と『 5 』だけですね……

普通に考えてみたら、このどちらかが、『 + 』のメソッドを持っているはずです。


しかし、今回は同じ数値リテラルですから、どちらで考えても同じことです。結局、数値リテラルも実はオブジェクトだよ、ということになるのではないでしょうか。

オブジェクトということは、その基になるクラスが必ずあります。


それでは『 11 』や『 5 』は何というクラスのインスタンスなのでしょうか。

その正体は、『Fixnum』クラスです。

Fixnum


ほんとかよ、って云われると困るので、ちょっと話が飛びますがオブジェクトのクラスを知る方法を紹介します。

例えば『 11 』のクラスが知りたければこのようにします。

puts 11.class

↓実行結果

Fixnum

このように、

<オブジェクト>.class

という形式で、そのオブジェクトが属するクラスを取得することができます(言い方を変えると、classというのはクラス名を返却するメソッドです)。

これを実行すれば、『 11 』はFixnumクラスのインスタンスである、ということが確認できると思います。


同じように、"Hello"のような文字列リテラルや、3.14のような浮動小数点数でもクラス名を確認できます。

puts "Hello".class
puts 3.14.class

↓実行結果

String
Float

文字列リテラルはStringクラスのインスタンスで、浮動小数点数はFloatクラスのインスタンスのようです。

これらはそれぞれ、String型・Float型というような言い方をされることもあります。


ともあれ、数値リテラルオブジェクトで、四則演算しているときも実際は定義されているメソッドを呼び出しているんだよ、というのがなんとなくわかってきました。

ついでなので、本当にそんなメソッドが定義されているのかも見てみたいと思います。

オブジェクトの持つメソッドを知るには、たとえば次のようにします。

puts 11.methods

methodsというのは、オブジェクトの持つメソッドの一覧を返却するメソッドで、

<オブジェクト>.methods

という形式で呼び出すことができます。

実行するとずらずらっと色々表示されて、その中に『 + 』とか『 / 』とか『 class 』とか『 methods 』自身も確認できると思います。

演算子も確かにメソッドでした……ふーむ。


ゆとり教育の正体は?

そういえば前回、

puts 11 / 3

を実行したら

3

が出てくる、ということで文句を云ってみました。

これについては通りすがりさんが

11 / 3 は、整数同士の割り算だから、答えも整数

小数点の答えをほしい場合はどちらかを小数にする必要がある。

puts( 11.0 / 3 )

ここからへんは、数値クラスについて、マニュアルにいろいろと書いてある。

とコメントで教えてくださいました。ありがとうございます。

そんなわけで早速マニュアルを見てみましょう。

数値関連クラスの定義メソッド

ここまでで、数値関連のクラスではFixnumとFloatを確認しています。

Rubyにおける数値型は、実際にはNumericというクラスを頂点とした継承関係を築いているようです。

前回の問題は結局のところ、『Fixnumクラス(というかIntegerクラス)の / メソッドは、引数がIntegerであるとき、Integerの結果を返却する』ように定義されている、ということなのだと思います。

通りすがりさんがおっしゃっているように、どちらかのオペランドの型をFloatにすれば解決です。


レシーバ

ここまでなんの断りもなく書いてきましたが、一応確認しておきます。

オブジェクトのデータやメソッドを表す場合は、

オブジェクト.データ
オブジェクト.メソッド

という具合に、.(ドット:ピリオド)で繋いで関係を表現します。

このとき、.の左側にあるものを“レシーバ”と呼びます。

このあたりは、リファレンスマニュアルを確認すると良さそうです。

メソッド呼び出し


さらりと書いちゃってますが、意外と重要です。レシーバ。すいません。


さてさてこんな風に呼び出すメソッドですが、以前数値を10進数以外の形式で表示させた際にも、このようにしてちゃっかりメソッドを利用しました。

puts '■10進整数\n'
puts 123.to_s(16)  # 16進表記
puts -123.to_s(8)  #  8進表記
puts 0d123.to_s(2) #  2進表記

puts '■16進整数'
puts 0xffff.to_s(16)

puts '■2進整数'
puts 0b1011.to_s(2)

puts '■8進整数'
puts 0377.to_s(8)
puts 0o377.to_s(8)

ここで使っているto_sメソッドは、引数で指定した基数による文字列表現に変換するメソッドです。

このような『to_なんとか』メソッドは、Rubyでは(なんとか型に)変換を行うメソッドに付けられる名前になっています。

to_sのsはString(文字列)のsなので、文字列表現に変換、という機能になるわけですね。


同じような感じで、to_f(to Float)なんていうメソッドもあって、これを使った場合だとFloatに変換できるわけです。

ということは、

puts (11 / 3).to_f

とすればいい感じの結果が!

3.0

……でません。


既に計算してしまった結果に対して変換をかけているので、あたりまえですね。

なので、こんな風にします。

puts (11.to_f / 3)
puts (11 / 3.to_f)
puts (11.to_f / 3.to_f)

これで完璧です。要するに演算前(メソッド呼び出し前)に適切な型にしておく必要があるよ、ということです。

でもまぁ素直に浮動小数点数リテラル使えばいいと思います。

[]オブジェクトとかクラスとか

いわゆるオブジェクト指向という考え方の中に登場する、基本中の基本の言葉について少し触れました。

これって用語がわかれば良いという話ではなくて、なかなか感覚を掴むまでが苦労する気がします。


オブジェクト指向感覚を得るには、『憂鬱なプログラマのためのオブジェクト指向開発講座』がとても良いです。

解説にはC++言語を使っているのですが、C++を知らなくても、むしろついでにC++を学べてしまうくらいで理解しやすい内容だと思います。

憂鬱なプログラマのためのオブジェクト指向開発講座 (DDJ Selection)

憂鬱なプログラマのためのオブジェクト指向開発講座 (DDJ Selection)

*1:そういえば以前は自動販売機は『関数』として考えたのでした。しかし、実はあれはクラスとして考えるともっとしっくりくる、という事だったのです……

2006-11-02

[]演算子・被演算子・式

算術演算

前回は数値リテラルの使い方を学びました。

せっかく数値が扱えるようになったのですから、これを利用して何かをしたいところです。

というわけで、まずは簡単に四則演算をさせてみたいと思います。

コンピュータは『計算機』といわれるくらいですから、その程度は簡単にやってもらわないと困りますね。

というわけで、ソースはこんな感じです。

puts 1 + 2  # 足し算
puts 5 - 3  # 引き算
puts 9 * 9  # 掛け算
puts 18 / 6 # 割り算

↓実行すると……

3
2
81
3

こうなります。

掛け算の記号が『*』で、割り算の記号が『/』だということくらいに気をつければ、まったく予想通りの結果だと思います。

このときに利用した[+-*/]と言った記号を、“演算子”(operator: オペレータ)と呼びます。

特に今回使用したものは四則演算を行う演算子なので、“算術演算子”などと呼ばれることがあります。

また、演算子が作用する対象を“被演算子”(operand: オペランド)と呼びます。

例えば先ほどの例のうち、

18 / 6

における演算子は『 / 』で、オペランドは『 18と6 』になります。

さらに、この場合の演算子/はオペランドを2つ持ちます。このような演算子を“二項演算子”と呼びます。

オペランドが一つの演算子は“単項演算子”、3つだったら“三項演算子”などになります。

そして、『演算子とそれに必要なオペランドが成立した状態』を“演算子式”或いは単に“式”(expression)と呼びます。


式について、リファレンスの解説を確認してみましょう。

Ruby の式には、変数と定数、さまざまなリテラル、それらの 演算や代入、if や while などの制御構造、 メソッド呼び出し、クラス/メソッドの定義があります。

式は括弧によってグルーピングすることができます。

空の式 () は nil を返します。

Rubyの式には値を返す式と返さない式があります。

この説明の中で現在わかっているのは、

というあたりでしょうか。


今まで学んできた文字列リテラルや数値リテラル

あれらはデータとしての文字列や数値を表していると同時に、Rubyの言語構造の中では“式”と呼ばれる要素として扱われている、ということのようです。

また、メソッドの呼び出し――今まで利用した例だと、printやputsですね。これもまた“式”である、という点でリテラルと同じレベルで解釈されるようです。

そして、それらの式(の単一項)は、演算子によって組み合わせることが可能らしい、ということを知りました。


ところで、式の解説の中に気になる文があります。

Rubyの式には値を返す式と返さない式があります。

値を『返す』とはどういうことなのでしょうか。


式の評価

実は、Rubyにおける“式”という存在は、それ自体が解釈(実行・処理)された後の“値”を持ちます。

これを“式の値”と呼びます。

そして、この式の値を導き出す(ために計算やメソッドの呼び出しなどをする)ことを“式の評価”“式を評価する”と言います。

これは少々特殊な言い回しのようにも感じられますが、よく使われる言葉です。

例で考えてみます。

256

これは単に256という数値リテラルです。

しかし、先ほど学んだように、リテラルもそれ自体が式となります。式であるということ、はそれを“評価できる”ということにもなります。

実際、これを式として評価した場合、256というそのままの値を得が得られます。これがこの式の“式の値”になります。

言い回しを変えてみると、「上記の式を評価すると、256という値が返される」などと言えます。


0b100000000 + 0x100

では、このような場合はどうでしょうか。

今回は数値リテラルが2つありますね。まずは、この2つの式をそれぞれ評価します。

まず左辺(この場合、左オペランドとも)は2進整数リテラルですが、これを10進数に変換します(前回やりました)。

1 × 2^(9 - 1) → 1 × 2^8 → 1 × 256 → 256

次に右辺(右オペランド)は16進整数リテラルです。

1 × 16^(3 - 1) → 1 × 16^2 → 1 × 256 → 256

これで、両辺の式の値が得られました。

そして演算子「+」は、これら両辺の式の値に対して演算を行います。

256 + 256 → 512

これが、「0b100000000 + 0x100」という演算子式の式の値になります。

さて。この式がまたもや値を持つということは、この式に対してさらなる演算が可能だということです。

つまり、

0b100000000 + 0x100 * 0o400

という演算も可能になります。


演算子の優先順位

……と終わってしまうと、実は微妙です。

確かに上の式は演算可能なのですが、「0b100000000 + 0x100」の式の値に対する演算、ではなくなっています。

実のところ、普通の計算がそうであるように、Rubyでも乗除は加減演算よりも先に評価されるというルールがあります。

リファレンスをよく確認してみましょう。

演算子式

ここに、演算子の一覧が挙げられています。

この一覧は演算子の“優先順位”の高い順に掲載されているようです。

すなわち、演算子式の中に複数の演算子が登場するとき、各式はここに挙げられている順序で評価される、ということです。


これを踏まえて考え直してみると、先ほどの例で足し算を先に評価したい場合、

(0b100000000 + 0x100) * 0o400

このように優先したい式を括弧でくくって、優先順位を調整する必要があります。


残された気になる点

これで式のこともわかったし、取り敢えず算術演算はできるようになったので多少なりともプログラムらしい感じに近づきつつあります。

しかし、次のコードを試したときに少し不安になりました。

puts 11 / 3

これは割り切れない計算なので、答えは「3.66666...」のようになるはずです。

実際電卓で計算することこんな感じです。

f:id:mailishmaid:20061102115717j:image

ところが、Rubyに計算させると

3

とだけ返してきます。これはゆとり教育の賜物でしょうか。


.

.

.


また、リファレンスマニュアル演算子式の解説に、次のような文があります。

ほとんどの演算子は特別な形式のメソッド呼び出しですが、一部のものは言語に組み込みで、再定義できません。

え、演算子はメソッド??

突然言われると何のことやら……です。突然でなくても面食らいますが。

しかも再定義できるとかできないとか。演算子の意味を変えられるとでも言うのでしょうか。

これはもう少し別の視点で見直す必要がありそうです。



……と、そんなわけで。

次回はクラスとメソッドについてさらっと学ぶ予定です。

2006-10-30

[]リテラルその2

前回に引き続き、リテラルについてです。

今日は数値リテラルを学びます。

数値リテラル

まずは公式サイトのリファレンスを確認しましょう。

数値リテラル


なんかもう、さすがにこれを見たら十分でしょう、という気がしないでもないですが、せっかくなので表にしてみます。

今回は数値リテラルのうち、整数と浮動小数点数のみを取り上げます。

整数 10進表現 123
-123
0d123
16進表現 0xffff
2進表現 0b1011
8進表現 0377
0o377
浮動小数点数 1.23
1.2e-3

やはり一番自然なのは、整数ならば「123」という書き方ですし、小数ならば「1.23」みたいな形式ですね。

ですので、たぶん特別な理由が無い限りは最も自然に思える書き方をしておけば良いのだと思います。

一応、すべての書き方を試すコードを作って実行してみます。

puts('■10進整数\n')
puts(123)
puts(-123)
puts(0d123)

puts('■16進整数')
puts(0xffff)

puts('■2進整数')
puts(0b1011)

puts('■8進整数')
puts(0377)
puts(0o377)

puts('■浮動小数点数')
puts(1.23)
puts(1.2e-3)

↓実行結果

■10進整数\n
123
-123
123
■16進整数
65535
■2進整数
11
■8進整数
255
255
■浮動小数点数
1.23
0.0012

あれ、実行結果は全部10進数表記なんですね……

取り敢えずその辺は良いでしょう。とにかくこのような表記方法で、ソースコードの中で数値を表現することができます。

文字列リテラルのように特別な記号で修飾するなどの処理は基本的に必要ありませんので、特に難しくはないと思います。

以上です。


……と、軽く流したいところですが、そうもいきません。

このコードでは、見覚えの無い関数らしきものを利用しています。適当に流さずに、ちゃんとリファレンスを確認してみます。

puts

説明と実行結果を見比べて頂けると思うのですが、今回のコードでは出力時に"\n"を記述していません。

にも関わらず一回の出力毎に改行されているのがputsの働きだったりします。

また、putsのサンプルコードには次のように書かれています。

puts "foo", "bar\n", "baz"

いままでのものと違って、関数名の隣に小括弧がありません。

実はRubyでは、『関数(メソッド)に対する引数が明らかであるとき、小括弧を省略して記述することができる』、という特徴があります。

ですので、上のコードも次のように記述可能です。

puts '■10進整数\n'
puts 123
puts -123
puts 0d123

puts '■16進整数'
puts 0xffff

puts '■2進整数'
puts 0b1011

puts '■8進整数'
puts 0377
puts 0o377

puts '■浮動小数点数'
puts 1.23
puts 1.2e-3

すっきりしました。

人の目からみてもこれは明らかだろ、という場合には積極的に省略してしまってよさそうです。


10進数以外で表示したい!

まぁ、どうしてもそういう要求が出てくることもあると思います。

少なくとも、整数値に関してはその要求を簡単に満たす方法が用意されているようです。

詳しい内容は次回にまわすとして、実際のコードだけ示しておきます。

puts '■10進整数\n'
puts 123.to_s(16)  # 16進表記
puts -123.to_s(8)  #  8進表記
puts 0d123.to_s(2) #  2進表記

puts '■16進整数'
puts 0xffff.to_s(16)

puts '■2進整数'
puts 0b1011.to_s(2)

puts '■8進整数'
puts 0377.to_s(8)
puts 0o377.to_s(8)

↓実行結果

■10進整数\n
7b
-173
1111011
■16進整数
ffff
■2進整数
1011
■8進整数
377
377

ポイントの部分はこんな形式になっています。

<数値リテラル>.to_s(<基数>)

この基数の値を変更すれば、3進数だろうが12進数だろうが変換することが可能です。

[]基数変換とか

基数

さて、何やらさらりと“基数”という言葉が出てきています。

一般的な用語といえばそうなのですが、聞いたこともないという方も居そうなので補足します。

まずは用語辞典をひいてみましょう。

基数 【radix

数値を表現する際に、各桁の重み付けの基本となる数。我々が普段している10進数では、10倍ごとに桁が上がっていくので、基数は10である。2進数、8進数、10進数、16進数の基数はそれぞれ2、8、10、16である。

読めばなんとなくわかりますね。別段難しいことではないようです。

それでは、この基数を相互に変換することを考えてみます。


位取り記数法

基数を変換する――というだけでは少々曖昧な気もするので、その前に知っておきたいことがあります。

それが、“位取り記数法”という名称です。

位取り記数法

位取り記数法(くらいどりきすうほう)は、数の表現方法の一種で、適当な自然数 N >1を指定して N 種類の記号(数字)を用意し、それを列べることによって数を表すための規則である。

位取り記数法で指定された自然数 N をこの記数法の基数といい、基数が N であるような位取り記数法を「N 進法」「N 進記数法」という。N 進法では、N 種類の数字からなる記号列において、隣り合う上位の桁に下位の桁の N 倍の意味を持たせる位取りによって数を表現する。

とまぁ小難しくかいてありますが、実はなんのことはなくて、普段普通の人がアラビア数字を使って数値を表記している場合、この“位取り記数法”という表現方法を用いています。

要するに普通の書き方、です。そんな説明でいいのかわかりませんが。

そして、先ほど調べた“基数”についても改めて解説されています。

このあたりを踏まえると、普段我々が使っている数の表記法は『基数を10とした位取り記数法』である、ということなのだと思います。

そして、この位取り記数法のなかで基数を別の数にすること――これが、“基数変換”になります。


10進数ってなんなのか

位取り記数法を意識したうえで、慣れ親しんだ10進数について見直してみます。

たとえば、次のような数値があります。

1234

なんの変哲もありません。「この数値はなに?」と尋ねられれば、「せんにひゃくさんじゅうよん」と問題なく答えて頂けるでしょう。

実はこの「せんにひゃくさんじゅうよん」という読み方がちょっとしたポイントだったりします。

このように数字同士をくっつけて表記し、さらに「数値」は何か、と聞かれた場合に「いちにぃさんよん」と答える方は……たまにしか居ません(と思います)。


しかし、確かに数字をひとつひとつ分解すれば、単に「1と2と3と4」が順番に並んでいるに過ぎません。

にも関わらず、自然に頭の中では「これはせんにひゃくさんじゅうよんだろ……」と解釈されているはずです。

このとき、「1と2と3と4」は額面どおりの値ではありません。

「1→実は千」であり、「2→実は二百」「3→実は三十」「4→実はも何も四」という風に各数字に対して、「ある計算」が施されてしまっています。

このとき、どのようなルールに則って計算されているのかというと、

ある桁の数字の値 × {10の(桁 − 1)乗}

という計算です。

たとえば先ほどの「1234」における「2」とは、

2 × {10^(3 − 1)}
→ 2 × 100
→ 200

という計算から、実は「二百」という数値であることがわかります。

当たり前といえば当たり前すぎる話ですが、このときに掛けられる『10の(桁 − 1)乗』のことを、各桁の“重み”といいます。

これは、位取り記数法の裏に隠されている暗黙の値です。

では、なぜこの値は『10の(桁 − 1)乗』になるのでしょうか。

それはいうまでもなく10進数は10個の集まりで一桁繰り上がるから、です。

ということは、もしも

1234

が実は10進数ではなく16進数だったとしたら、各桁の値には『16の(桁 − 1)乗』が掛かることになるわけです。

というわけで実際に計算してみると、

1234(16) → 1 × {16~(4 - 1)} + 2 × {16^(3 - 1)} + 3 × {16^(2 - 1)} + 4 × {16^(1 - 1)}
         → 1 × 4096 + 2 × 256 + 3 × 16 + 4 × 1
         → 4096 + 512 + 48 + 4
         → 4660

という具合です。

そして実はこれこそが、N進数から10進数への基数変換のやり方になります。

N進数の各桁には『Nの(桁 − 1)乗』の重みがかかりますので、そこだけうまく置き換えて同じように計算するだけで10進数への変換は楽々できてしまいます。

慣れれば簡単なので、経験のない方は2進・8進・16進あたりから10進に変換する練習をしておくといつか役に立つかもしれません。少なくとも、基本情報処理などの資格取得には役立つと思いますw

検算には、Googleの電卓機能(http://www.google.com/help/calculator.html)を使ったり、[ファイル名を指定して実行]で「calc」として、関数電卓を利用すると良いですね。


10進数以外への変換は、また別の機会に書きます。


因みに、こういう話題に興味を持ったぜ、という方には『Write Great Code』という書籍が個人的にはおすすめです。

まつもとさん繋がり、というあたりも重要です。

[]感謝とおことわり

いつもこのブログをご覧下さっている皆様、本当にありがとうございます。

こんなつまらない、且つ遅々として話題の進まないブログに関心を持ってくださる稀有な方もいらっしゃるようで、光栄の極みです。

ただでさえ進行が遅いので、なるべく毎日書き進めたいと考えているのですが、実はこの先数日なかなか時間がとれない可能性があります。


何かというと自身の結婚式が次の大安に迫っているからなのですがw

そんなまったくの私事により少々更新が滞るかと思いますが、ちゃんと続ける気はありますので、どうか見捨てないであげてくださいw

以上、誠に勝手なお願いでした。

2006-10-27

[]昨日の復習

昨日はようやくRuby言語の学習を始めることができました。

まずは「Hello, world」と出力するプログラムを読み解こうとしています。

その中で、昨日学んだことを改めて書き出してみます。



と、こんなところでしょうか。

[]リテラル

消えたダブルクォーテーション

前回学習したことから、

print("Hello, world")

という文は「"Hello, world"を表示してください」という命令だということがわかりました。


ところが、このプログラムの実行結果はたしか

Hello, world

でした。

大体よさそうなのですが、「Hello, world」を囲む""(ダブルクォート:二重引用符)が消えてなくなってます。

これはいったいどういうことなのでしょうか。


文字列リテラル

実は、この""で囲まれたものは、“文字列リテラル”という存在になります。

Rubyリファレンスマニュアルの該当部分を確認してみましょう。

リテラル

文字列リテラル


リテラルとは

数字の1や文字列"hello world"のようにRubyプログラムの中に直接記述できる値の事をリテラルといいます。

ふむ。

一応、元々の言葉の意味も調べてみます。

literal

literal

【名-1】 誤植{ごしょく}、誤字{ごじ}

【名-2】 《コ》直定数{ちょく ていすう}

【形】 文字{もじ}どおりの(意味{いみ}の)、逐語的{ちくごてき}な、文字{もじ}の、融通{ゆうずう}の利かない、事実{じじつ}に忠実{ちゅうじつ}な、想像力{そうぞうりょく}に欠けた、味気{あじけ}ない、四角四面{しかく しめん}の

「文字通りの意味の」というのがそれらしい感じですね。


これはつまりどういうことなのでしょう。実例で考えてみます。

たとえば、今までの「Hello, world」ではなくて「print」と出力してみたい場合の正解は、

print("print")

こうなります。

もし、これを

print(print)

と書いてしまったらどうなるでしょうか。

書いた当人としては「print」と出力したくて書いたのですが、考えてみるとprintというのは関数(厳密にはメソッドとやら)です。

ですから、これだと「print関数(の戻り値)」を出力します、という風に読めてしまうわけですね。

それでは困るので、これは「関数の名前としてのprint」ではなく、「ただの文字の並びとしてのprint」です、ということが明示できなければいけません。それを実現するのが、“文字列リテラル”という存在なのです。


文字列リテラルのつくりかた

それでは文字列リテラルについて引用してみます。

文字列はダブルクォートまたはシングルクォートで囲まれています。ダブルクォートで囲まれた文字列ではバックスラッシュ記法 と式展開(後述)が有効になります。シングルクォートで囲まれた文字列では、\\(バックスラッシュそのもの)と \'(シングルクォート)、行末の\(改行を無視します) を除いて文字列の中身の解釈は行われません。

%記法 による別形式の文字列表現もあります。

……だ、そうです。

つまり。

  • ダブルクォートまたはシングルクォートで囲まれる
  • ダブルクォートで囲まれた場合
  • シングルクォートで囲まれた場合
    • 基本的に文字列の中身の解釈は行われない
    • ただし、\\・\'・行末の\は例外
  • ダブルクォート、シングルクォートを使わない%記法というものもある

ということで、最初の定義を最後の最後で覆してくれました。

%記法についてもそれなりに見ておきましょう。⇒%記法

いくつか並んでますが、今回見るべきは以下の3つです。

  • %!STRING! : ダブルクォート文字列
  • %Q!STRING! : 同上
  • %q!STRING! : シングルクォート文字列

さらに、

!の部分には改行を含めた任意の非英数字を使うことができます。始まりの区切り文字が括弧(`(',`[',`{',`<')である時には、終りの区切り文字は対応する括弧になります。括弧を区切り文字にした場合、対応が取れていれば区切り文字と同じ括弧を要素に含めることができます。

ということらしいです。

結局のところ、文字列リテラルを表現する方法は5通り*1あるのですね。*2

ためしに全部使ってみます。

print("北海道")
print('日本ハム')
print(%!ファイターズ!)
print(%Q+日本一+)
print(%q(おめでとう!))

↓実行結果

北海道日本ハムファイターズ日本一おめでとう!

見事な時事ネタです。


さてさて、このように書き方は5通りですが、その解釈のされかたは結局ダブルクォート系とシングルクォート系の2つです。

ではこの2つの解釈の違いを確認してみましょう。

先ほど文字列リテラルについて引用した際に確認しましたが、この2つの違いは

の2点が有効であるかどうか、という話に尽きるようです。

このうち、式展開については諸々の都合により後回しに……。

今回はバックスラッシュ記法についてのみ確認してみましょう。


バックスラッシュ記法

=>バックスラッシュ記法

\t、\n、\r……と、頭に\が付いた文字が並んでいます。

私の環境で見る限り、バックスラッシュには見えないのですが、実はこれにはワケがあります。

バックスラッシュ約物の一つで、\のような形をしている。バックスラッシュとはスラッシュ ( / ) の逆という意味である。/に比べれば、自然言語ではあまり使われることのない記号である。

ASCIIバックスラッシュ (0x5C, 5/12) はJIS X 0201では円記号であるため、日本のコンピュータや日本語のフォントOS環境ではバックスラッシュが円記号として表示されるものが多い。

ということで、元来バックスラッシュで使われていた文字コード番号が、日本の環境では円記号として使われている場合が多いのです。

そんなわけなので、ひとまずは『バックスラッシュ ⇒ 円記号』と脳内置き換えしておいてもなんとかなるような気がします。


表示の問題は良いとして、そもそもこれらはどのような役割を持つものなのでしょうか。

実はこれら(\tや\nなど)は『バックスラッシュ(円記号)+文字』で1文字分のデータを表します。そして何故頭にバックスラッシュが付いているのかというと、これによって実際の文字では表現しきれない特殊な文字を表すことができるのです。

具体的にはリファレンスマニュアルにあるとおりで、例えば、

\t
タブ(Tab)
\n
改行(New line)
\r
キャリッジリターン(復帰:carriage Return)

などなどです。

このようなものを文字列リテラル内に含めることで、特殊な文字の出力を表現できるのです。

試しにこんな風に書いてみると、

print("シ\n")
print("\tン\n")
print("\t\tジ\n")
print("\t\t\tラ\n")
print("\t\t\t\tレ\n")
print("\t\t\t\t\tナ\n")
print("\t\t\t\t\t\t〜\n")
print("\t\t\t\t\t\t\tイ")

↓実行

シ
	ン
		ジ
			ラ
				レ
					ナ
						〜
							イ

こうなります。

全部を一つの文字列にしてしまっても大丈夫。

print("シ\n\tン\n\t\tジ\n\t\t\tラ\n\t\t\t\tレ\n\t\t\t\t\tナ\n\t\t\t\t\t\t〜\n\t\t\t\t\t\t\tイ")

もちろん、%記法を使ってこんな風にしてもOKです。

print(%!シ\n!)
print(%Q!\tン\n!)
print(%!\t\tジ\n!)
print(%Q!\t\t\tラ\n!)
print(%!\t\t\t\tレ\n!)
print(%Q!\t\t\t\t\tナ\n!)
print(%!\t\t\t\t\t\t〜\n!)
print(%Q!\t\t\t\t\t\t\tイ!)

このようなバックスラッシュ記法はダブルクォート系の文字列リテラルでないと有効になりません。

ですので、

print('シ\n')
print(%q{\tン\n})
print('\t\tジ\n')
print(%q(\t\t\tラ\n))
print('\t\t\t\tレ\n')
print(%q[\t\t\t\t\tナ\n])
print('\t\t\t\t\t\t〜\n')
print(%q@\t\t\t\t\t\t\tイ@)

このようなシングルクォート系を利用したコードだと……

シ\n\tン\n\t\tジ\n\t\t\tラ\n\t\t\t\tレ\n\t\t\t\t\tナ\n\t\t\t\t\t\t〜\n\t\t\t\t\t\t\tイ

バックスラッシュもなにも、そのまま表示されます。


それにしても、ヒルマン監督おおはしゃぎですね。


バックスラッシュ記法はいくつかありますが、特殊文字については最初は\n・\t・\r・\bくらい知っておけば問題ないように思えます。

使われる文字も大体元の単語から取ったものだったりしますので、結びつけて覚えておくと良いでしょう。

もう一つ知っておかなければいけないのは、\xとして紹介されている「文字そのもの」を表す書き方です。これはいったいどのような場面で使われるのでしょうか?

たとえば次のような出力結果を得たいと考えます。

This site is 'Hatena Diary'.

このとき、

print("This site is 'Hatena Diary'.")
print('This site is 'Hatena Diary'.')

上記のようなコードを書いてしまうと……

parse error, unexpected tCONSTANT, expecting ')'
print('This site is 'Hatena Diary'.')
                           ^
warning: parenthesize argument(s) for future version
parse error, unexpected ')', expecting $
print('This site is 'Hatena Diary'.')
                                     ^

こんな感じのメッセージが出て、正しく実行できません。問題は、2行目のシングルクォーテーション版にあります。

''で囲まれた部分が文字列リテラルになるので、このコードの場合だと

print('This site is 'Hatena Diary'.')

赤字で示した部分のみが文字列リテラルとして解釈され、その間が不正な文章になってしまいます。

同じように、

print("This site is "Hatena Diary".")

というように""が入れ子になってしまっている状況でも同様の問題がおきます。

print("This site is "Hatena Diary"'.")

このような状況に対応するために、\xを利用します。

つまり、シングルクォートやダブルクォートを『文字列を定義するための特別な記号』として扱わずに、単なる文字として認識して欲しい、と明示するわけです。

print("This site is \"Hatena Diary\".")
print('This site is \'Hatena Diary\'.')

このように「ただの文字だよ」と示したい文字の前に\を付加することで、特別な意味を打ち消すことができます。


あるいはバックスラッシュを使わずに、

print(%Q!This site is "Hatena Diary".!)
print(%q!This site is 'Hatena Diary'.!)

などと%記法を使っても書けます。

引用符が入り混じるような文字列の場合は、こちらの方が便利かもしれません。


文字列リテラルの使いわけ

以上のような特徴を踏まえたうえで、実際の場面ではどの方法で文字列リテラルを定義すれば良いのでしょうか。

現在の段階では、下図のような判断基準を持ち出すことができそうです。

f:id:mailishmaid:20061027144536j:image

実のところ、取り敢えずダブルクォーテーション使ってれば何とかなる気はします。

でも、たまにはちょっと意識しておかないと忘れてしまいそうなので、気をつけましょう。


まとめと練習問題

今回は、こんなことを学びました。



問題

次の出力を行うプログラムを作成してください。

なお、各行の間には1つ空行があり、2行目の先頭にはタブが1つ含まれています。

リーグ優勝、'日本一'

	そして……

次は"アジアの頂点"へ!

記述例

続きを読む

*1:もちろん、%記法の場合は任意の非英数字が使えるのでバリエーションはいくつかありますが

*2:ヒアドキュメントはひとまず置いておきます