1. ホーム
  2. スクリプト・コラム
  3. ルビートピックス

Rubyの文法と言語機能のまとめ

2022-02-07 02:26:01

Rubyはインタプリタ型のオブジェクト指向動的型付け言語で、柔軟性と実行時の安全性のバランスを取るという戦略を取っている。2006年頃、Railsというフレームワークの登場により、Rubyは一躍人気者になり、人々をプログラミングの楽しさへと導いた。Rubyは実行速度の面ではあまり効率的ではありませんが、プログラマーがより効率的にプログラミングを行うことができるようになりました。この記事では、Rubyの基本的な構文、コードブロックやクラスの定義など、Rubyの基本的な言語機能を紹介します。

1. 基礎知識
Rubyの対話型コマンドラインで次のコマンドを入力します(>>はコマンドラインプロンプト、=>は戻り値、=>の記号と文はその戻り値を示すために下に一行で記述されます)。

>> puts 'hello, world'
hello, world
=> nil

>> language = 'Ruby'
=> "Ruby"

>> puts "hello, #{language}"
hello, Ruby
=> nil



上記のコードでは、puts出力を使用し、変数に値を代入し、#{}構文で文字列置換を実装しています。これは、Rubyが解釈可能であること、変数は宣言なしに直接初期化および代入できること、Rubyコードのすべての部分が何らかの値を返すこと、一重引用符で囲まれた文字列は直接解釈されること、二重引用符で囲まれた文字列は文字列置換を引き起こすこと、を示しています。

1.1 プログラミングモデル

