『プロを目指す人のためのRuby入門』を読んでのメモ(第1章〜第2章)
第1章 本書を読み進める前に
言語処理系について
言語処理とは?
- プログラミング言語で書かれたコードをコンピュータが実行できるように変換すること
- 主な方法としてインタープリターとコンパイラがある
- 現代のプログラミング言語の多くではインタープリターとコンパイラを組み合わせて使用するハイブリッドアプローチを使用している
ChatGPT
はい、その理解で概ね正しいです。プログラミングの言語処理とは、プログラミング言語で書かれたコードをコンピュータが実行できる形に変換する作業のことです。この翻訳には主に2つの方法、インタープリターとコンパイラがあります。以下にそれぞれについて詳しく説明します。
プログラミング言語処理の概要
プログラミング言語で書かれたソースコードは、人間が理解しやすい形式ですが、コンピュータが直接理解できる形式ではありません。言語処理系は、このソースコードをコンピュータが実行可能な形式(一般に機械語またはバイトコード)に変換します。この変換の方法には以下の2つがあります。
インタープリター
- 動作原理:インタープリターはソースコードを1行ずつ逐次解釈し、その場で実行します。
- 利点:プログラムの一部をすぐに実行できるため、デバッグや試行錯誤に向いています。
- 欠点:実行速度が遅くなる傾向があります。毎回コードを解釈するため、同じコードを何度も実行する場合に非効率です。
- 例:Python、Ruby(MRI)、JavaScript(ブラウザ内で動作する場合)
コンパイラ
- 動作原理:コンパイラはソースコード全体を一度に機械語または中間コード(バイトコード)に変換し、その後そのコードを実行します。
- 利点:コンパイルされたコードは直接実行されるため、実行速度が速くなります。特に、最適化された機械語にコンパイルされる場合、非常に高速です。
- 欠点:コードの変更があった場合、再度コンパイルが必要です。また、コンパイルには時間がかかることがあります。
- 例:C、C++、Rust、Go
ハイブリッドアプローチ
多くの現代のプログラミング言語は、インタープリターとコンパイラの両方のアプローチを組み合わせています。
-
バイトコードコンパイラ:一部の言語はまずソースコードをバイトコード(仮想マシンが実行する中間コード)にコンパイルし、その後インタープリターやJITコンパイラを使ってバイトコードを実行します。
- 例:Java(JVM)、Python(CPython)、Ruby(YARV)
-
JIT(Just-In-Time)コンパイラ:インタープリターとコンパイラの利点を組み合わせたアプローチで、実行時に必要な部分だけをコンパイルして実行速度を向上させます。
- 例:JavaScript(V8エンジン)、Java(HotSpot JVM)、.NET(CLR)
まとめ
- プログラミングの言語処理とは、ソースコードをコンピュータが実行できる形式に翻訳する作業です。
- 主にインタープリターとコンパイラの2つの方法があります。
- 多くの現代の言語は、インタープリターとコンパイラの両方の要素を組み合わせて使うハイブリッドアプローチを採用しています。
この理解を基に、様々なプログラミング言語がどのように実行されるかを考えると、その設計や性能特性をより深く理解することができます。
インタープリターとコンパイラとは?
1、インタープリター(interpreter,翻訳系):言語を意味を解析しながら、その意味する動作を実行する。
2、コンパイラ(compiler,通訳系):言語を他の言語に変換し、その言語のプログラムを計算機上で実行させるもの。狭い意味でコンパイラは、言語を機械語に変換し、実行するものであるが、他の言語、あるいは仮想機械コードに変換するものもコンパイラと呼ぶ。他の言語に変換するときには、特にtranslatorと呼ぶ場合もある。
引用元:言語処理系とは
Rubyの処理系は?
- 基本的にはインタープリター型・一部コンパイルを取り入れている
ChatGPT
Rubyの処理系は主にインタープリター型です。Rubyはスクリプト言語として設計されており、コードを実行時に逐次解釈して実行するインタープリターを使用しています。しかし、Rubyの実装によってはコンパイルを取り入れているものもあります。以下に詳しく説明します。
主なRubyの処理系
-
MRI (Matz's Ruby Interpreter)
- 最も一般的なRubyの実装であり、元々は完全なインタープリターとして設計されました。
- Ruby 1.9以降、MRIはYARV (Yet Another Ruby VM) というバイトコードインタープリターを導入しました。Rubyのソースコードはまずバイトコードにコンパイルされ、その後バイトコードが実行されます。
- したがって、MRIはインタープリターとコンパイラの両方の要素を持っていますが、最終的にはインタープリターとして動作します。
-
JRuby
- Java仮想マシン (JVM) 上で動作するRubyの実装です。
- JRubyはRubyのコードをJVMのバイトコードにコンパイルし、そのバイトコードを実行します。
- これにより、JRubyはJVMのJIT (Just-In-Time) コンパイラの恩恵を受けることができます。
-
Rubinius
- Rubyのコードをバイトコードにコンパイルし、そのバイトコードを仮想マシンで実行します。
- RubiniusもJITコンパイルをサポートしており、実行時にネイティブコードにコンパイルされることがあります。
-
MRuby
- 軽量で組み込み用のRuby実装です。
- MRubyはRubyのコードをバイトコードにコンパイルし、そのバイトコードをインタープリターで実行します。
まとめ
Rubyの処理系は基本的にはインタープリター型ですが、効率を向上させるためにコンパイルの要素も取り入れています。特に、Rubyのコードをバイトコードにコンパイルしてから実行することで、パフォーマンスを改善しています。また、JRubyやRubiniusのようにJITコンパイルを利用する実装もあります。したがって、Rubyの処理系はインタープリターとコンパイラのハイブリッドとも言えますが、全体としてはインタープリターとして分類されます。
- Rubyの処理系は公式のMRIが一強らしい( Rubyアソシエーション: Ruby処理系の概要)
- MRIはmacOS、Windows、Linuxなど主要なOS上で動作する
- C言語で実装されている
読んでいく上での参考資料諸々
- リファレンスマニュアル
https://docs.ruby-lang.org/ja/latest/doc/index.html - るりまサーチ
https://docs.ruby-lang.org/ja/search/ - Rubyの公式リファレンスが読めるようになる本
https://zenn.dev/jnchito/books/how-to-read-ruby-reference
使えそうなTips
- VScodeでの矩形選択・矩形編集:
command+option+shift
第2章 Rubyに関する基礎知識
p.29 複数の代入に関する注意点
=
を2回使用する場合
a = b = "goodluck"
p a #=>"goodluck"
p b #=>"goodluck"
p a.object_id #=>60
p b.object_id #=>60
a.delete!("luck")
p a #=>"good"
p b #=>"good"
- 参照先が同じになるためaに破壊的メソッドを実行するとbも変わってしまう。
- その証拠としてobject_idが共通していることが挙げられる。
ChatGPT
はい、その理解は正しいです。Rubyでは、a = b = "goodluck"
というコードは、a
とb
が同じ文字列オブジェクトを参照することになります。そのため、a
やb
のどちらかで文字列を変更すると、その変更はそのオブジェクトを参照している全ての変数に影響します。
具体的には以下のように動作します:
-
a = b = "goodluck"
は、文字列"goodluck"
を作成し、その参照をa
とb
の両方に割り当てます。 -
a.object_id
とb.object_id
は同じオブジェクトIDを持ち、これはa
とb
が同じオブジェクトを参照していることを示します。 -
a.delete!("luck")
は、a
が参照している文字列オブジェクトを直接変更します。この操作は破壊的なメソッドであるため、元のオブジェクトが変更されます。 -
a
もb
も同じオブジェクトを参照しているため、a
の変更がb
にも反映されます。
したがって、a
やb
のどちらかで文字列を変更すると、両方にその変更が反映されるのです。
多重代入を使用する場合
a,b = "goodluck", "goodluck"
p a #=>"goodluck"
p b #=>"goodluck"
p a.object_id #=>60
p b.object_id #=>80
a.delete!("luck")
p a #=>"good"
p b #=>"goodluck"
- こちらはaとbは別のオブジェクトになるので、片方の変更が他に影響しない。
参照の値渡しについて
- Rubyは値渡しの言語
- ただ、渡す値が「参照」(下記資料のメモリ番地)である場合がある。ただあくまでも「参照」の「値」を渡しているという考え方をしている。
- 値渡しと参照渡しの違いを理解する
第2章 Rubyに関する基礎知識
p.36 Rubyの真偽値
- 偽:
false
とnil
- 真:それ以外
nilとは何か?
- nil は NilClass クラスの唯一のインスタンス
- nilもオブジェクト
- nilかどうか確認するメソッドはnil?
- class NilClass (Ruby 3.3 リファレンスマニュアル)
falseとは何か?
- false は FalseClass クラスの唯一のインスタンス
- もちろんfalseもオブジェクト
- class FalseClass (Ruby 3.3 リファレンスマニュアル)
nil?
とempty?
Rubyにおける空文字確認系のメソッド→nil?
- Nilクラス
- nil?
- Objectクラス(これのおかげで他のクラスで
nil?
呼び出しても使えるのだと思われる) - Object#nil? (Ruby 3.3 リファレンスマニュアル)
empty?
- ありとあらゆるクラスにありそう(参考)ですが必要そうなものだけ
- nilにempty?するとNoMethodErrorになります
-
String#empty? (Ruby 3.3 リファレンスマニュアル)
- 文字列が空 (つまり長さ 0)の時trueになる
p " ".empty? #=>false(空の文字がある)
-
Array#empty? (Ruby 3.3 リファレンスマニュアル)
- 配列の要素が0の時trueになる
-
Hash#empty? (Ruby 3.3 リファレンスマニュアル)
- ハッシュが空の時trueになる
blank?
とpresent?
Rubyにおける空文字確認系のメソッド→-
blank?
- nil, “”, “ “(半角スペースのみ), 空の配列, 空のハッシュのときにfalseを返します
-
present?
- !blank? を実行するメソッド。il, “”, “ “(半角スペースのみ), 空の配列, 空のハッシュのときにfalseを返す。
- 参考
第2章 Rubyに関する基礎知識
p.38 演算式の優先順位・再定義
- カッコ>かつ(&&)>または(||)
- 演算子式 (Ruby 3.3 リファレンスマニュアル)
- 再定義できない演算子は下記の通り
= ?: .. ... not && and || or ::