Rubyは純粋にオブジェクト指向の言語です。Rubyではすべてがオブジェクトで、オブジェクトのメソッドを"."で呼び出すことができます。オブジェクトの型とクラスとメソッドメソッドがサポートしているメソッドを確認できます。例えば4.class => Fixnum, 7.methods => ["inspect", "%", "<& quot;, "numerator", ...] などです。, false.class => FalseClass (角括弧は配列).

1.2 プロセス制御

条件判断には、通常のブロック形式とストレートな1行形式があり、一般的なif文のほかにunless文(if notと同等だが、より読みやすい)もある。同様に、ループも通常のブロック形式と一行形式の両方が用意されている。注:nilとfalse以外の値はすべてtrueを表し、0も含まれる!

# Block form
if x == 4
 puts 'This is 4.'
end
# Single line form
puts 'This is false.' unless true
x = x + 1 while x < 10 # The result of x is 10
x = x - 1 until x == 1 # The result of x is 1


他のC系言語と同様に、Rubyの論理演算子and (&&) とor (||) には短絡機能が付いており、式全体を実行したい場合は & や | を使用することができます。

1.3 ダックタイプ

4 + 'four'を実行するとTypeErrorエラーが発生します。これはRubyが強い型付け言語であり、型の衝突があった場合にエラーが発生することを表しています。def. . end 関数の定義に文を入れると、関数が呼ばれたときにだけエラーが報告されます。これは、Rubyがコンパイル時ではなく実行時に型チェックを行うことを示しており、これを動的型付けと呼びます。

a = ['100', 100.0]
puts a[0].to_i # => 100
puts a[1].to_i # => 100


これはダックタイピングと呼ばれるものです。配列の最初の要素はString型、2番目の要素はFloat型ですが、整数への変換はto_iです。ダックタイピングでは、オブジェクトがアヒルのように歩き、アヒルのように鳴けば、内部の型はどうであれ、それはアヒルと呼ばれます。オブジェクト指向の設計思想には、「実装ではなくインターフェイスをコーディングせよ」という重要な原則があります。この原則を実現するために、アヒル型を利用すれば、ほとんど余分な作業をすることなく、簡単に実装することができるのです。

1.4 関数

def tell_the_truth
 true
end


各関数は結果を返し、戻り値が明示的に指定されていない場合は、関数を終了する前に最後に処理した式の値を返します。また、関数は他の関数に引数として渡すことができるオブジェクトでもあります。

1.5 配列

Pythonと同様にRubyの配列は括弧で定義されます。例えば animals = ['lion', 'tiger', 'bear']; 負の添え字は逆数の要素を返します。例えば animals[-1] => "bear"; Rangeオブジェクトを指定して、例えば animals[1..2] => ['tiger', 'bear'] など、ゾーン毎の要素を取得することも可能です。さらに、配列の要素は互いに異なるものにすることができ、複数の配列は配列の配列に他なりません。配列には、キュー、チェーン、スタック、コレクションなどを実装するための非常に豊富なAPIが用意されています。

1.6 ハッシュテーブル

numbers = {2 => 'two', 5 => 'five'}
stuff = {:array => [1, 2, 3], :string => 'Hi, mom!}
# stuff[:string] => "Hi, mom!"

ハッシュリストはどんなタイプのキーでも取れますが、上のコードのもののキーはもっと特殊で、前にコロン識別子をつけたシンボルです。例えば、同じ値の2つの文字列は物理的に異なりますが、同じシンボルは同じ物理的なオブジェクトです。これは、'i am string'.object_id と :symbol.object_id を繰り返し呼び出すことで分かります。また、tell_the_truth :profession => :lawyer のようにハッシュテーブルを関数の最後の引数として使用する場合、中括弧は省略可能です。

2.オブジェクト指向
2.1 コードブロック

コードブロックは名前のない関数(無名関数)であり、関数の引数として渡すことができます。コードブロックは、1行だけの場合は中括弧で、複数行に渡る場合は do/end で囲まれており、複数の引数を取ることができます。

3.times {puts 'hehe'} # Output 3 lines of hehe
['lion', 'tiger', 'bear'].each {|animal| puts animal} # Output the contents of the list


上記の回数は、実際には Fixnum 型のメソッドであり、このようなメソッドを自分で実装するのは非常に簡単です:。

class Fixnum
 def my_times
  i = self
   while i > 0
    i = i - 1
    yield
  end
 end
end


3.my_times {puts 'hehe'} # Output 3 lines of hehe


このコードでは、既存のクラスを開き、カスタムメソッドmy_timesを追加し、yieldでコードブロックを呼び出しています。Rubyでは、コードブロックはループだけでなく、ブロック内の動作が関連するyieldが呼ばれたときにのみ実行される遅延実行にも使うことができます。コードブロックは、ファイルの最小行からコレクションに対するあらゆる種類の複雑な操作の実行まで、Rubyのさまざまなライブラリに浸透しています。

2.2 クラス

オブジェクトのclassメソッドを呼び出すとその型がわかり、superclassを呼び出すとその型の親クラスがわかるようになっています。次の図は、数字の継承の連鎖を示しています。水平の矢印は、右側が左側でインスタンス化されたオブジェクトであり、垂直の矢印は、下側が上側から継承していることを示しています。Rubyのすべてのものは、共通の祖先であるObjectを持っています。

最後に、Rubyのクラスがどのように定義され、使用されているかを見るための完全な例 - ツリーの定義 - と、すべての注意すべき点がコメントに書かれています。

class Tree
 # Define instance variables, using either the attr or attr_accessor keyword, the former defining variables and accessing them with the same name getter method (i.e. read-only), the latter defining variables with more setter methods with the same name (note the use of notation here)
 attr_accessor :children, :node_name

 # Construct method (constructor must be named initialize)
 def initialize(name, children=[])
  @node_name = name
  @children = children
 end

 # Iterate over all nodes and execute the block, note that the argument is preceded by a & to pass the block to the function as a closure
 def visit_all(&block)
  visit &block
  children.each {|c| c.visit_all &block}
 end

 # Visit a node and execute the code block block
 def visit(&block)
  block.call self
 end
end

ruby_tree = Tree.new("Ruby", 
 [Tree.new("Reia"),
  Tree.new("MacRuby")])
# Visit a node
ruby_tree.visit {|node| puts node.node_name}
# Visit the whole tree
ruby_tree.visit_all {|node| puts "Node: #{node.node_name}"}



もう一つ、Rubyの命名規則について触れておきます。

(1) クラスはキャメルケースの命名法を使用する
(2) インスタンス変数(オブジェクトが値を持つ)の前には@を、クラス変数(クラスが値を持つ)の前には@@を付けなければならない。
(3) 変数名、メソッド名はすべて小文字、アンダースコア命名法、例:underscore_style
(4) 定数名は全角のアンダースコア、例:ALL_CAPS_STYLE
(5) 論理テストに使用する関数やメソッドは、一般に疑問符を付けて、if test?

3. モジュールとミキシン(Mixin)
オブジェクト指向言語では、類似したオブジェクトに動作を伝播させるために継承を使用します。あるオブジェクトが複数の振る舞いを継承するような場合、C++のように多重継承を用いるのが一つの方法である。Javaではこの問題を解決するためにインターフェースを用い、Rubyではモジュール Mixin を用いる。モジュールは関数や定数のコレクションで、モジュールをクラスに含めると、そのモジュールの振る舞いや定数もクラスの一部となる。

# Define the module ToFile
module ToFile
 # Get the name of the file
 def filename
  "object_name.txt"
 end

 # Create a file
 def to_f
  File.open(filename, 'w') {|f| f.write(to_s)} # Note that to_s is defined elsewhere here!
 end
end

# Define the user class
class Person
 include ToFile
 attr_accessor :name

 def initialize(name)
  @name = name
 end

 def to_s
  name
 end
end

Person.new('matz').to_f # Creates a file object_name.txt containing the contents matz



上記のコードは、1つのことを除いてよく理解できます。to_sはモジュールで使われ、クラスで実装されますが、それを実装するクラスは、モジュールが定義された時点ではまだ定義すらされていないのです。これがダックタイプの本質である。ファイルに書き込む機能はクラスとしてのPersonとは関係なく(クラスは所属するものをやればいい)、実際の開発ではPersonクラスをファイルに書き込むという追加機能が必要であり、mixinはその要件を簡単に満たすことができるのです。

Ruby には enumerable と comparable という二つの重要な mixin があります。クラスを列挙可能にするには each メソッドを実装し、クラスを比較可能にするには <=> (スペースシップ)演算子(2つのオペランド a,b を比較して 1, 0, -1 を返す)を実装しなければなりません。'begin' <=> 'end => -1。配列は、このようなことを行うためのいくつかの良い方法を持っています。

a = [5, 3, 4, 1]
a.sort => [1, 3, 4, 5] # The integers have been implemented with the Fixnum class as spacecraft operators, so they are comparable and sortable
a.any? {|i| i > 4} => true
a.all? {|i| i > 0} => true
a.select {|i| i * 2} => [10, 6, 8, 2]
a.select {|i| i % 2 == 0} => [4]
a.member?(2) => false
a.inject {|product, i| product * i} => 60 # The first argument is the result of the last execution of the block, if no initial value is set, the first value of the list is used as the initial value



4.メタプログラミング(メタプログミング)
メタプログラミングとは、簡単に言うと "プログラムを書くプログラムを書く" ということですが、次の例で説明するように、ちょっと口が悪いです。

4.1 オープンクラス

Rubyでは、Class.newメソッドを再定義するなど、Rubyを完全に麻痺させてしまうほど、任意のクラスを再定義し、拡張したいメソッドを与えることが可能です。開発クラスの場合、このトレードオフは自由度を考慮することがほとんどで、どんなクラスやオブジェクトでも再定義できる自由があることで、わかりやすいコードを書くことができますが、自由と力がある分、負う責任も大きいことを理解することも重要です。

class Numeric
 def inches
  self
 def
 def feet
  self * 12.inches
 end
 def miles
  self * 5280.feet
 end
 def back
  self * -1
 back
 def forward
  self
 end
end


Numericクラスを開くと、上記のコードは、次のような最も簡単な構文を使ってインチで実装できます:pts 10.miles.back, pts 2.feet.forward.

4.2 method_missingの使用

Ruby はメソッドが見つからないとき、特別なコールバック・メソッド method_missing を呼び出して、診断メッセージを表示します。この特別なメソッドをオーバーライドすることで、いくつかの非常に興味深く強力な機能を実装することができます。次の例は、きれいな構文でローマ数字を実装する方法を示しています。

class Roman
 # Override self.method_missing method
 def self.method_missing name, *args
  roman = name.to_s
  roman.gsub!("IV", "IIII")
  roman.gsub!("IX", "VIIII")
  roman.gsub!("XL", "XXXX")
  roman.gsub!("XC", "LXXXX")

  (roman.count("I") +
   roman.count("V") * 5 +
   roman.count("X") * 10 +
   roman.count("L") * 50 +
   roman.count("C") * 100)
 end
end

puts Roman.III # => 3
puts Roman.XII # => 12



ローマ字クラスの実際のメソッドは定義していませんが、ローマ字クラスであらゆるローマ字を表現することはすでに可能なのです! 原則は、定義されたメソッドが見つからないときにメソッド名と引数をmethod_missingに渡して実行することです。まず to_s を呼んでメソッド名を文字列に変換し、次にローマ数字を "left minus" という特殊な形から "right plus" という形に変換し(数えやすい)、最後に記号の数を数えて重み付けをします。

もちろん、このような強力なツールには代償があります。Rubyではメソッドが見つからないことを教えてくれなくなったので、クラスのデバッグが非常に難しくなっています。つまり、method_missingは諸刃の剣なのです。構文はずっとシンプルになりますが、それはプログラムの堅牢性を人為的に高める場合のみです。

4.3 モジュールの使用

Rubyのメタプログラミングの中で最もよく使われているのがモジュールです。以下のコードは、csvファイルを読み込むクラスをモジュール方式で拡張する方法を説明します。

module ActsAsCsv

 # Whenever a module is included by another module, the included method of the included module will be called
 def self.include(base)
  base.extend ClassMethods
 end

 module ClassMethods
  def acts_as_csv
   include InstanceMethods
  end
 end

 module InstanceMethods
  attr_accessor :headers, :csv_contents

  def initialize
   read
  end

  def read
   @csv_contents = []
   filename = self.class.to_s.downcase + '.txt'
   file = File.new(filename)
   @headers = file.gets.chomp.split(', ') # String's chomp method removes the carriage return at the end of the string
   file.each do |row|
    @csv_contents << row.chomp.split(', ')
   end
  end
 end

end # end of module ActsAsCsv

class RubyCsv # No inheritance, feel free to add
 include ActsAsCsv
 acts_as_csv
end

m = RubyCsv.new
puts m.headers.inspect
puts m.csv_contents.inspect



上記のコードでは、RubyCsv に ActsAsCsv が含まれているので、ActsAsCsv の included メソッドでは base は RubyCsv を指し、ActsAsCsv モジュールは RubyCsv クラスに唯一のクラスメソッド acts_as_csv を追加し、さらに RubyCsv クラスが開かれてクラス内のすべてのインスタンスメソッドが含まれることになります。このようにして、プログラムを書く(モジュールを介してクラスメソッドを動的に追加する)プログラムが書かれている。

BuilderやActiveRecordなど、Rubyの優れたフレームワークの中には、特にメタプログラミングに依存して可読性を高めているものがあります。メタプログラミングの力を使えば、正しいRubyの文法と日常的な使用との間の距離を最小にすることが可能です。すべてはコードの読みやすさを向上させるために行われていることに注意してください。

5. 要約
Rubyの純粋なオブジェクト指向は、一貫した方法でオブジェクトを扱うことを可能にします。Rubyのモジュールとオープンクラスは、クラスで定義される伝統的なメソッドやインスタンス変数をはるかに超えて、プログラマが動作を構文に緊密に統合することを可能にします。
コアベネフィット
(1) 優雅な構文と強力な柔軟性
(2)スクリプティング Rubyの構文上の工夫は生産性を飛躍的に向上させますし、多種多様なライブラリやgem(Rubyパッケージ)が用意されているので、日常的に必要なものはほとんど揃っています。
(3) ウェブ開発。Ruby on Railsというフレームワークを使ったWeb開発のために、最終的にRubyを学ぶ人は多い。TwitterはもともとRubyで実装されており、Rubyの圧倒的な生産性により、市場に出せるプロダクトを短期間で開発することが可能である。
弱点
(1)パフォーマンス これはRubyの最大の弱点です。時間が経つにつれて、Rubyは本当にどんどん速くなっています。もちろん、Rubyはプログラマーの体験を向上させるために作られたものであり、高いパフォーマンスを必要としないアプリケーションシナリオでは、生産性の大幅な向上と引き換えにパフォーマンスを上げることは、確かに価値があることだと思います。
(2) 並行処理とオブジェクト指向プログラミング。オブジェクト指向は、一連の振る舞いを包む状態を基本としているが、通常は状態が変化する。このプログラミング戦略は、プログラム中に並行性がある場合に深刻な問題を引き起こす。
(3)型安全性。Rubyのような動的型付け言語では、IDEを実装することは非常に困難です。