| ウィキペディアにRubyの記事があります。 |
本書は、Rubyのチュートリアルです。Rubyは、比較的習得が容易なオブジェクト指向スクリプティング言語です。まつもとゆきひろによって開発されました。Ruby on Railsの記述言語として選ばれたことで有名になりましたが、ウェブアプリケーション以外にもシステム管理やネットワークアプリケーションなどさまざまな用途に用いられます。
Rubyには、強力で多用途な言語とするための多くの特徴があります。その特徴とは以下の通りです:
Rubyは強力で汎用性の高い言語であり、さまざまなタスクに適しています。Rubyの学習と使用は簡単で、開発者の大規模で活発なコミュニティがあります。強力で汎用性が高く、習得が容易な言語をお探しなら、Rubyは良い選択です。
Rubyを利用するためには、公式ウェブサイトからインストーラーをダウンロードしてインストールします。
以下は、主要なプラットフォームへのインストール手順の概要です:
sudoapt-getupdatesudoapt-getinstallruby
RubyでのHello Worldプログラムは非常に簡単です。以下は、コンソールに"Hello, World!"と出力するプログラムです:
puts"Hello, World!"
これをhello.rbという名前で保存し、ターミナルでruby hello.rbと実行すると、"Hello, World!"が表示されます。
$rubyhello.rbHello,World!$
Rubyでは、様々なデータ型が利用できます。文字列、数値、配列、ハッシュなどがその代表的なものです。動的型付け言語であるRubyでは、変数の型宣言が不要です。以下に、主要なデータ型と変数の宣言と代入について詳細を示します。
42のように表され、浮動小数点数は例えば3.14のように表されます。"Hello"や'Ruby'などが文字列です。[ ] で囲まれ、コンマで区切ります。例えば、[1, 2, 3]や['a', 'b', 'c']などが配列です。{ } で囲まれ、キーと値はコロン: で区切ります。例えば、{'name' => 'Alice', 'age' => 30}などがハッシュです。変数は代入演算子= を使用して宣言および初期化されます。変数名はアルファベットまたはアンダースコア_ で始まり、その後にはアルファベット、数字、アンダースコアを組み合わせることができます。型宣言は不要であり、変数は動的に型が決まります。
以下は、変数の宣言と代入の例です。
name="Alice"# 文字列型の変数age=30# 整数型の変数scores=[85,90,75,100]# 配列型の変数person={'name'=>'Bob','age'=>25}# ハッシュ型の変数
これらの例では、それぞれの変数に適切なデータ型の値が代入されています。
Rubyには様々な制御構造が用意されており、以下のようなものがあります。
if/elsif/elseunless/elseif notと同等の意味case/when/elsecase/in/elsewhileuntilfor/inloopbreak/nextbegin/rescue/else/ensureraisetimes/upto/downtotapyieldRubyの制御構造はPerlやBashなどとよく似ていますが、独自の構文も存在します。また、Rubyはブロックの概念があり、それを活用した制御構造があるのが特徴です。
# 条件分岐name='Alice'ifname=='Alice'puts'こんにちは、Aliceさん'elsifname=='Bob'puts'こんにちは、Bobさん'elseputs"あなたの名前は#{name}ですね"end# ifは式putsifname=='Alice''こんにちは、Aliceさん'elsifname=='Bob''こんにちは、Bobさん'else"あなたの名前は#{name}ですね"end# unlessfile_exists=falseputs'ファイルが存在しません'unlessfile_exists# case/whenage=25caseagewhen0...18puts'未成年者です'when18...65puts'働き盛りの年齢ですね'elseputs'シニア世代かもしれません'end# パターンマッチングputscase[2,3,5]in0,1,2'最初の整数'in0,2,4'最初の偶数'in1,3,5'最初の奇数'in2,3,5=>n"最初の素数、三番目は#{n}"else'どの数列でもない'end# 出力: "最初の素数、三番目は5"# 繰り返し# whilei=0whilei<5puts"i =#{i}"i+=1end# untilj=0untilj>5puts"j =#{j}"j+=1end# for/inarray=[10,20,30,40]foriteminarrayputs"要素は#{item}"end# loopcount=1loopdoputs"回数は#{count}"count+=1breakifcount>5end# 繰り返し制御# break/nextnumbers=[1,2,3,4,5]numbers.eachdo|n|nextifn.even?puts"奇数は#{n}"breakifn==5end# 例外処理# begin/rescue/else/ensurebeginresult=10/0rescueZeroDivisionError=>eputs"0で割ろうとしました:#{e.message}"elseputs"結果は#{result}"ensureputs'計算処理が完了しました'end# each[2,3,5,7,11].each{|i|putsi}# times/upto/downto5.times{puts'Hello'}1.upto(5){|i|puts"uptoの値は#{i}"}5.downto(1){|i|puts"downtoの値は#{i}"}# tapstring='Hello'result=string.tap{|s|puts"tap内:#{s}"}puts"tapの結果:#{result}"# yielddefgreetyield('Morning')yield('Evening')endgreet{|time|puts"Good#{time}!"}
Rubyには条件実行を簡潔に記述するためのいくつかのイディオムがあります。
条件式?真の場合の値:偽の場合の値
allowed=user.admin??"Full access":"Read only"
name=params[:name]||"Guest"# nameがnilまたは空文字列の場合、"Guest"が代入される
obj&&=obj.method# objがnilやfalseでない場合のみ、obj.methodが評価される
name=params.dig(:user,:name)&.strip# params[:user]または params[:user][:name]がnilの場合にnilを返す
これらのイディオムを適切に使うことで、条件実行を簡潔で読みやすく記述できます。
Rubyには反復実行を簡潔に記述するためのいくつかのイディオムがあります。
[1,2,3].eachdo|n|putsnend
(1..5).map{|n|n**2}# => [1, 4, 9, 16, 25][1,2,3].select(&:even?)# => [2]
5.times{puts"Ruby!"}# "Ruby!"が5回出力される
loopdocmd=gets.chompbreakifcmd=="exit"# 処理end
[1,2,3].each{|n|putsn}fornin[1,2,3]doputsnend
ary=[1,2,3]i=0whilei<ary.lengthputsary[i]i+=1end
["a","b","c"].each_with_index{|item,index|puts"#{index}:#{item}"}
["a","b","c"].with_index{|item,index|puts"#{index}:#{item}"}
Rubyの反復構文はループだけでなく、ブロックやイテレータによる関数型のスタイルの記述も可能です。状況に応じて適切な方法を選ぶことで、簡潔でわかりやすいコードを書くことができます。
条件分岐は、プログラムの中で特定の条件が満たされた場合に異なる処理を実行するための制御構造です。Rubyには、if-elsif-else-end 構文、case-when-else-end 構文があります。
if、elsif、elseキーワードを使用して条件分岐を記述します。以下はその基本的な構文です:
if条件式# 条件が真の場合の処理elsif別の条件式# 条件が真の場合の処理else# どの条件も満たされない場合の処理end
例えば、次のように使用します:
age=20ifage>=20puts"You are an adult."elseputs"You are not an adult yet."end
この場合、ageが20以上の場合は"You are an adult."が出力され、それ以外の場合は"You are not an adult yet."が出力されます。
Rubyのcase-when-else-end文は、条件に応じて異なる処理を行うための制御構造です。
以下に、基本的な構文と使い方を解説します。
case式when条件1# 式が条件1に合致したときに実行されるコードwhen条件2,条件3# 式が条件2または条件3に合致したときに実行されるコードelse# どの条件にも合致しない場合に実行されるコードend
caseの後に評価したい式を置きます。その式の評価結果と、when節の各条件を比較し、合致する場合に対応するブロックが実行されます。else節は、どの条件にも合致しない場合のデフォルトの処理を提供します。
例えば、以下はcase-when-else-end文の使用例です。
grade='B'putscasegradewhen'A'then'Excellent!'when'B'then'Good job!'when'C'then'You passed, but you could do better.'else'Sorry, you failed.'end#=> Good job!
この例では、変数gradeの値に応じて異なるメッセージが出力されます。gradeが'A'の場合は"Excellent!"が出力され、'B'の場合は"Good job!"が出力されます。gradeが'C'の場合は"You passed, but you could do better."が出力され、どの条件にも一致しない場合は"Sorry, you failed."が出力されます。
case-when-else-end文は、複数の条件をテストする必要がある場合や、条件に基づいてさまざまな処理を行う必要がある場合に便利です。
パターンマッチングは、プログラムの中で特定のパターンに処理を実行するための制御構造です。Rubyには、case-in-else-end 構文と=>演算子とin演算子があります。
Rubyのパターンマッチング(Pattern Matching)は、構造化された値の深いマッチングを可能にする機能です。これにより、構造をチェックし、一致した部分をローカル変数にバインドすることができます。
Rubyのパターンマッチングは、case/in式を使用して実装されています。
以下に、その基本的な構文と使い方を示します。
case式inパターン1# 式がパターン1に合致したときに実行されるコードinパターン2# 式がパターン2に合致したときに実行されるコードelse# どのパターンにも合致しない場合に実行されるコードend
case式の中のinブロックは、パターンと一致するかどうかをチェックし、マッチした場合に対応するコードを実行します。else節は、どのパターンにも一致しない場合のデフォルトの処理を提供します。
以下は、パターンマッチングの使用例です。
putscase[2,3,5]in0,1,2'最初の整数'in0,2,4'最初の偶数'in1,3,5'最初の奇数'in2,3,5=>n"最初の素数、三番目は#{n}"else'どの数列でもない'end# 出力: "最初の素数、三番目は5"
このコードは、パターンマッチングを利用して、与えられた配列の最初の要素に応じて異なる処理を行います。与えられた配列[2, 3, 5] の最初の要素が特定のパターンに一致するかどうかをテストし、一致する場合に対応するメッセージを出力します。
具体的には、与えられた配列[2, 3, 5] の最初の要素が各条件に一致するかどうかをテストします。
[2, 3, 5] は0, 1, 2 に一致せず、次の条件へ進みます。[2, 3, 5] は0, 2, 4 に一致せず、次の条件へ進みます。[2, 3, 5] は1, 3, 5 に一致せず、次の条件へ進みます。[2, 3, 5] は2, 3, 5 => n に一致し、n に7 が割り当てられます。最終的に"最初の素数、三番目は7" というメッセージが出力されます。
else 節は、どの条件にも一致しない場合に実行されますが、この場合、どの数列でもないことを示すメッセージが出力されることになります。
=>演算子とin演算子 は、パターンマッチングを簡潔に行う方法の1つです。通常、パターンマッチングは複数の行にわたるケース式や条件分岐を使用しますが、=>演算子とin演算子 は1行の式でパターンをマッチングする手法です。
具体的には、次のような形式で表現されます:
式=>パターン
あるいは
式inパターン
この形式では、式 の値がパターン にマッチするかどうかを評価します。=>演算子とin演算子 でパターンマッチングに失敗すると、NoMatchingPatternKeyError例外が raise されます。
{a:1,b:2,c:3}=>{b:n}putsn# 2{a:1,b:2,c:3}=>{x:n}# {:a=>1, :b=>2, :c=>3}: key not found: :x (NoMatchingPatternKeyError)
パターンマッチングにはさまざまなパターンがあります:
これらのパターンを組み合わせて、複雑なデータ構造をマッチングできます。また、パターンマッチングではマッチした部分をローカル変数にバインドすることもできます。
Rubyにおける反復(ループ)は、いくつかの方法で行うことができます。主な方法は以下の通りです。
配列やハッシュなどのコレクションに対して使われる最も一般的な反復方法です。以下はその例です。
array=[1,2,3,4,5]array.eachdo|item|putsitemend
for文も使用できますが、Rubyではあまり一般的ではありません。
foriin1..5putsiend
while文を使って条件が満たされている間、ブロック内のコードを実行します。
x=0whilex<5doputsxx+=1end
until文は条件が満たされるまでループします。
x=0untilx==5doputsxx+=1end
固定回数のループを行う場合に便利です。
5.timesdo|i|putsiend
これらの方法を組み合わせて、さまざまな反復のニーズに対応できます。ただし、Rubyではeachメソッドが最も一般的で、他の方法よりも好まれることが多いです。
メソッドは、Rubyプログラムで特定の処理を実行するための手段を提供します。メソッドは、関数や手続きと同様の機能を持ち、再利用可能なコードブロックを定義します。Rubyでは、メソッドはオブジェクト指向プログラミングの基本的な概念であり、ほとんどの処理がメソッドとして定義されます。
メソッドは以下のような形式で定義されます:
defメソッド名(引数1,引数2,...)# メソッドの処理end
ここで、defキーワードを使ってメソッドを定義し、メソッド名を指定します。メソッドが引数を取る場合は、引数のリストを指定します。メソッドの本体は、endキーワードで終了します。
以下は、簡単なメソッドの例です:
defgreet(name)puts"Hello,#{name}!"endgreet("Alice")
この例では、greetという名前のメソッドが定義されています。このメソッドはnameという引数を取り、それを使用して挨拶を表示します。greet("Alice")という呼び出しでは、"Hello, Alice!"というメッセージが表示されます。
endキーワードを伴わず、代入に似た形式でも定義できます:
defメソッド名(引数1,引数2,...)=式
ここで、defキーワードと=を使ってメソッドを定義し、メソッド名を指定します。メソッドが引数を取る場合は、引数のリストを指定します。
以下は、簡単なメソッドの例です:
defgreet(name)=puts"Hello,#{name}!"greet("Alice")
また、Rubyではメソッド内で最後に評価された式が自動的にメソッドの戻り値となります。明示的なreturn文は省略することができますが、必要に応じて使用することもできます。
さらに、メソッドはオプションのブロックを受け取ることができます。ブロックを受け取るメソッドは、yieldキーワードを使ってブロックを実行します。
defgreet(name)puts"Hello,#{name}!"yieldifblock_given?end
このメソッドでは、引数として受け取ったnameに対して挨拶を出力し、その後にyieldを使ってブロックを実行しています。block_given?メソッドは、メソッドがブロックを受け取ったかどうかを判定します。
メソッドを呼び出す際には、通常の引数と一緒にブロックを渡すことができます。
greet("Alice")doputs"Nice to meet you!"end
このコードは、以下を出力します。
Hello, Alice!Nice to meet you!
配列は複数の要素を格納するためのデータ構造であり、それぞれの要素は順序付けされています。Rubyでは、配列は角かっこ[ ] で囲まれ、コンマで区切られた要素のリストとして表現されます。
<<演算子やpushメソッドを使用して要素を配列に追加することができます。fruits=['apple','banana','orange']fruits<<'grape'fruits.push('kiwi')
delete_atメソッドやpopメソッドを使用して配列から要素を削除することができます。fruits.delete_at(0)# インデックス0の要素を削除fruits.pop# 末尾の要素を削除
putsfruits[0]# => "banana"
ハッシュは、キーと値のペアを格納するデータ構造であり、各要素はキーを使用して参照されます。Rubyでは、波かっこ{ } を使用してハッシュを定義します。
= を使用します。person={'name'=>'Alice','age'=>30}person['gender']='female'
deleteメソッドを使用して、指定したキーとそれに対応する値を削除することができます。person.delete('age')
putsperson['name']# => "Alice"
クラスは、オブジェクトの設計図として機能し、それぞれのオブジェクトはそのクラスのインスタンスです。Rubyでは、classキーワードを使用してクラスを定義します。
classPersondefinitialize(name,age)@name=name@age=ageenddefintroduceputs"My name is#{@name} and I'm#{@age} years old."endend
この例では、Personという名前のクラスが定義されています。initializeメソッドは、新しいインスタンスが作成されるときに呼び出され、インスタンス変数@name と@age に値を設定します。introduceメソッドは、そのインスタンスの情報を出力します。
alice=Person.new("Alice",30)bob=Person.new("Bob",25)
これにより、Personクラスの新しいインスタンスalice とbob が作成されます。それぞれのインスタンスは、initializeメソッドによって設定された名前と年齢を持ちます。
alice.introduce# => "My name is Alice and I'm 30 years old."bob.introduce# => "My name is Bob and I'm 25 years old."
各インスタンスは、クラス内で定義されたメソッドを呼び出すことができます。
オブジェクト指向プログラミング(OOP)は、プログラムをオブジェクトとそれらの相互作用に基づいて構造化する方法です。OOPには、次のような重要な概念があります:
これらの概念を使用することで、コードをより柔軟で保守しやすくすることができます。
例外処理は、予期しないエラーが発生した場合にプログラムの実行を中断せずに制御を継続するための仕組みです。Rubyでは、begin、rescue、endキーワードを使用して例外処理を実装します。
begin# 例外が発生する可能性のあるコードresult=10/0rescueZeroDivisionError=>e# 例外が発生した場合の処理puts"Error occurred:#{e.message}"end
上記の例では、beginブロック内でゼロ除算エラーが発生する可能性があります。rescueブロックでは、特定の例外(ここではZeroDivisionError)が発生した場合の処理を指定します。例外が発生した場合、その例外オブジェクトがrescueブロックの変数eに割り当てられ、messageメソッドを使ってエラーメッセージを取得します。
ファイルの読み書きは、プログラムでよく行われる操作の1つです。Rubyでは、Fileクラスを使用してファイルの読み書きを行います。
# ファイルの書き込みFile.open("example.txt","w")do|file|file.write("Hello, World!")end# ファイルの読み込みFile.open("example.txt","r")do|file|content=file.readputscontent# => "Hello, World!"end
ファイルポインタを移動して、ファイル内の特定の位置から読み書きを行うこともできます。
# ファイルのオープンFile.open("example.txt","r")do|file|# ファイルポインタの移動file.seek(7)# 指定位置からの読み込みcontent=file.readputscontent# => "World!"}
モジュールは、関連するメソッドや定数をまとめるための仕組みであり、クラスにインクルードすることでその機能を利用することができます。
moduleGreetingdefsay_helloputs"Hello!"endendclassMyClassincludeGreetingendobj=MyClass.newobj.say_hello# => "Hello!"
上記の例では、Greetingモジュールを定義し、MyClassクラスにインクルードしています。その結果、MyClassのインスタンスであるobjからsay_helloメソッドが利用できるようになります。
Mix-inは、複数のクラスに同じメソッドや振る舞いを提供するためのデザインパターンです。モジュールを使ってMix-inを実現することができます。
moduleDebugdefdebug_infoputs"#{self.class} -#{self.inspect}"endendclassMyClassincludeDebugendobj=MyClass.newobj.debug_info# => "MyClass - #<MyClass:0x00007fb35a20b3b8>"
Debugモジュールは、debug_infoメソッドを提供し、このメソッドを含むクラスにMix-inします。これにより、クラスのインスタンスがデバッグ情報を表示するメソッドを利用できるようになります。
Minitestは、Rubyプログラミング言語用の軽量なテストフレームワークです。Minitestは、Rubyの標準ライブラリに含まれており、Rubyのバージョン1.9以降で利用可能です。Minitestは、テスト駆動開発(Test-Driven Development、TDD)や振る舞い駆動開発(Behavior-Driven Development、BDD)などのソフトウェア開発手法を支援するために使用されます。
Minitestは、単体テストや統合テストなど、さまざまな種類のテストをサポートしています。また、アサーションを用いて期待される振る舞いを確認するための豊富な機能も提供しています。Minitestの特徴は、シンプルで使いやすいインターフェース、高速な実行速度、そしてRubyの標準ライブラリに含まれているため、追加の依存関係を導入せずに利用できることです。
開発者がMinitestを使用することで、安定性や品質を向上させることができます。また、テストコードの記述や実行が容易であるため、素早く効果的なテストを作成することができます。
実際のコードを観てみましょう。
下記は、逆ポーランド記法の式を評価する機能を配列を継承して実装した例で、コメントを合わせても30行のコードですが、各演算子の実装の確認など基礎的なテストケースを書くだけで、数倍のコード量になります。
# frozen_string_literal: true# RPNクラスは逆ポーランド記法(Reverse Polish Notation)の式を評価するためのクラスです。# スタック構造を使用して式を解析し、結果を計算します。# Rubyでは、Arrayクラスがpop,pushなどのスタックとしてのメソッドが完備されているので継承しました。classRPN<Array# 与えられた逆ポーランド記法の式を評価し、結果を返します。# @param expression [String] 評価する式# @return [Numeric] 式の評価結果defeval(expression)expression.split.eachdo|token|casetokenwhen/\A-?\d+\z/,# 十進数/\A[+-]?0[Bb][01]+\z/,# 2進数/\A[+-]?0[Oo][0-7]+\z/,# 8進数/\A[+-]?0[Xx][0-9A-Fa-f]+\z/# 10進数pushInteger(token)when/\A-?\d+(\.\d+)?([eE][-+]?\d+)?\z/# 浮動小数点数push(token.to_f)when*%w[+ - * / % ** & | ^ << >>]# 二項演算子left,right=pop(2)pushleft.send(token.to_sym,right)elseraiseRuntimeError,"Invalid operator:#{token}"endend# 最終的な結果はスタックの一番上に残るpeekend# スタックの一番上の値を返しますが、スタックから削除しません。# @return [Numeric, nil] スタックの一番上の値。スタックが空の場合はnilを返します。aliaspeeklastendrequire'minitest/autorun'classTestRPN<Minitest::Testdefsetup@rpn=RPN.newenddeftest_eval_with_single_operandassert_equal5,@rpn.eval('5')assert_equal5,@rpn.eval('0b101')assert_equal-5,@rpn.eval('-0b101')assert_equal5,@rpn.eval('+0b101')assert_equal127,@rpn.eval('0o177')assert_equal-127,@rpn.eval('-0o177')assert_equal127,@rpn.eval('+0O177')assert_equal195935983,@rpn.eval('0xbadbeef')assert_equal-195935983,@rpn.eval('-0xbadbeef')assert_equal1.5,@rpn.eval('1.5')assert_equal1e234,@rpn.eval('1e+234')enddeftest_eval_with_additionassert_equal8,@rpn.eval('3 5 +')assert_equal10,@rpn.eval('2 +')enddeftest_eval_with_subtractionassert_equal7,@rpn.eval('10 3 -')enddeftest_eval_with_multiplicationassert_equal24,@rpn.eval('4 6 *')enddeftest_eval_with_divisionassert_equal5,@rpn.eval('15 3 /')enddeftest_eval_with_remainassert_equal2,@rpn.eval('11 3 %')enddeftest_eval_with_exponentiationassert_equal8,@rpn.eval('2 3 **')assert_equal1.4142135623730951,@rpn.eval('2 0.5 **')assert_equal633825300114114700748351602688,@rpn.eval('2 99 **')enddeftest_eval_with_bitwidth_andassert_equal8,@rpn.eval('12 10 &')assert_raises(NoMethodError){@rpn.eval('12.0 10 &')}assert_raises(TypeError){@rpn.eval('12 10.0 &')}enddeftest_eval_with_bitwidth_orassert_equal14,@rpn.eval('12 10 |')assert_raises(NoMethodError){@rpn.eval('12.0 10 |')}assert_raises(TypeError){@rpn.eval('12 10.0 |')}enddeftest_eval_with_bitwidth_exclusive_orassert_equal6,@rpn.eval('12 10 ^')assert_raises(NoMethodError){@rpn.eval('12.0 10 ^')}assert_raises(TypeError){@rpn.eval('12 10.0 ^')}enddeftest_eval_with_shift_leftassert_equal12288,@rpn.eval('12 10 <<')assert_raises(NoMethodError){@rpn.eval('12.0 10 <<')}assert_equal12288,@rpn.eval('12 10.1 <<')enddeftest_eval_with_shift_rightassert_equal15,@rpn.eval('123 3 >>')assert_raises(NoMethodError){@rpn.eval('123.0 3 >>')}assert_equal15,@rpn.eval('123 3.9 >>')enddeftest_eval_with_invalid_expressionassert_raises(TypeError){@rpn.eval('2 +')}enddeftest_peek_returns_top_element@rpn.eval('1 2 3 + +')assert_equal6,@rpn.peekenddeftest_nan_or_zero_division@rpn.eval('0.0 0 /')assert@rpn.peek.nan?@rpn.eval('0 0.0 /')assert@rpn.peek.nan?@rpn.eval('-0.0 0 /')assert@rpn.peek.nan?assert_raises(ZeroDivisionError){@rpn.eval('0 0 /')}enddeftest_inf_or_zero_division@rpn.eval('1.0 0 /')assert_equalFloat::INFINITY,@rpn.peek@rpn.eval('-1 0.0 /')assert_equal-Float::INFINITY,@rpn.peek@rpn.eval('1 -0.0 /')assert_equal-Float::INFINITY,@rpn.peek@rpn.eval('-1 -0.0 /')assert_equalFloat::INFINITY,@rpn.peekassert_raises(ZeroDivisionError){@rpn.eval('110 0 /')}enddeftest_peek_returns_nil_for_empty_stackassert_nil@rpn.peekendend
このコードは、逆ポーランド記法(Reverse Polish Notation)の式を評価するためのRPN(Reverse Polish Notation)クラスを定義しています。このクラスは、与えられた式をスタックを使用して解析し、計算します。以下に、このコードの機能と構造についての解説を行います。
# frozen_string_literal: true: この行は、ファイル内の文字列リテラルが変更されないことを確認するためのリテラルです。RPNクラスはArrayクラスを継承しており、スタックとしての機能を使用します。evalメソッドは与えられた逆ポーランド記法の式を評価し、結果を返します。peekメソッドは、スタックの一番上の値を返しますが、スタックから削除しません。TestRPNクラスはMinitest::Testを継承しており、RPNクラスのテストを定義します。setupメソッドは各テストメソッドの前に実行され、テストで使用するRPNオブジェクトをセットアップします。test_eval_with_single_operandからtest_eval_with_invalid_expressionまでのメソッドは、evalメソッドが正しく動作することを確認するためのテストです。test_peek_returns_top_elementはpeekメソッドがスタックの一番上の値を返すことを確認するテストです。test_nan_or_zero_divisionとtest_inf_or_zero_divisionは、NaNやInfinityの扱い、ゼロ除算の例外処理をテストします。このコードは、逆ポーランド記法の式の評価に関する機能を提供し、テストによってその正確性が検証されています。
RSpecは、Rubyでのテストを行うためのフレームワークの1つです。RSpecを使用すると、テストをより明確に記述し、テスト結果を見やすく出力することができます。
# Gemfilesource'https://rubygems.org'gem'rspec'
# コマンドラインbundleinstallユニットテストは、個々のコンポーネント(クラスやメソッドなど)が正しく機能するかをテストするものです。一方、統合テストは複数のコンポーネントが互いに連携して正しく動作するかをテストします。
# spec/my_class_spec.rb (ユニットテスト)require'my_class'RSpec.describeMyClassdodescribe'#method_name'doit'returns something'doobj=MyClass.newexpect(obj.method_name).toeq(something)endendend
# spec/integration_spec.rb (統合テスト)require'app'RSpec.describe'Integration Test'doit'checks something'do# 統合テストの実装endend
RSpecを使ってユニットテストと統合テストを書くことで、コードの品質を向上させ、予期せぬバグを見つけるのに役立ちます。
継承は、既存のクラスから新しいクラスを作成し、そのクラスが親クラスのすべての特性を引き継ぐことができる機能です。ポリモーフィズムは、同じ名前のメソッドが異なるクラスで異なる振る舞いをすることを指します。カプセル化は、データやメソッドをオブジェクト内部に隠蔽し、外部からのアクセスを制限することです。これらの概念を活用することで、より柔軟で効率的なコードを書くことができます。
classAnimaldefspeakraiseNotImplementedError,"Subclasses must implement the 'speak' method."endendclassDog<Animaldefspeak"Woof!"endendclassCat<Animaldefspeak"Meow!"endenddog=Dog.newputsdog.speak# => "Woof!"cat=Cat.newputscat.speak# => "Meow!"
上記の例では、Animalクラスが親クラスとして定義され、speakメソッドが定義されています。DogクラスとCatクラスはそれぞれAnimalクラスを継承し、speakメソッドをオーバーライドしています。これにより、DogクラスとCatクラスは同じ名前のメソッドを持ちつつも、それぞれ異なる振る舞いをします。
オブジェクト指向デザインパターンは、再利用可能なソフトウェア設計のガイドラインです。これらのパターンは、特定の問題に対する解決策を提供し、コードの再利用性、拡張性、保守性を向上させます。代表的なデザインパターンには、Singleton、Factory、Observer、Decoratorなどがあります。
# Singleton パターンの例require'singleton'classSingletonClassincludeSingletondefinitialize@counter=0enddefincrement_counter@counter+=1enddefget_counter@counterendendinstance1=SingletonClass.instanceinstance1.increment_counterputsinstance1.get_counter# => 1instance2=SingletonClass.instanceputsinstance2.get_counter# => 1
上記の例では、Singletonパターンを使用して、特定のクラスのインスタンスが常に1つしか存在しないことを保証しています。Singletonモジュールをインクルードすることで、インスタンスが複数作成されることを防ぎます。
Rubyにおける並行プログラミングは、スレッドとプロセスを使用して実現されます。スレッドはプロセス内で動作し、複数のスレッドが同時に実行されることで並行処理が可能になります。一方、プロセスは独立した実行単位であり、それぞれが独自のメモリ空間を持ちます。
以下では、Rubyにおけるスレッドとプロセスの管理について解説し、コード例を示します。
RubyではThreadクラスを使用してスレッドを作成し、Thread.newメソッドを使って新しいスレッドを生成します。生成されたスレッドは並行して実行され、メインスレッドと同時に動作します。スレッドの制御にはjoinメソッドを使用して、メインスレッドがスレッドの終了を待機することができます。
threads=[]# スレッドを生成して配列に格納threads<<Thread.newdo3.timesdosleep1puts"Thread 1 executing"endendthreads<<Thread.newdo3.timesdosleep2puts"Thread 2 executing"endend# 全てのスレッドが終了するのを待機threads.each(&:join)puts"All threads finished"
上記の例では、2つのスレッドを生成し、それぞれが一定間隔でメッセージを出力しています。joinメソッドにより、メインスレッドが全てのスレッドの終了を待機します。
Rubyでは、forkメソッドを使用して新しいプロセスをフォークし、子プロセス内で処理を実行することができます。プロセスの終了を待機するにはProcess.waitメソッドを使用します。
child_pid=forkdoputs"Child process executing"sleep3puts"Child process finished"endputs"Parent process waiting for child to finish"Process.wait(child_pid)puts"Parent process finished waiting"
上記の例では、親プロセスが子プロセスをフォークし、子プロセスが一定時間待機した後に終了します。親プロセスはProcess.waitメソッドで子プロセスの終了を待機します。
Rubyのスレッドとプロセスを使った並行プログラミングには、他にも多くの機能や制御方法がありますが、基本的な部分はこのようになります。
Rubyには、スレッドとプロセス以外にも並行プログラミングを行うためのさまざまな手段があります。以下にいくつかの方法を紹介します。
Fiberは、イテレータと似たコンテキストを持つ軽量なスレッドのようなものです。複数のFiberを作成し、それらを切り替えながら処理を行うことで、非同期の並行処理を実現することができます。
fiber1=Fiber.newdoputs"Fiber 1 executing"Fiber.yieldputs"Fiber 1 resumed"endfiber2=Fiber.newdoputs"Fiber 2 executing"Fiber.yieldputs"Fiber 2 resumed"endfiber1.resumefiber2.resumefiber1.resumefiber2.resume
Concurrent Ruby gemは、並行プログラミングを行うための機能を提供するGemです。ThreadPoolやFuture、Promise、Actorなどの機能を使って、複雑な並行処理を実現することができます。
require'concurrent'pool=Concurrent::FixedThreadPool.new(5)future=Concurrent::Future.execute(executor:pool)do# 並行処理を実行するコードsleep1"Future result"endputs"Waiting for future to complete..."putsfuture.value
Celluloid gemは、アクターモデルをベースにした並行プログラミングを行うためのGemです。アクターモデルでは、個々のアクターがメッセージを送受信しながら処理を実行します。
require'celluloid'classMyActorincludeCelluloiddefinitializeputs"Actor initialized"enddefprocess_message(message)puts"Received message:#{message}"endendactor=MyActor.newactor.async.process_message("Hello from main thread!")
これらの手法を使うことで、Rubyでより効果的な並行プログラミングを実現することができます。選択肢はプロジェクトの要件や好みに応じて異なりますが、Rubyの並行処理に関する柔軟性と機能性は広範囲にわたっています。
並行プログラミングでは、競合状態やデッドロックなどの問題が発生する可能性があります。競合状態は、複数のスレッドが同時に共有されたリソースにアクセスしようとすることで起こります。デッドロックは、複数のスレッドがお互いにリソースを解放するのを待ち合わせることで発生します。
これらの問題を回避するための解決策として、適切なロックの使用、スレッドセーフなデータ構造の選択、並行処理の慎重な設計などがあります。
並行処理の問題点と解決策について、コード例を示して説明します。
競合状態は、複数のスレッドが同時に共有されたリソースにアクセスしようとすることで発生します。
以下は、競合状態が発生する可能性のあるコード例です。
counter=0threads=10.times.mapdoThread.newdo1000.times{counter+=1}endendthreads.each(&:join)puts"Counter:#{counter}"
このコードでは、複数のスレッドがcounter変数にアクセスしています。しかし、counter += 1の操作は複数のスレッドで同時に実行される可能性があり、結果として期待通りにカウンターが増加しない可能性があります。
競合状態を回避するために、ロックを使用して複数のスレッドが同時に共有されたリソースにアクセスできないようにします。Rubyでは、Mutexクラスを使用してスレッドセーフなロックを実装できます。
counter=0mutex=Mutex.newthreads=10.times.mapdoThread.newdo1000.timesdomutex.synchronize{counter+=1}endendendthreads.each(&:join)puts"Counter:#{counter}"
このコードでは、Mutexを使用してcounter変数へのアクセスを同期化しています。これにより、複数のスレッドが同時にcounter変数にアクセスすることができず、競合状態が回避されます。
デッドロックは、複数のスレッドがお互いにリソースを解放するのを待ち合わせることで発生します。
以下は、デッドロックが発生する可能性のあるコード例です。
mutex1=Mutex.newmutex2=Mutex.newthread1=Thread.newdomutex1.locksleep1mutex2.lockmutex1.unlockmutex2.unlockendthread2=Thread.newdomutex2.locksleep1mutex1.lockmutex2.unlockmutex1.unlockendthread1.jointhread2.join
このコードでは、thread1がmutex1をロックし、同時にthread2がmutex2をロックする可能性があります。その後、各スレッドは相手のロックを解放するのを待ち合わせることになり、デッドロックが発生します。
デッドロックを回避するために、ロックを取得する順序を一貫させることが重要です。この方法により、複数のスレッドが同じ順序でロックを取得しようとしても、デッドロックを引き起こすことがありません。
mutex1=Mutex.newmutex2=Mutex.newthread1=Thread.newdomutex1.locksleep1mutex2.lockmutex1.unlockmutex2.unlockendthread2=Thread.newdomutex1.lock# ロックの順序を変更sleep1mutex2.lockmutex1.unlockmutex2.unlockendthread1.jointhread2.join
この修正では、thread2がmutex1を先にロックするように変更されており、ロックの順序が一致しています。これにより、デッドロックが発生する可能性が低くなります。
以上が、並行処理の問題点とその解決策をコード例を交えて説明したものです。競合状態やデッドロックは並行プログラミングにおける一般的な問題であり、適切な対処が必要です。
Ruby on Railsは、RubyでのWebアプリケーション開発を支援する人気のあるフレームワークです。Railsを使用することで、MVC(Model-View-Controller)アーキテクチャをベースにした効率的なWebアプリケーションを開発することができます。
# Railsアプリケーションの作成railsnewmyapp# モデルの作成railsgeneratemodelUsername:stringemail:string# データベースマイグレーションの実行railsdb:migrate# コントローラの作成railsgeneratecontrollerUsersController
# ルーティングの設定Rails.application.routes.drawdoresources:usersend
Railsでは、コマンドラインツールを使用してモデルやコントローラを簡単に生成し、ルーティングを設定することができます。これにより、短期間で効率的なWebアプリケーションを構築することができます。
MVC(Model-View-Controller)アーキテクチャは、アプリケーションの設計を3つの主要なコンポーネントに分割するアーキテクチャパターンです。
このように、MVCアーキテクチャは各コンポーネントが疎結合であり、変更が発生した際に一部のコンポーネントを変更しても他のコンポーネントに影響を与えにくい特徴があります。これにより、柔軟性が向上し、保守性が高まります。
RoR(Ruby on Rails)は、MVC(Model-View-Controller)アーキテクチャの理念に基づいて設計されたWebアプリケーションフレームワークです。RoRにおけるMVCの役割と機能を説明します。
classUser<ApplicationRecordvalidates:name,presence:trueend
<% @users.eachdo|user|%> <p><%=user.name%></p><% end%>
classUsersController<ApplicationControllerdefindex@users=User.allendend
config/routes.rbファイルに記述され、特定のURLパスへのリクエストを特定のコントローラアクションにマップします。Rails.application.routes.drawdoresources:users,only:[:index]end
Ruby on RailsはMVCアーキテクチャにより、アプリケーションの各コンポーネントが明確に分離され、保守性が向上し、柔軟性が高まります。
Railsでは、アクティブレコードと呼ばれるオブジェクト関係マッピング(ORM)ツールを使用して、データベースとのやり取りを簡単に行うことができます。アクティブレコードを使用することで、SQLクエリを直接記述せずに、Rubyオブジェクトとしてデータベースのテーブルを操作することができます。
# ユーザーデータの取得users=User.all# 新しいユーザーの作成user=User.new(name:"Alice",email:"alice@example.com")user.save
Railsでは、データベースへの接続設定を行うことで、アプリケーション内でアクティブレコードを使用してデータベースとやり取りすることができます。
ORMは、オブジェクトとリレーショナルデータベースとの間のマッピングを行うためのツールです。ORMを使用することで、データベースのテーブルをオブジェクトとして操作することができ、SQLクエリの記述を最小限に抑えることができます。これにより、コードの可読性や保守性が向上します。
メモリ管理とコードの最適化は、Rubyプログラミングにおいて重要な要素です。効率的なメモリ管理を行うことで、アプリケーションのメモリ使用量を最小限に抑え、パフォーマンスを向上させることができます。また、コードの最適化により、処理速度を高速化し、リソースの効率的な利用を実現することができます。
プロファイリングツールは、アプリケーションのパフォーマンスを詳細に分析し、ボトルネックを特定するための有用なツールです。プロファイリングツールを使用することで、どの部分が最もリソースを消費しているかや、どの部分が最も遅いかなどを把握することができます。
これらのプロファイリングツールを使用して、アプリケーションのパフォーマンスを詳細に分析し、効率的な最適化を行いましょう。
上級レベルでは、オブジェクト指向デザインパターンの理解と実装に焦点を当てましたが、実際のアプリケーション開発では、これらのパターンを活用して柔軟で効率的なコードを書くことが重要です。より実践的な例や、実際の問題に対する解決策を学ぶことで、より高度な開発スキルを身につけることができます。
プロの開発者になるためには、セキュリティとエラーハンドリングの重要性を理解し、適切な対策を講じることが不可欠です。セキュリティホールや悪意のある攻撃からアプリケーションを保護し、エラーが発生した際にユーザーやシステムに影響を与えないようにするための方法を学びましょう。
テスト駆動開発(Test-Driven Development, TDD)は、コードを書く前にテストを書き、そのテストをパスするようなコードを実装する開発手法です。TDDを実践することで、より品質の高いコードを生産し、バグの早期発見やリファクタリングの容易化が可能となります。
コード品質は、プロの開発者にとって非常に重要な要素です。コードを読みやすく、保守しやすく、拡張しやすくするためのリファクタリングのテクニックやベストプラクティスを学び、コードベースを常に改善していきましょう。
これらのトピックを網羅的に学ぶことで、より高度なRubyプログラミングスキルを身につけることができます。プロの開発者としてのキャリアを築くために、着実にスキルを向上させていきましょう。
Rubyの変数は、オブジェクトに名前をつけるために使われます。
Rubyでは、変数は宣言する必要はありません。
オブジェクトを変数に代入するには
変数名=オブジェクト
の様に代入演算子= を使います。
a=1pa# => 1a="abc"pa# => "abc"a=[1,2,3]pa# => [1, 2, 3]b=apb# => [1, 2, 3]b[1]=999pa# => [1, 999, 3]# bはaと同じ配列を参照しているため、bの変更がaにも反映されます。
これに対し、pメソッドはオブジェクトにinspectメソッドで適用した結果を表示します。
puts"Hello, world!"# => Hello, world!p"Hello, world!"# => "Hello, world!"
Rubyにおける定数は、変更できない値のことです。定数は大文字で始まり、一度値を代入するとその後変更することができません。定数を宣言するには、変数名の先頭に大文字のアルファベットを付けます。
定数は、クラスやモジュールの定義内で宣言される場合、そのクラスやモジュールのスコープにのみ存在します。クラスやモジュール外で宣言された定数は、グローバルスコープに属します。
定数にアクセスするには、定数名を参照します。定数が未定義の場合、RubyはNameError例外を発生させます。
以下は、定数を宣言して参照する例です。
# クラス内で定数を宣言するclassMyClassMY_CONSTANT=100end# クラス外で定数を参照するputsMyClass::MY_CONSTANT#=> 100
また、Rubyでは組み込み定数もいくつか存在します。例えば、Math::PIは円周率を表す定数です。
グローバル変数は、プログラム中のどこからでもアクセス可能な変数です。グローバル変数は、$記号で始まる変数名を持ちます。一般的に、グローバル変数は、複数のメソッドで共有するデータを格納するために使用されます。ただし、グローバル変数は多用すべきではなく、できるだけ避けるべきです。
$global_variable=10defprint_globalputs"Global variable is#$global_variable"endprint_global
このコードでは、$global_variableというグローバル変数が定義されています。そして、print_globalメソッド内でその値が参照されて表示されます。グローバル変数はプログラム中のどこからでも参照可能であるため、異なるメソッド間でデータを共有する場合に使用されます。しかし、グローバル変数の使用は避けるべきであり、できるだけローカル変数やインスタンス変数を使用することが推奨されます。
特殊変数は、Rubyがあらかじめ定義している変数で、プログラム中で直接代入することができません。これらの変数は、プログラムの実行中に自動的に設定され、Rubyの様々な機能で使用されます。
$0: 現在のプログラムファイル名$~: 最後にマッチした正規表現$&: 最後にマッチした文字列$': 最後にマッチした文字列より後ろの文字列$': 最後にマッチした文字列より後ろの文字列$1,$2,$3...: 最後にマッチした正規表現の1番目、2番目、3番目...のキャプチャグループにマッチした文字列$stdin: 標準入力$stdout: 標準出力$stderr: 標準エラー出力$LOAD_PATH: ライブラリの検索パス$:: ライブラリの検索パス($LOAD_PATHの別名)puts"This program's name is#{$0}"puts"The Ruby version is#{RUBY_VERSION}"puts"The current line number is#{$.}"
Rubyは完全なオブジェクト指向言語であり、すべてがオブジェクトです。オブジェクトは、データとそれに対する操作をカプセル化したものであり、オブジェクトを操作するためにメソッドを使用します。
以下は、オブジェクトについての基本的な説明です。
これらのオブジェクトのクラスを知ることは、そのオブジェクトがどのような振る舞いをするかを理解するために非常に重要です。
Rubyにおけるすべてのオブジェクトは、Objectから継承されています。Objectには、すべてのオブジェクトに共通するメソッドが定義されています。
以下は、Objectに関する基本的な説明です。
Objectのサブクラスであるため、Objectに定義されているメソッドはすべてのオブジェクトで利用できます。Objectには、例外を発生させるraiseメソッドや、オブジェクトのクラスを取得するclassメソッド、オブジェクトが同一かどうかを判定するequal?メソッドなど、多くの便利なメソッドが定義されています。Objectには、to_sメソッドも定義されており、すべてのオブジェクトはto_sメソッドを呼び出すことができます。to_sメソッドは、オブジェクトを文字列に変換します。このメソッドは、デバッグやログ出力などに活用されます。Objectには、同一性比較に使用される==メソッドも定義されています。==メソッドは、2つのオブジェクトが同じかどうかを比較します。オブジェクトの同一性を比較する場合は、equal?メソッドを使用します。Objectには、.initializeメソッドも定義されており、新しいオブジェクトを作成する際に呼び出されます。.initializeメソッドをオーバーライドすることで、新しいオブジェクトが初期化される際の処理をカスタマイズすることができます。オブジェクトのクラスを調べるには、Objectクラスのclassメソッドを使います。
puts"Hello, world!".class# ==> Stringputs10.class# ==> Integerputs3.14.class# ==> Floatputs[2,3,5,7,11].class# ==> Arrayputs({"key1"=>"value1","key2"=>"value2"}).class# ==> Hashputs/hello/.class# ==> Regexpputs(1..10).class# ==> Rangeputs:abc.class# ==> Symbolputsfalse.class# ==> FalseClassputstrue.class# ==> TrueClassputsnil.class# ==> NilClassputs(lambda{|x,y|x+y}).class# ==> ProcputsComplex(3,4).class# ==> ComplexputsRational(22,7).class# ==> RationalputsTime.now.class# ==> TimeputsStruct.new("Cat",:sex,:age).class# ==> Class
| メソッド名 | 引数の数 | 説明 |
|---|---|---|
!= | 1 | オブジェクトと引数が等しくない場合にtrue、等しい場合にfalseを返す |
! | 0 | オブジェクトがfalseまたはnilの場合にtrueを返す |
== | 1 | オブジェクトと引数が等しい場合にtrue、等しくない場合にfalseを返す |
=== | 1 | 引数がオブジェクトと等しい場合にtrue、そうでない場合にfalseを返す |
__id__ | 0 | オブジェクトのobject_idを返す |
__send__ | 1以上 | メソッドを動的に呼び出す |
class | 0 | オブジェクトのクラスを返す |
clone | 0 | オブジェクトを複製して返す |
dup | 0 | オブジェクトを浅く複製して返す |
equal? | 1 | オブジェクトと引数が同じオブジェクトである場合にtrue、そうでない場合にfalseを返す |
instance_eval | 0または1 | レシーバーオブジェクトに対してブロックを評価する |
instance_of? | 1 | オブジェクトが指定したクラスのインスタンスである場合にtrue、そうでない場合にfalseを返す |
instance_variable_defined? | 1 | 指定されたインスタンス変数が定義されている場合にtrue、そうでない場合にfalseを返す |
instance_variable_get | 1 | 指定されたインスタンス変数の値を取得する |
instance_variable_set | 2 | 指定されたインスタンス変数に値を設定する |
kind_of? | 1 | オブジェクトが指定したクラスのインスタンスである場合にtrue、そうでない場合にfalseを返す |
method | 1 | 指定されたメソッドオブジェクトを返す |
nil? | 0 | オブジェクトがnil |
Rubyにおける式と演算子について説明します。
Rubyにおける式とは、ある値を評価するためのコードのことを指します。例えば、以下のような式があります。
1+2
この式は、1と2を足した結果を評価するための式です。この式を実行すると、3という値が返されます。
演算子は、式を組み合わせたり、値を変更するために使用されます。Rubyにはさまざまな種類の演算子があります。以下はいくつかの例です。
数値演算子には、加算、減算、乗算、除算、剰余演算子などがあります。
以下はいくつかの例です。
# 整数p10+3# 13: 加算p10-3# 7: 減算p10*3# 30: 乗算p10/3# 3: 除算p10%3# 1: 剰余演算# 文字列p"abc"+"xyz"# abcxyz: 連結p"abc"*3# abcabcabc: 反復p"%x"%77# 4d: 書式化# 配列p[1,2,3]+[7,8,9]# [1, 2, 3, 7, 8, 9]: 連結# 有理数pRational(2,3)+3# (11/3)pRational(2,3)-3# (-7/3)pRational(2,3)*3# (2/1)pRational(2,3)/3# (2/9)# 複素数p(-2)**0.5# (0.0+1.4142135623730951i)pComplex(2,3)+3# (5+3i)pComplex(2,3)-3# (-1+3i)pComplex(2,3)*3# (6+9i)pComplex(2,3)/3# ((2/3)+1i)
Rubyの四則演算子と剰余演算子は、通常の数値演算に使用されることが一般的ですが、オブジェクト指向プログラミング言語であるRubyでは、これらの演算子がオーバーロードされることがあります。
数値演算子の使用は、さまざまなデータ型に対して行われます。整数、文字列、配列、有理数、複素数など、Rubyは様々なデータ型をサポートしており、それぞれのデータ型に対して適切な演算が行われます。
例えば、文字列の場合、加算演算子は文字列の連結を行います。同様に、整数や有理数の場合は数値演算が行われますが、複素数の場合は複素数の演算が行われます。
また、Rubyでは四則演算子や剰余演算子などの演算子がオーバーロードされることがあります。これにより、ユーザー定義のクラスやオブジェクトに対して独自の演算を定義することができます。オブジェクト指向の特性を活かした柔軟な演算の定義が可能です。
数値演算子の適切な使用は、プログラムの正確性や効率性に直結します。Rubyの数値演算子を適切に理解し、適切に活用することで、より効率的で読みやすいコードを書くことができます。
比較演算子とComparableモジュールは、Rubyにおいてオブジェクトの比較を可能にする重要な機能です。
比較演算子には以下のようなものがあります:
==: 等しい!=: 等しくない>: より大きい<: より小さい>=: 以上<=: 以下以下はいくつかの例です。
a=1b=2putsa==b# 等しいputsa!=b# 等しくないputsa>b# より大きいputsa<b# より小さいputsa>=b# 以上putsa<=b# 以下
これらの演算子を使って、オブジェクト同士を比較することができます。例えば、整数同士や文字列同士の比較などが挙げられます。
Comparableモジュールは、比較可能なオブジェクトを定義するためのモジュールです。このモジュールをクラスにincludeすることで、そのクラスのインスタンス同士を比較可能にすることができます。
Comparableモジュールでは、<=>演算子(スペース船演算子)を実装する必要があります。この演算子は、二つのオブジェクトを比較して以下のような値を返します:
Comparableモジュールを使用することで、クラス内で<=>演算子を定義するだけで、そのクラスのインスタンス同士を比較することができます。これにより、コードの可読性を高め、一貫した比較処理を実現することができます。
例えば、数値や文字列など、大小関係を持つオブジェクトを扱う場合に便利です。また、minやmaxなどのメソッドを利用して、比較可能なオブジェクトの最小値や最大値を簡単に取得することもできます。
classPersonincludeComparableattr_accessor:name,:agedefinitialize(name,age)@name=name@age=ageenddef<=>(other)@age<=>other.ageendendp1=Person.new("Alice",25)p2=Person.new("Bob",30)putsp1<p2# trueputsp1==p2# falseputsp1>p2# false
Personクラスでは、<=>演算子を定義して、ageインスタンス変数を比較しています。そのため、Personクラスのインスタンスは、<,<=,==,>,>=,between?演算子によって比較可能になります。
論理演算子には、AND、OR、NOT演算子があります。以下はいくつかの例です。
a=trueb=falseputsa&&b# false: ANDputsaandb# true: AND(低い優先度なのでputsと先に結合)putsa||b# true: ORputsaorb# true: OR(低い優先度)puts!a# false: NOT
&& の優先度は|| より強いです(論理積と論理和なので)。|| の優先度はand より強いです(単語版の方が弱いとおぼえてください)。and の優先度はor より強いです(論理積と論理和なので)。以下に、論理積 (AND)、論理和 (OR)、論理否定 (NOT) を表現するための真理値表を示します。
| A | B | A AND B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
| A | B | A OR B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
| A | NOT A |
|---|---|
| 0 | 1 |
| 1 | 0 |
上記の表で、A と B はそれぞれ真 (1) または偽 (0) の値を表し、AND や OR などの論理演算子によって表された結果は、真 (1) または偽 (0) のいずれかです。また、論理否定では、NOT A は A の真偽を反転させた値となります。
Rubyの短絡評価とは、論理式の評価において、最初の式で結果が確定した場合に、その後の式を評価しないことを指します。つまり、式が最初の部分で真偽値が確定した場合、後続の式を評価せずに即座に結果を返します。
例えば、以下のようなコードがあったとします。
defcheck_name(name=nil)ifname&&name.length>0puts"名前は#{name}です"elseputs"名前が未入力です"endendcheck_name()# => "名前が未入力です"check_name("")# => "名前が未入力です"check_name("John")# => "名前はJohnです"
このコードでは、name がnil でないかつ文字列の長さが0より大きいかを同時にチェックしています。具体的には、条件式name && name.length > 0 によってチェックしています。
この条件式では、短絡評価 (short-circuit evaluation) が利用されています。短絡評価とは、条件式の全ての部分を評価せずに、最初に結果が確定した部分で評価を終了するという評価方法です。具体的には、この場合はnameがnilである場合、name.length > 0 の評価は行われず、条件式全体がfalseと判定されます。
このような短絡評価の利用により、コードのパフォーマンスを向上させることができます。また、name がnil の場合にname.length を呼び出すことがなくなるため、エラーを避けることができます。
しかし、短絡評価を乱用すると、プログラムの可読性が低下したり、バグを引き起こしたりすることがあります。適切な場面でのみ利用するようにしましょう。
「Rubyでは演算子もメソッド」とは、Rubyにおいて演算子 (たとえば+,-,*,/ など) が単なる記号ではなく、それぞれが対応するメソッドとして定義されているということを指します。
例えば、+ 演算子は、2つの数値を足し合わせるために使われますが、実際には+ メソッドとして定義されています。以下のように+ メソッドを使って足し算を行うことができます。
a=1b=2c=a.+(b)# a + b と同じputsc# => 3
また、* 演算子も同様に* メソッドとして定義されています。以下のように、文字列に対して* メソッドを使って、指定した回数だけ繰り返した新しい文字列を作ることができます。
str="hello"new_str=str.*(3)# str * 3 と同じputsnew_str# => "hellohellohello"
このように、Rubyでは演算子も単なる記号ではなく、それぞれが対応するメソッドとして定義されているため、オブジェクト指向の考え方に基づいた柔軟なプログラミングが可能になっています。
Rubyでは、クラスやモジュールで演算子に対応するメソッドを定義することができます。このようにして定義された演算子メソッドをオーバーロードと呼びます。
以下は、Vector2 クラスで+ 演算子に対応するメソッドを定義する例です。このメソッドでは、Vector2 クラスのインスタンス同士を足し合わせることができます。
classVector2attr_accessor:x,:ydefinitialize(x,y)@x=x@y=yenddef+(other)Vector2.new(@x+other.x,@y+other.y)enddefto_s()"[#{@x},#{@y}]"endendv1=Vector2.new(1,2)v2=Vector2.new(3,4)v3=v1+v2# => Vector2オブジェクトputsv3# => [4, 6]
このコードは、2次元ベクトルを表すVector2 クラスを定義し、2つのベクトルを足し合わせるメソッドを実装しています。
まず、attr_accessor によって、x とy のインスタンス変数に対する getter/setter メソッドが定義されています。これによって、外部からv1.x のようにしてインスタンス変数にアクセスしたり、v1.x = 10 のようにしてインスタンス変数に値を代入したりできます。
次に、initialize メソッドが定義されています。これは、new メソッドが呼ばれた時に、引数として渡されたx とy をインスタンス変数@x と@y に代入するためのコンストラクタです。
+ メソッドは、引数として与えられたother という別のVector2 オブジェクトを足し合わせ、新しいVector2 オブジェクトを作成して返します。これによって、v1 + v2 のようにして2つのベクトルを足し合わせることができます。
最後に、to_s メソッドが定義されています。これは、オブジェクトを文字列に変換するためのメソッドで、ベクトルの座標を[x, y] のような形式の文字列に変換して返します。
実際に、v1 とv2 を足し合わせた結果をv3 に代入し、puts v3 でv3 を表示しています。v3 の値は[4, 6] となります。
このように演算子メソッドをオーバーロードすることで、プログラムの読みやすさや柔軟性を高めることができます。ただし、意図しない挙動を招くこともあるため、注意して使いましょう。
Rubyでは、演算子をオーバーロードすることが出来ます。オーバーロード可能な演算子には、組み込みクラスの演算子も含まれます。
classStringdef/(s)=split(s)endary="The quick brown fox jumps over the lazy dog"/" "pary# => ["The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"]str=ary*" ! "pstr# => "The ! quick ! brown ! fox ! jumps ! over ! the ! lazy ! dog"
このコードは、RubyのString クラスに、新しいメソッド/ を追加しています。
/ メソッドは、文字列を引数s で分割するためのメソッドです。実装はsplit メソッドを呼び出すだけで、文字列オブジェクト自身をself で表しています。
まず、/ メソッドによって、文字列"The quick brown fox jumps over the lazy dog" がスペース" " で分割され、配列ary に格納されます。ここでary は、["The", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"] という値を持ちます。
次に、* メソッドによって、配列ary が感嘆符"!" で連結され、文字列str が作成されます。ここでstr は、"The ! quick ! brown ! fox ! jumps ! over ! the ! lazy ! dog" という値を持ちます。
このように、/ と* メソッドを利用することで、簡単に文字列の分割や連結を行うことができます。ただし、他の開発者がこのようなメソッドを利用しているコードを読む場合、このような振る舞いを予想できない可能性があるため、注意が必要です。
/ と* を、それぞれ文字列の分割と結合に割り当てる例は、Pikeに見ることが出来ます。
演算子には優先順位があります。
例えば、以下のようなコードがあった場合、演算子の優先順位に従って評価されます。
a=2+3*4**2/2
ここで、**は先に評価されます。次に、*と/が同じ優先順位なので、左から右に評価されます。最後に+が-よりも優先度が高いので、+が評価されます。
a=2+((3*(4**2))/2)
つまり、aの値は50になります。
同じ優先順位の演算子が続いた場合、演算子の結合方向により実行順序が決まります。
例えば、以下のコードがある場合、
a=2**3**2
**は右結合なので、3 ** 2が先に評価されます。そして、2 ** 9が評価されて、aは512になります。
a=2**(3**2)
以下は、Rubyの演算子の優先順位と結合方向の表です。
| 演算子 | 説明 | 優先順位 | 結合方向 |
|---|---|---|---|
| :: | クラスやオブジェクトのネスト | 1 | 左結合 |
| [] | 配列やハッシュから要素を取得する | 2 | 左結合 |
| + | 単項演算子(正) | 3 | 右結合 |
| ! | 論理否定 | ||
| ~ | ビット否定 | ||
| ** | べき乗 | 4 | 右結合 |
| - | 単項演算子(負) | 5 | 右結合 |
| * | 乗算 | 6 | 左結合 |
| / | 除算 | ||
| % | 剰余算 | ||
| + | 加算 | 7 | 左結合 |
| - | 減算 | ||
| << | ビットシフト(左) | 8 | 左結合 |
| >> | ビットシフト(右) | ||
| & | ビット積 | 9 | 左結合 |
| | | ビット和 | 10 | 左結合 |
| ^ | ビット排他的論理和 | ||
| > | 大なり | 11 | 左結合 |
| >= | 以上 | ||
| < | 未満 | ||
| <= | 以下 | ||
| <=> | 比較 | 12 | 左結合 |
| == | 一致 | ||
| === | 厳密に一致 | ||
| != | 不一致 | ||
| =~ | パターンマッチング | ||
| !~ | アンマッチ | ||
| && | 論理積 | 13 | 左結合 |
| || | 論理和 | 14 | 左結合 |
| .. | 範囲を生成する(終端を含む) | 15 | 左結合 |
| ... | 範囲を生成する(終端を含まない) | ||
| ? : | 条件演算子 | 16 | 右結合 |
| = | 代入 | 17 | 右結合 |
| op= | 代入演算子 | ||
| not | 論理否定 | 18 | 右結合 |
| and | 論理積 | 19 | 左結合 |
| or | 論理和 |
a=5b=10puts"a =#{a}"puts"b =#{b}"puts"-"*20# 算術演算子puts"算術演算子:"puts"a + b =#{a+b}"puts"a - b =#{a-b}"puts"a * b =#{a*b}"puts"b / a =#{b/a}"puts"b % a =#{b%a}"puts"a ** 2 =#{a**2}"puts"-"*20# 比較演算子puts"比較演算子:"puts"a == b :#{a==b}"puts"a != b :#{a!=b}"puts"a > b :#{a>b}"puts"a < b :#{a<b}"puts"b >= a :#{b>=a}"puts"a <= b :#{a<=b}"puts"-"*20# 論理演算子puts"論理演算子:"puts"a == 5 && b == 10 :#{a==5&&b==10}"puts"a == 5 || b == 5 :#{a==5||b==5}"puts"! (a == 5) :#{!(a==5)}"puts"-"*20# 代入演算子puts"代入演算子:"puts"a += b :#{a+=b}"puts"a -= b :#{a-=b}"puts"a *= b :#{a*=b}"puts"a /= b :#{a/=b}"puts"a %= b :#{a%= b}"puts "a **=2:#{a **= 2}"puts"-"*20# 三項演算子puts"三項演算子:"puts"a > b ? 'a is greater than b' : 'a is less than or equal to b'"puts"-"*20# ビット演算子puts"ビット演算子:"puts"a & b :#{a&b}"puts"a | b :#{a|b}"puts"a ^ b :#{a^b}"puts"~a :#{~a}"puts"a << 1 :#{a<<1}"puts"a >> 1 :#{a>>1}"puts"-"*20# 演算子メソッドputs"演算子メソッド:"puts"a == b :#{a.==(b)}"puts"a != b :#{a.!=(b)}"puts"a > b :#{a.>(b)}"puts"a < b :#{a.<(b)}"puts"b >= a :#{b.>=(a)}"puts"a <= b :#{a.<=(b)}"puts"a <=> b :#{a.<=>(b)}"puts"a === b :#{a.===(b)}"puts"a =~ b :#{a.=~(b)}"puts"-"*20# その他の演算子puts"その他の演算子:"puts"defined? a :#{defined?(a)}"puts"(1..10).each do |i| puts i end"puts":symbol.to_s :#{:symbol.to_s}"puts"%w(one two three) :#{%w(one two three)}"puts"-"*20
a = 5b = 10--------------------算術演算子:a + b = 15a - b = -5a * b = 50b / a = 2b % a = 0a ** 2 = 25--------------------比較演算子:a == b : falsea != b : truea > b : falsea < b : trueb >= a : truea <= b : true--------------------論理演算子:a == 5 && b == 10 : truea == 5 || b == 5 : true! (a == 5) : false--------------------代入演算子:a += b : 15a -= b : 5a *= b : 50a /= b : 5a %= b : 5a **= 2 : 25--------------------三項演算子:a > b ? 'a is greater than b' : 'a is less than or equal to b'--------------------ビット演算子:a & b : 8a | b : 27a ^ b : 19~a : -26a << 1 : 50a >> 1 : 12--------------------演算子メソッド:a == b : falsea != b : truea > b : truea < b : falseb >= a : falsea <= b : falsea <=> b : 1a === b : falsea =~ b :--------------------その他の演算子:defined? a : local-variable(1..10).each do |i| puts i end:symbol.to_s : symbol%w(one two three) : ["one", "two", "three"]--------------------
Rubyには、if-else-elsif-end、unless-else-end、case-when-else-end、case-in-else-end、while文、until文、for文、そしてbegin-rescue-else-ensure-endのような例外処理構文など、様々な制御構造があります。これらの制御構造を使うことで、プログラムの実行を条件に応じたループや分岐によって制御することができます。制御構造を正しく理解し、適切に使用することで、効率的で洗練されたプログラムを書くことができます。
また広義の制御構造にはブロックを伴う each や loop などのメソッドも含まれ、ここではそれらについても解説します。
# if-elsif-else-endif条件式1then処理1elsif条件式2then処理2else処理3end# if修飾子式if条件式# unless-else-endunless条件式then処理1else処理2end# unless修飾子式unless条件式# case-when-else-endcase対象のオブジェクトwhen条件式1then処理1when条件式2then処理2when条件式3then処理3else処理4end# while文while条件式do処理end# while修飾子式while条件式# until文until条件式do処理end# until修飾子式until条件式# for文for変数in範囲do処理end# loopメソッドloopdo処理end# eachメソッドコレクション.eachdo|変数|処理end# timesメソッド回数.timesdo|変数|処理end
Rubyの条件分岐には、if-elsif-else-end、unless-else-end、そしてcase-when-else-endがあります。また、if, unless には修飾子構文もあります。
if-elsif-else-endは、条件に応じて処理を分岐するためのもっとも基本的な構文です。
if条件式1処理1elsif条件式2処理2else処理3end
例えば、ある数値変数numが0未満なら「negative」、0なら「zero」、0より大きいなら「positive」と表示するプログラムは以下のように書けます。
num=-3ifnum<0puts"negative"elsifnum==0puts"zero"elseputs"positive"end
if修飾子は、条件式が肯定されたとき式を評価する構文です。
式if条件式
例えば、ある数値変数numが0未満なら「negative」と表示するプログラムは以下のように書けます。
num=-3puts"negative"ifnum<0
unless-else-endは、if-elsif-else-endと似たような構文ですが、条件式が否定されたときに処理が実行されます。
unless条件式処理1else処理2end
例えば、ある数値変数numが0以上なら「non-negative」、0未満なら「negative」と表示するプログラムは以下のように書けます。
num=-3unlessnum>=0puts"negative"elseputs"non-negative"end
unless修飾子は、if修飾子に似ていますが、条件式が否定されたときに式を評価する構文です。
式unless条件式
例えば、ある数値変数numが0以上でなければ「negative」と表示するプログラムは以下のように書けます。
num=-3puts"negative"unlessnum>=0
case-when-else-endは、複数の値に応じて処理を分岐するための構文です。if-elsif-else-endと比較して、1つのオブジェクトに対して複数の条件式を一度に評価できるという利点があります。
caseオブジェクトwhen条件式1処理1when条件式2処理2else処理3end
例えば、ある文字変数cに対して、それが母音なら「vowel」、子音なら「consonant」、数字なら「number」、それ以外なら「other」と表示するプログラムは以下のように書けます。
c='a'casecwhen'a','e','i','o','u'puts"vowel"when'0','1','2','3','4','5','6','7','8','9'puts"number"when'b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','y','z'puts"consonant"elseputs"other"end
Rubyのcase 文では、比較に用いるオブジェクトの型に応じて条件分岐することができます。具体的には、case 文の式の型が以下のいずれかである場合に、対応する条件分岐が行われます。
Integer) の場合String) の場合Regexp) の場合Class) の場合以下に、それぞれの場合の例を示します。
casenumwhen0puts"num is zero"when1puts"num is one"when2puts"num is two"elseputs"num is something else"end
casestrwhen"foo"puts"str is foo"when"bar"puts"str is bar"when/baz/puts"str includes baz"elseputs"str is something else"end
casestrwhen/foo/puts"str includes foo"when/bar/puts"str includes bar"whenRegexp.new("baz")puts"str includes baz"elseputs"str is something else"end
caseobjwhenIntegerputs"obj is an instance of Integer"whenStringputs"obj is an instance of String"whenArrayputs"obj is an instance of Array"elseputs"obj is an instance of something else"end
これらの例では、case 文の式の型によって、比較に用いるオブジェクトの型や正規表現パターンが異なることに注意してください。また、どの条件にも一致しない場合に実行されるelse 節は省略可能です。
if、unless、case は、制御フロー構文としての使い方に加えて、式としても使うことができます。例えば、if 式を使ってみましょう。通常のif は、条件がtrue ならば処理を実行します。
ifx>10puts"x is greater than 10"end
このif 文を式として使うこともできます。具体的には、以下のようにif の後ろに式を書き、式の値がtrue であれば、if の条件式の評価値が式の値となります。
a=ifx>10"x is greater than 10"else"x is less than or equal to 10"endputsa
同様に、unless も式として使うことができます。
a=unlessx>10"x is less than or equal to 10"else"x is greater than 10"endputsa
また、case 式もあります。case 式は、複数の条件に対する分岐処理を行う場合に使われます。以下は、case 式の例です。
a=casexwhen1"one"when2"two"else"other"endputsa
case 式では、when 句を使って、複数の条件式を書くことができます。case 式の評価値は、when 句の条件式と一致する最初のものの右辺の式の値となります。Rubyのパターンマッチングは、バージョン2.7で導入された新機能であり、構造化された値に対してパターンをマッチングすることができます。これにより、より複雑なデータ構造を柔軟に操作できるようになります。
以下に、Rubyのパターンマッチングの基本的な構文と機能について説明します。
case式inパターン1# パターン1にマッチする場合の処理inパターン2# パターン2にマッチする場合の処理else# どのパターンにもマッチしない場合の処理end
パターンは以下のようなものがあります:
パターンマッチングによって、マッチした部分をローカル変数にバインドすることができます。
case[1,2]inInteger=>a,Integerputs"マッチしました:#{a}"elseputs"マッチしませんでした"end
ifを使用して、パターンがマッチした場合に追加の条件を指定することができます。
case[1,2]ina,bifb==a*2puts"マッチしました"elseputs"マッチしませんでした"end
複数のパターンを組み合わせてマッチングを行うこともできます。
case[1,2]in[1,Integer]puts"マッチしました"elseputs"マッチしませんでした"end
パターンマッチングが失敗した場合には、例外が発生します。これは、NoMatchingPatternErrorとして知られています。
カスタムクラスに対してパターンマッチングを行う場合、deconstructやdeconstruct_keysメソッドを実装することでマッチングを行うことができます。
以上が、Rubyのパターンマッチングについての基本的な説明です。この機能は、プログラミングのパラダイムをより関数型プログラミング寄りのスタイルに変えることができます。
Rubyの反復構文は、特定の条件が満たされるまでコードのブロックを繰り返すことができる文のセットです。最も一般的な反復文は、while、until、for、loop です。
また、while, until には修飾子構文もあります。
これらの基本的な反復処理文に加えて、Ruby には break、next、redo、lazy などの反復処理文があります。これらの文により、コードの実行の流れを制御することができます。
# このコードでは、1から10までの数字を表示します。whilei<=10doputsii+=1end
while文の構文は以下の通り:
while条件do# 処理end
条件とは、ループの実行を継続するためにtrueと評価されなければならないブール値の式です。処理の部分は、条件が真である限って実行されるコードのブロックです。
まえの例では、条件は「i <= 10」です。これは、iの値が10以下である限り、ループが実行され続けることを意味します。処理の部分は、puts i行です。この行はiの値をコンソールに出力します。
while文は、特定の条件が満たされる限り、コードのブロックを繰り返すことができる強力なツールです。
while修飾子は、if修飾子に似ていますが、条件式が肯定されている間は式を評価し続ける構文です。
式while条件式
例えば、ある数値変数numが0未満の間、num に 2 を足し続けるプログラムは以下のように書けます。
num=-3num+=2whilenum<0
# このコードでは、10から1までの数字を表示します。i=10untili<=1doputsii-=1end
until文の構文は以下の通り:
until条件do# 処理end
条件とは、ループの実行を継続するためにfalseと評価されなければならないブール値の式です。処理の部分は、条件が偽である限って実行されるコードのブロックです。
まえの例では、条件はi <= 1である。これは、iの値が1以上である限り、ループが実行され続けることを意味します。処理の部分は、puts i行です。この行はiの値をコンソールに出力します。
until文は、特定の条件が満たされない限り、コードのブロックを繰り返すために使用できる強力なツールです。
until修飾子は、while修飾子に似ていますが、条件式が否定されている間は式を評価し続ける構文です。
式until条件式
例えば、ある数値変数numが0以上でない間、num に 2 を足し続けるプログラムは以下のように書けます。
num=-3num+=2untilnum>=0
# このコードでは、配列の要素を順に表示します。array=[2,3,5,7,11]forelementinarrayputselementendputselement#=> 11 ## forループを抜けてもスコープは終わらない
for文の構文は以下の通り:
forループ変数inコレクション# ループ変数にまつわる処理end
ループ変数は、コレクション内の現在の要素のプレースホルダーです。コレクションは、配列、範囲、その他の反復可能なオブジェクトのいずれでもかまいません。ループ変数にまつわる処理の部分は、コレクション内の各要素に対して実行されるコードのブロックです。
この例では、コレクションは配列[2, 3, 5, 7, 11]です。ループ変数はelementです。ループ変数にまつわる処理の部分は、puts element行です。この行は、elementの値をコンソールに表示します。
for文は、要素のコレクションを繰り返し処理するために使用できる強力なツールです。
Rubyには、反復制御を行う文が5つあります。
反復制御文は、Rubyのコードをより効率的に、より読みやすくするために使用できる強力なツールです。
break文は、現在のループを終了させます。
(1..).step(12)do|i|breakifi>=50putsiend#=> 1# 13# 25# 37# 49
このコードは、1から始まり、12ずつ増加する無限の数列を生成し、その数列の各要素をdo..endブロック内で処理しています。各要素は変数iに割り当てられ、puts文を使って出力されます。
しかし、break if i >= 50の部分によって、iが50以上になった場合、ループが終了します。つまり、ループは50未満の値のときだけ実行されます。
このコードは、1, 13, 25, 37, 49といった50未満の数列を出力します。そして、iが50以上の値になった時点でループが終了します。
next文は、ループの現在の反復をスキップさせます。
1.upto(10)do|i|nextifi==5putsiend#=> 1# 2# 3# 4# 6# 7# 8# 9# 10
このコードは、1から10までの数列を生成し、その数列の各要素をdo..endブロック内で処理しています。各要素は変数iに割り当てられ、puts文を使って出力されます。
next if i == 5の部分によって、iが5の場合、それ以降の処理をスキップして次の要素の処理に進みます。つまり、5は出力されません。
したがって、このコードは1, 2, 3, 4, 6, 7, 8, 9, 10という数列を出力します。5がスキップされ、それ以外の値が出力されます。
redo文は、現在のループの繰り返しを再度実行します。
count=0(1..10).eachdo|i|count+=1redoifi==5andcount<20puts"#{count}:#{i}"end#=> 1: 1# 2: 2# 3: 3# 4: 4# 20: 5# 21: 6# 22: 7# 23: 8# 24: 9# 25: 10
例外が発生した場合に、その処理をやり直します。
begin# 何らかの処理rescueSomeExceptionretryend
メソッド内でループを含む場合、returnを使ってループからも抜けることができます。以下はその例です。
defexample_method(1..10).eachdo|i|returnifi==5putsiendendexample_method
この場合、returnが呼び出されると、example_method自体が終了し、その場でループも終了します。
これらのキーワードやメソッドを使うことで、ループの挙動を制御することができます。
Rubyでは、反復処理を用いて演算を行うメソッドを反復処理メソッドと呼びます。反復処理とは、コードのブロックを指定された回数だけ、あるいは特定の条件が満たされるまで繰り返す処理のことです。
Rubyには、ArrayやHashなどのコレクションオブジェクトを簡単にイテレーションできる多数のメソッドがあります。以下はいくつかの例です:
# eachメソッドarray=[1,2,3]array.each{|x|putsx}# mapメソッドarray=[1,2,3]new_array=array.map{|x|x*2}# selectメソッドarray=[1,2,3]new_array=array.select{|x|x.even?}# reduceメソッドarray=[1,2,3]sum=array.reduce(0){|total,x|total+x}# each_with_indexメソッドarray=[1,2,3]array.each_with_index{|value,index|puts"#{index}:#{value}"}
上記の例では、以下のように動作しています。
これらのメソッドを使用することで、コレクションの要素に簡単にアクセスできます。また、each_with_indexを使用することで、要素のインデックスにも簡単にアクセスできます。
Rubyには、様々なメソッドやイテレータで使用される暗黙のブロック引数があります。これらは_1,_2 などの形式で表され、ブロック内で引数を明示的に書く必要がなくなります。代表的なものを以下に示します。暗黙のブロック引数はRuby 2.7からサポートされました。
each メソッドなどのイテレータでの使用:numbers=[1,2,3,4,5]numbers.each{|num|putsnum}# 通常の引数を使用する場合numbers.each{puts_1}# `_1` を使用することで、暗黙の引数として要素を取得hash={a:1,b:2,c:3}hash.each{|key,value|puts"#{key}:#{vale}"}hash.each{puts"#{_1}:#{_2}"}
map メソッドでの使用:doubled=numbers.map{|num|num*2}# 通常の引数を使用する場合doubled=numbers.map{_1*2}# `_1` を使用することで、暗黙の引数として要素を取得して計算
このように、暗黙のブロック引数は通常の引数の代替として使われ、ブロック内でのコードをより簡潔にすることができます。ただし、可読性の観点から、コードが複雑になりすぎないよう注意が必要です。
Rubyには、数値を連続して扱うための便利なメソッドがあります。その中でもupto、downto、stepはよく使われるものです。これらのメソッドについて解説します。
# uptoの例1.upto(5)do|i|putsiend# 実行結果: 1 2 3 4 5# downtoの例5.downto(1)do|i|putsiend# 実行結果: 5 4 3 2 1# stepの例1.step(10,2)do|i|putsiend# 実行結果: 1 3 5 7 9
上記の例では、以下のように動作しています。
は、第2引数に指定した数値が増加量として使用されます。この例では、2ずつ増加しているため、1, 3, 5, 7, 9という結果になっています。
過去の編集で、loopをloop文と紹介されていましたが、loopはKernelオブジェクトのメソッドで構文ではありません。
loopメソッドの使い方は以下の通り:
loopdo# 処理end
処理の部分は、無限に実行されるコードのブロックです。
# このコードは、永久に "This will print forever!" を表示します。loopdoputs"This will print forever!"end
この例では、処理パートはputs "This will print forever!"です。この行は、"This will print forever!"というメッセージをコンソールに無限に出力します。
enum=[1,2,3].eachputsenum.class#=> Enumeratorloopdoputsenum.nextend#=> 1# 2# 3
enumは[1, 2, 3].eachから生成されたEnumeratorです。Enumeratorは、eachメソッドをブロックなしで呼び出すことで生成され、配列内の各要素を反復処理することができます。
その後、loop構文が使われています。loop構文は、無限ループを作成するためのもので、ループ内のコードを繰り返し実行します。
ループ内では、enum.nextが呼び出されています。これにより、Enumeratorから次の要素が取得され、その要素がputsメソッドを使って出力されます。最初は1が出力され、次に2、そして3が出力されます。
しかし、3を出力した後、Enumeratorからはもう要素がないため、StopIteration例外が発生します。この例外がloop構文によって自動的に捕捉され、ループが終了します。
break、next、redo文もこれらのメソッドと一緒に使うことができます。
たとえば、次のようなものです:
array=[1,2,3]array.eachdo|element|putselementbreakifelement==2end
このコードでは、配列の要素のうち、要素2までを表示します。
array=[1,2,3]array.eachdo|element|putselementnextifelement==2end
このコードでは、配列の要素を、要素2をスキップして表示します。
array=[1,2,3]array.eachdo|element|putselementredoifelement==2end
このコードは、配列の要素を表示しますが、要素2を無限に表示します。
Rubyのイテレーションメソッドは、繰り返し処理を行うためのメソッドで、通常はブロックを引数に取ります。ブロックは、繰り返し処理を行うためのコードブロックで、メソッドに渡されたオブジェクトの要素に対して、一つずつ実行されます。
イテレーションメソッドに渡されるブロックには、引数を指定することができます。引数は、ブロック内で処理する要素を表します。引数を指定する方法には、以下の2つがあります。
array=[1,2,3]array.eachdo|x|putsxend# 出力結果: 1, 2, 3
array=[1,2,3]array.each{puts_1}# 出力結果: 1, 2, 3
このように、イテレーションメソッドは、ブロックを引数に取り、ブロック内で要素に対する処理を実行するため、とても柔軟性が高く、コードの再利用性を高めることができます。
通常は配列自身ですが、breakでループを中断すると nil を返します。この特徴を利用すると、ZigやPythonのループと結合したelseの様に「ループを完走したときだけ実行する処理」を記述できます。
primes=[](2..100).eachdo|i|primes.push(i)ifprimes.eachdo|prime|breakifi%prime==0endendpprimes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
Rubyにおける例外処理は、プログラムが想定外のエラーに遭遇した場合に、プログラムの強制終了を防ぐために使用されます。例外処理を使用すると、プログラムはエラーが発生しても継続して実行されるため、エラーに含まれる情報をログファイルに記録したり、エラーメッセージを表示したりすることができます。
以下は、Rubyで例外処理を行うための基本的な書式です。
begin# 例外が発生する可能性のある処理rescue[エラークラス=>変数]# エラークラスが発生した場合に実行する処理else# 例外が発生しなかった場合に実行する処理ensure# 必ず実行される処理end
例外処理の流れは、まずbeginブロック内で例外が発生する可能性のある処理を書きます。そして、rescueブロックで発生しうる例外の種類を指定し、例外が発生した場合に行う処理を書きます。elseブロックは、例外が発生しなかった場合に実行する処理を書く部分です。ensureブロックには、必ず実行される処理を書きます。
以下は、例外処理を用いたコードの例です。
defdivide(a,b)beginresult=a/brescueZeroDivisionError=>eputs"Error:#{e.message}"result=nilelseputs"Result is#{result}"ensureputs"Process finished"endreturnresultendputsdivide(10,0)putsdivide(10,2)
この例では、divideメソッド内で引数bが0である場合にZeroDivisionErrorが発生する可能性があるため、beginブロック内で計算を行っています。また、rescueブロックではZeroDivisionErrorを補足して、エラーメッセージを表示しています。elseブロックでは、計算結果を表示しています。ensureブロックでは、処理が必ず実行されるように設定しています。
このコードを実行すると、以下のような結果が得られます。
Error: divided by 0Process finishednilResult is 5Process finished5
Rubyでは、制御構造の修飾子版も提供されています。これは一行で短く書くことができ、可読性を向上させる場合があります。
制御構造の修飾子版には、以下のものがあります:
puts"Hello"ifflag
この場合、"Hello"はflagが真の場合にのみ出力されます。
puts"Hello"unlesscond
この場合、"Hello"はcondが偽の場合にのみ出力されます。
x=0putsx+=1whilex<5
この場合、ループが続行される条件が満たされている間、xの値がインクリメントされ、その値が出力されます。
x=0putsx+=1untilx==5
この場合、ループが続行される条件が満たされるまで、xの値がインクリメントされ、その値が出力されます。
Rubyのrescue修飾子は、単一行の式での例外処理を行うために使用されます。通常のbegin..rescue..endブロックと異なり、修飾子版はコードを短く保ち、よりコンパクトに書くことができます。
rescue修飾子の構文は以下の通りです:
式1rescue式2
式1が例外を発生させた場合、rescue修飾子はその例外をキャッチし、代わりに式2を評価します。
以下はrescue修飾子を使用した例です:
result=risky_operation()rescuedefault_value
この例では、risky_operationというメソッドを呼び出しますが、もし例外が発生した場合には、デフォルト値を代入することでエラーハンドリングを行っています。
rescue修飾子は、例外が発生する可能性のある単一の式を簡潔に記述する場合に便利ですが、複雑な例外処理を行う場合や、複数の式に対する例外処理を行う場合には、通常のbegin..rescue..endブロックを使用する方が適しています。
これらの修飾子版は、単純な条件の場合や短いコードブロックの場合に便利です。ただし、複雑な制御構造や複数の条件がある場合は、通常の制御構造を使用した方が可読性が高くなります。
Rubyのメソッドとは、ある一定の処理をまとめて名前をつけたもので、複数の場所で同じような処理を書く必要がある場合に便利です。
以下は、Rubyでメソッドを定義する方法の例です。
defメソッド名(仮引数)処理end
上記のコードでは、defキーワードを使ってメソッドを定義します。メソッド名は自分で決定し、仮引数は必要に応じて任意の数を指定できます。また、endキーワードでメソッドの終わりを示します。
以下は Ruby における関数定義における引数に関する機能の例です。
# 固定順位引数defgreet(name)puts"Hello,#{name}!"endgreet("Alice")# => "Hello, Alice!"greet("Bob")# => "Hello, Bob!"# 固定順位引数のデフォルト値defgreet_with_default(name="world")puts"Hello,#{name}!"endgreet_with_default# => "Hello, world!"greet_with_default("Alice")# => "Hello, Alice!"# キーワード引数defgreet_with_keyword(name:)puts"Hello,#{name}!"endgreet_with_keyword(name:"Alice")# => "Hello, Alice!"greet_with_keyword(name:"Bob")# => "Hello, Bob!"# キーワード引数のデフォルト値defgreet_with_keyword_default(name:"world")puts"Hello,#{name}!"endgreet_with_keyword_default# => "Hello, world!"greet_with_keyword_default(name:"Alice")# => "Hello, Alice!"# ブロック引数defgreet_with_block(name)puts"Hello,#{name}!"yieldendgreet_with_block("Alice"){puts"Glad to see you!"}# => "Hello, Alice!"# => "Glad to see you!"# 残余引数defgreet_with_remainder(*names)names.each{|name|puts"Hello,#{name}!"}endgreet_with_remainder("Alice","Bob","Charlie")# => "Hello, Alice!"# => "Hello, Bob!"# => "Hello, Charlie!"
greet 関数は固定順位引数を使用しています。greet_with_default 関数では、name のデフォルト引数が指定されています。greet_with_keyword 関数では、キーワード引数を使用しています。greet_with_keyword_default 関数では、キーワード引数のデフォルト引数が指定されています。greet_with_block 関数では、ブロック引数を使用しています。最後に、greet_with_remainder 関数は残余引数を使用しています。
これらの機能を駆使することで、より柔軟かつ読みやすいコードを作成することができます。
例えば、以下のようなメソッドを定義してみましょう。
defsay_hello(name)puts"Hello,#{name}!"end
このメソッドは、引数に渡した名前に対して「Hello, 〇〇!」というメッセージを出力します。引数の「name」には、任意の名前を指定することができます。
このメソッドを呼び出すためには、以下のように書きます。
say_hello("John")#=> Hello, John!
引数に指定した名前が、「name」の部分に代入され、putsメソッドによってメッセージが出力されます。
また、Rubyには引数を指定しないデフォルト値を設定する方法もあります。以下は、デフォルト値を設定したメソッドの例です。
defsay_hello(name="World")puts"Hello,#{name}!"end
この場合、引数を指定しなかった場合は「World」という名前が、引数を指定した場合は指定した名前が代入されます。例えば、以下のように呼び出します。
say_hello#=> Hello, World!say_hello("Mary")#=> Hello, Mary!
キーワード引数では、引数名を指定することができます。これにより、引数の並び順を気にすることなく、任意の引数を指定することができます。
defperson(name:,age:)puts"名前は#{name}、年齢は#{age}才です。"endperson(name:"山田太郎",age:30)#=> 名前は山田太郎、年齢は30才です。
キワード引数の実引数でに順序は任意です。
キーワード引数もディフォルト値を設定することができます。引数を指定しなかった場合は、初期値が使われるようになります。
defperson(name:"名無し",age:0)puts"名前は#{name}、年齢は#{age}才です。"endperson(name:"山田太郎")#=> 名前は山田太郎、年齢は0才です。
ブロック引数とは、メソッドの引数としてブロックを受け取るための仕組みです。ブロックを受け取るメソッドの引数リストの最後に、"&ブロック名"という形式の引数を指定することで、ブロックをメソッド内で扱うことができます。
以下に例を示します。
defmy_method(&block)block.callendmy_methoddoputs"Hello, World!"end# => "Hello, World!"
上記の例では、my_methodメソッドにブロックを渡しています。my_methodメソッド内では、ブロックを扱うための引数として"&block"を指定しており、ブロックを呼び出すために"block.call"を用いています。
yieldは、メソッドに渡されたブロックを実行するためのキーワードです。yieldを使用することで、メソッド内でブロックを実行することができます。
以下に例を示します。
defmy_methodyieldendmy_methoddoputs"Hello, World!"end# => "Hello, World!"
上記の例では、my_methodメソッド内でyieldを用いてブロックを実行しています。my_methodメソッドが呼び出されると、引数として渡されたブロックが実行されます。
ブロック引数の呼び出しやyieldは引数を渡すこともできます。
defmy_method(name,&block)block.call(name)endmy_method("John")do|name|puts"Hello,#{name}!"end# => "Hello, John!"
上記の例では、my_methodメソッドにブロックを渡し、ブロック引数に"name"を指定しています。ブロック内で引数を使用するために、block.callで"name"を渡しています。ブロック内では、引数として渡された"name"を使用して文字列を出力しています。
Procクラスは[]をオーバーロードしており。
defmy_method(name,&block)block[name]end
と書くことも出来ます。
defmy_method(name)yield(name)endmy_method("John")do|name|puts"Hello,#{name}!"end# => "Hello, John!"
上記の例では、my_methodメソッドに引数として"name"を渡し、yieldに"name"を引数として渡しています。ブロック内では、引数として渡された"name"を使用して文字列を出力しています。
再帰を行う場合、通常はブロック引数を使用する方が適切です。再帰的な関数では、ブロック引数を使用することで、再帰呼び出しのたびに異なるブロックを渡すことができます。これにより、再帰呼び出しのたびに異なる処理を行うことが可能になります。
例えば、再帰的なメソッドを定義する際には、ブロック引数を使用して次の再帰呼び出し時に実行される処理を指定します。
defrecursive_method(n,&block)ifn>0# ブロックを実行block[n]# 再帰呼び出しrecursive_method(n-1,&block)endendrecursive_method(5){|num|putsnum}
このように、ブロック引数を使うことで、再帰呼び出し時に実行される処理を柔軟に指定することができます。これにより、再帰メソッドがより汎用的で柔軟なものになります。
Rubyでは、メソッド内でブロックが渡されたかどうかを判定する方法があります。これを行うには、block_given?メソッドを使用します。このメソッドは、メソッド内でブロックが渡された場合にtrueを返し、それ以外の場合にfalseを返します。
以下は、block_given?メソッドを使用してブロックが渡されたかどうかを判定する例です。
defmethod_with_blockifblock_given?puts"ブロックが渡されました"# yield が使えますelseputs"ブロックが渡されていません"# yield は使えませんendendmethod_with_block{puts"Hello, block!"}# => ブロックが渡されましたmethod_with_block# => ブロックが渡されていません
このように、block_given?メソッドを使うことで、メソッド内でブロックが渡されたかどうかを簡単に判定することができます。
ブロック引数を受け取るメソッドの呼び出しで、ブロック引数を省略した場合の戻り値は、Enumeratorオブジェクトとする慣習があります。
例えば、配列をeachメソッドで呼び出した場合、ブロックが省略されるとEnumeratorオブジェクトが返ります。
array=[2,3,5,7]enum=array.eachputsenum.class# => Enumeratorbeginputsenum.nextwhiletruerescueStopIteration=>eputse.inspectend# => 2# 3# 5# 7# #<StopIteration: iteration reached an end>enum2=10.upto(15)loopdoputsenum2.nextend# => 10# 11# 12# 13# 14# 15# 一見無限ループになりそうですが# loop は StopIteration をキャッチすると静かに繰り返しを終了します。
このように、eachメソッドがブロック引数を省略された場合、戻り値はイテレータとして機能するEnumeratorオブジェクトとなります。このオブジェクトは、コレクションの各要素を順番に処理するために使用されます。
definorder_traversal(node=@root,&block)returnto_enum(__method__,node)unlessblockdefcore(node,&block)returnifnode.nil?core(node.left,&block)yieldnode.valuecore(node.right,&block)endcore(node,&block)selfend
Rubyにおける「残余引数(rest argument)」とは、メソッド定義の中で最後の引数として指定され、そのメソッドに渡された全ての追加の引数を1つの配列として受け取る機能です。残余引数は、メソッドが可変長の引数を受け取ることを可能にします。
残余引数は、メソッド定義の引数の前にアスタリスク * を付けて指定されます。これにより、メソッドが受け取る引数の数を動的に決定することができます。
以下は、残余引数を使用する例です。
defgreet(greeting,*names)names.each{|name|puts"#{greeting},#{name}!"}endgreet("Hello","Alice","Bob","Charlie")# 出力:# Hello, Alice!# Hello, Bob!# Hello, Charlie!
この例では、greet メソッドは greeting という通常の引数と、*names という残余引数を持っています。greet メソッドは最初の引数を挨拶の文字列として受け取り、残りの引数を配列 names として受け取ります。その後、names 配列内の各要素に対して、挨拶と名前を組み合わせたメッセージを出力します。
残余引数を使用することで、メソッドが任意の数の引数を受け取る柔軟性が向上し、可読性の高いコードを記述することができます。
メソッド内で最後に評価される値が、そのメソッドの戻り値になります。例えば、次のようなメソッドを考えてみましょう。
defadd(a,b)returna+bend
このメソッドは2つの引数を受け取り、それらの和を返します。return キーワードを使用して、明示的に戻り値を指定しています。このようにreturn を使わずに値を返すと、メソッドの最後に評価された値が自動的に戻り値として返されます。
defadd(a,b)a+bend
このように、明示的にreturn を書かなくても、メソッド内で最後に評価された値が自動的に戻り値となります。
さらに、1つの式からなる関数は= を使いend を伴わない構文で定義できます。
defadd(a,b)=a+b
メソッドを呼び出す際には、その戻り値を変数に代入したり、別のメソッドの引数として渡したりすることができます。戻り値が必要な場合や、逆に必要ではない場合など、メソッドの設計時にはしっかりと考慮して実装する必要があります。
Rubyには4つのスコープがあります。以下にそれぞれのスコープとコード例を示します。
deflocal_scopex=10putsx# output: 10endputsx# undefined local variable or method `x' for main:Object (NameError)
classPersondefinitialize(name)@name=nameenddefgreetputs"Hello,#{@name}!"endendperson=Person.new("Alice")person.greet# Hello, Alice!puts@name# undefined method `name' for main:Object (NameError)
classCounter@@count=0definitialize@@count+=1enddefself.count@@countendendcounter1=Counter.newcounter2=Counter.newputsCounter.count# 2
$global_var="I'm global"defcheck_global_varputs$global_varendcheck_global_var# I'm globaldefset_global_var$global_var="I'm changed globally"endset_global_varcheck_global_var# I'm changed globally
以上がRubyの4つのスコープです。
p( と) は、曖昧さのない場合は省略可能です。上の例は、puts(s)と書く代わりに、put s と書く事ができます。
s='Hello World!'putss
Hello World!
Rubyでは、メソッド内で再帰を行うことができます。以下は、再帰を行うシンプルな例です。
defcountdown(num)ifnum>0putsnumcountdown(num-1)elseputs"Blast off!"endendcountdown(5)
このコードでは、countdownメソッドが自分自身を呼び出して、カウントダウンを実行しています。呼び出し結果は以下の通りです。
5 4 3 2 1 Blast off!
このように、再帰を使うと、同じ処理を繰り返し実行することができます。
Rubyにはクロージャーという概念があります。クロージャーとは、変数とブロックを組み合わせたもので、後からブロック内で変数が参照された場合でも、当時の変数の値を保持している特殊なオブジェクトです。
以下は、クロージャーを使った例です。
defgreeting_proc(name)Proc.new{puts"Hello,#{name}!"}endhello=greeting_proc("Alice")hello.call# => "Hello, Alice!"changing_name="Bob"hello.call# => "Hello, Alice!" (value of name is retained)
上記の例では、greeting_procメソッドがブロックを返し、そのブロックがhello変数に代入されます。このブロック内で参照される変数nameの値が保持され、hello.callを複数回実行しても、最初に指定した名前が表示されます。
また、changing_nameという変数を新しく定義しても、クロージャー内で参照されるnameの値には影響がなく、hello.callを実行すると、"Hello, Alice!"が表示されます。
Rubyにおけるクラスは、オブジェクト指向プログラミングにおいて重要な役割を持ちます。クラスは、同じ構造や属性を持つオブジェクトの集合を定義するもので、それぞれのオブジェクトはクラスのインスタンスであると考えることができます。
# クラス定義classMyClass# クラス変数@@class_variable="Class Variable"# インスタンス変数attr_accessor:instance_variable# コンストラクターdefinitialize(arg)@instance_variable=argend# クラスメソッドdefself.class_methodputs@@class_variableend# インスタンスメソッドdefinstance_methodputs@instance_variableendend# クラスの利用my_object=MyClass.new("Instance Variable")MyClass.class_method#=> "Class Variable"my_object.instance_method#=> "Instance Variable"my_object.instance_variable="New Instance Variable"my_object.instance_method#=> "New Instance Variable"
クラス定義はclassキーワードを使い、インスタンス変数を作成するためにattr_accessorを使っています。コンストラクターはinitializeメソッドを使い、引数を受け取ってインスタンス変数を初期化します。
クラスメソッド、インスタンスメソッドはそれぞれdef self.method_name、def method_nameの形式で定義されます。また、クラス変数は@@で始められ、インスタンス変数は@で始められます。
このように、Rubyのクラスを使用することで、簡単にオブジェクト指向プログラミングを実現することができます。
Rubyには3種類のアクセス修飾子があります。
以下は、それぞれのアクセス修飾子を使用した例です。
classAnimaldefswim# publicputs"I can swim!"endprotecteddefrun# protectedputs"I can run!"endprivatedeffly# privateputs"I can fly!"endendclassCat<Animaldefcan_runrun# protectedなのでAnimalクラスのサブクラス内から呼び出し可能enddefcan_flyfly# privateなので呼び出し不可能endendcat=Cat.newcat.swim# publicなので呼び出し可能cat.can_run# protectedなので呼び出し可能cat.can_fly# privateなので呼び出し不可能
上記の例では、Animalクラスにswim、run、fly メソッドを定義しています。swimメソッドはpublic、run メソッドはprotected、fly メソッドはprivate となっています。
また、Cat クラスをAnimal クラスのサブクラスとして定義し、can_runメソッドで、Animalクラスでprotected として定義したrun メソッドを呼び出しています。一方で、can_fly メソッドでprivate として定義したfly メソッドを呼び出しているため、エラーが発生しています。
# 親クラスclassAnimaldefspeakputs"動物が鳴いています"endend# 子クラスclassDog<Animaldefspeakputs"わんわん"endend# 子クラスclassCat<Animaldefspeakputs"にゃーにゃー"endend# 子クラスのインスタンス化dog=Dog.newcat=Cat.new# 子クラスのメソッドを呼び出しdog.speak#=> わんわんcat.speak#=> にゃーにゃー# 親クラスのメソッドも呼び出し可能dog.superclass#=> Animaldog.superclass.superclass#=> Object(親クラスの親クラス)
この例では、Animal という親クラスを作成し、Dog とCat という子クラスを作成しています。Dog とCat はそれぞれspeak メソッドを持っており、それぞれ異なる出力をします。
Dog とCat はAnimal クラスを継承しています。このため、Dog とCat もAnimal クラスのspeak メソッドを呼び出すことができます。
以下はRubyにおける演算子オーバーロードの例です。
classPersonattr_accessor:name,:agedefinitialize(name,age)@name=name@age=ageenddef+(other)Person.new("#{self.name}#{other.name}",self.age+other.age)enddef==(other)self.name==other.name&&self.age==other.ageendendperson1=Person.new("John",30)person2=Person.new("Doe",40)# カスタム演算子 '+' の利用person3=person1+person2putsperson3.name#=> John Doeputsperson3.age#=> 70# カスタム演算子 '==' の利用putsperson1==person3#=> falseperson4=Person.new("John Doe",70)putsperson3==person4#=> true
classMyClassdefinitialize(name)@name=nameendendmy_object=MyClass.new("Bob")classMyClassdefgreet()puts"Hello,#{@name}!"endendmy_object.greet#=> "Hello, Bob!"
注目すべきは:
の2点です。
Rubyでは、オブジェクトに固有のメソッドを定義することができます。これを特異メソッドと呼びます。通常、メソッドはクラス定義内で定義されますが、特異メソッドは特定のオブジェクトに対して定義されます。
特異メソッドを定義するには、以下のように書きます。
オブジェクト名.define_singleton_method(シンボル)do|引数|# メソッドの中身end
例えば、以下のように書くことで、特定のオブジェクトにhelloメソッドを定義することができます。
str="Hello, world!"str.define_singleton_method(:hello)doputs"Hello!"end
これで、strオブジェクトにhelloメソッドが定義されました。呼び出すには以下のようにします。
str.hello#=> "Hello!"
Rubyでは、オブジェクトに対して特異クラスというものが存在します。これは、そのオブジェクトだけが持つクラスのことです。この特異クラスに対して特異メソッドを定義することで、そのオブジェクトだけにメソッドを追加できます。
特異クラスは、以下のように記述します。
class<<オブジェクト名# メソッドの定義end
例えば、以下のように記述することで、上記のstrオブジェクトに対してメソッドを定義することができます。
class<<strdefgoodbyeputs"Goodbye!"endend
これで、strオブジェクトにgoodbyeメソッドが定義されました。呼び出すには以下のようにします。
str.goodbye#=> "Goodbye!"
以上が、Rubyの特異メソッドについてのチュートリアルです。オブジェクトに対して固有のメソッドを定義することで、便利なプログラミングをすることができます。
Go/メソッドとインターフェースの都市間の大圏距離を求めるメソッドを追加した例を、Rubyに移植しました。
classGeoCoordattr_accessor:longitude,:latitudedefinitialize(longitude,latitude)@longitude,@latitude=longitude,latitudeenddefto_s()ew,ns="東経","北緯"long,lat=@longitude,@latitudeew,long="西経",-longiflong<0.0ns,lat="南緯",-latiflat<0.0"(#{ew}:#{long},#{ns}:#{lat})"enddefdistance(other)i,r=Math::PI/180,6371.008Math.acos(Math.sin(@latitude*i)*Math.sin(other.latitude*i)+Math.cos(@latitude*i)*Math.cos(other.latitude*i)*Math.cos(@longitude*i-other.longitude*i))*rendenddefGeoCoord(longitude,latitude)GeoCoord.new(longitude,latitude)endSites={"東京駅":GeoCoord(139.7673068,35.6809591),"シドニー・オペラハウス":GeoCoord(151.215278,-33.856778),"グリニッジ天文台":GeoCoord(-0.0014,51.4778),}Sites.each{|name,gc|puts"#{name}:#{gc}"}keys,len=Sites.keys,Sites.sizekeys.each_with_index{|x,i|y=keys[(i+1)%len]puts"#{x} -#{y}:#{Sites[x].distance(Sites[y])} [km]"}
東京駅: (東経: 139.7673068, 北緯: 35.6809591)シドニー・オペラハウス: (東経: 151.215278, 南緯: 33.856778)グリニッジ天文台: (西経: 0.0014, 北緯: 51.4778)東京駅 - シドニー・オペラハウス: 7823.269299386704 [km]シドニー・オペラハウス - グリニッジ天文台: 16987.2708377249 [km]グリニッジ天文台 - 東京駅: 9560.546566490015 [km]
このコードは、GeoCoordというクラスを定義し、そこに緯度と経度を持つ「地球上の位置」を表現しています。それを利用して、Sitesというディクショナリに都市名と位置を紐付けています。
このコードのメインの処理は、各都市に対して、その距離を計算して出力しています。すなわち、都市の位置情報から、地球上での距離を求めることができます。
この「地球上の距離を求める」処理は、distanceメソッドで定義されており、球面三角法を使って計算されています。具体的には、2点の緯度経度から、その2点間の地表面上の距離を求める数式を用いています。
また、都市名と位置情報を紐付けたSitesに対しては、各都市の位置を出力しています。それぞれの都市の位置は、東経か西経か、北緯か南緯かで表され、丸括弧でくくられ、「東京駅」の場合は(東経: 139.7673068, 北緯: 35.6809591)という文字列が出力されます。
JavaScript/クラス#包含と継承を、Rubyに移植しました。
classPointdefinitialize(x=0,y=0)@x,@y=x,yenddefinspect()="x:#{@x}, y:#{@y}"defmove(dx=0,dy=0)@x,@y=@x+dx,@y+dyselfendendclassShapedefinitialize(x=0,y=0)@location=Point.new(x,y)enddefinspect()=@location.inspect()defmove(x,y)@location.move(x,y)selfendendclassRectangle<Shapedefinitialize(x=0,y=0,width=0,height=0)super(x,y)@width,@height=width,heightenddefinspect()="#{super()}, width:#{@width}, height:#{@height}"endrct=Rectangle.new(12,32,100,50)p["rct=",rct]p['rct.instance_of?( Rectangle ) => ',rct.instance_of?(Rectangle)]p['rct.instance_of?( Shape ) => ',rct.instance_of?(Shape)]p['rct.instance_of?( Point ) => ',rct.instance_of?(Point)]rct.move(11,21)p["rct=",rct]
["rct=", x:12, y:32, width:100, height:50]["rct.instance_of?( Rectangle ) => ", true]["rct.instance_of?( Shape ) => ", false]["rct.instance_of?( Point ) => ", false]["rct=", x:23, y:53, width:100, height:50]
このコードは、Rubyでオブジェクト指向プログラミングを用いた簡単な図形クラスの例です。
Pointクラスは、座標を表現するためのクラスであり、xとyの2つのインスタンス変数を持っています。 コンストラクタで、xとyの初期値が設定できます。inspectメソッドは、オブジェクトを文字列に変換するために使用されます。moveメソッドにより、(dx, dy)だけ点を移動することができます。
Shapeクラスは、点の座標を持つ図形を表すためのクラスであり、initializeメソッド内で、Pointクラスのオブジェクトを作成して、点の座標を設定します。inspectメソッドは、点の座標を返します。moveメソッドによって、図形を(dx, dy)だけ移動することができます。
Rectangleクラスは、Shapeクラスを継承しています。コンストラクタで、左上の点の座標と幅と高さを設定できます。inspectメソッドは、左上の点の座標と、幅と高さを表現する文字列を返します。
最後の行では、Rectangleクラスのインスタンスrctを作成し、その後、rctがRectangleクラス、Shapeクラス、Pointクラスのどれに対してインスタンスかを調べるようにしています。また、rctの位置を(11, 21)だけ移動させます。
Rubyの高階関数とは、関数を引数や戻り値として扱うことができる関数のことを指します。これにより、コードの再利用性が向上し、より柔軟なプログラミングが可能になります。
例えば、以下のような高階関数があります。
defmanipulate_array(arr,func)arr.map(&func)end
この関数は、配列と関数を受け取り、配列の各要素に対してその関数を適用した新しい配列を返します。このように、関数自体を引数として渡せることで、どのような処理でも適用することができます。
また、RubyのProcオブジェクトを使うことで、無名関数を定義して高階関数に渡すこともできます。例えば、以下のようなコードです。
add_num=Proc.new{|x|x+1}putsmanipulate_array([1,2,3],add_num)# => [2, 3, 4]
このように、Procオブジェクトを定義して関数に渡すことで、より柔軟なプログラミングが可能になります。
Rubyのlambdaとは、無名関数(関数を名前を付けずに定義すること)の一種であり、関数をオブジェクトとして扱うことができます。
以下は、lambda式の基本構文です。
lambda{|引数|処理}
lambdaには引数を渡すことができ、渡された引数を処理に渡すことができます。また、処理の中でreturn を使うことができ、lambdaの戻り値として処理結果を返すことができます。以下が、lambdaを利用した簡単な例です。
# lambda式を変数に代入するadd=lambda{|x,y|x+y}# lambda式の引数の数putsadd.arity#=> 2# lambda式を直接呼び出すputslambda{|x,y|x+y}.call(2,3)#=> 5# lambda式を変数に代入したものを呼び出すputsadd.call(2,3)#=> 5# ブラケット記法による呼び出しputsadd[2,3]#=> 5
Ruby 1.9 からは
->(引数){処理}
のような構文が追加され
# lambda式を変数に代入するadd=->(x,y){x+y}# lambda式の引数の数putsadd.arity#=> 2# lambda式を直接呼び出すputs->(x,y){x+y}[2,3]#=> 5
とも書けるようになりました。
以上が、Rubyのlambdaの基本的な使い方です。
RubyのProcとlambdaは、両方とも無名関数を定義するための方法ですが、いくつかの違いがあります。
引数の扱い方 Procの場合、渡された引数の数が関係なく、無視されます。一方、lambdaの場合は、正確な数の引数が期待されます。引数が足りない場合はエラーが発生します。
return文の扱い方 Procの場合、return文は元の呼び出し元に戻りますが、そのブロック内の後続のコードも実行されます。lambdaの場合、return文はただちにブロックから返され、コードの後続部分は実行されません。
以下は、Procとlambdaの振る舞いの違いを示す例です。
defproc_testp=Proc.new{return"Proc"}p.callreturn"proc_test method"enddeflambda_testl=lambda{return"Lambda"}l.callreturn"lambda_test method"endputsproc_test# 出力結果: Procputslambda_test# 出力結果: lambda_test method
この例では、Procはブロック内でreturn文を呼び出していますが、lambdaは呼び出していません。そのため、Procはreturn文で元の呼び出し元に戻って、その後のコードも実行されなくなっています。一方、lambdaはreturn文を呼び出すことがないので、lambda_testメソッドが正常に終了します。
Rubyには以下のような並行処理の仕組みが提供されています。
なお、Rubyには以上の他にも、ThreadクラスやFiberクラスと同様に、GVL(Global VM Lock)によってスレッドセーフに実行されるイベントマシンであるEventMachineや、並行処理のためのツールキットであるCelluloidなどが存在します。
Rubyにおけるスレッドは、軽量な並列処理を実現するための機能であり、プログラム内で独立して動作する変数やメソッドの集合を表します。スレッドは、複数のタスクを同時に処理することで、CPUを最大限活用することができます。
スレッドは、Threadクラスを使用して作成することができます。Thread.newメソッドを使用して、新しいスレッドを作成した後、startメソッドを呼び出すことでスレッドを開始します。また、Thread.currentメソッドを使用することで、現在のスレッドを取得することができます。
以下は、スレッドを使用した簡単な例です。
t1=Thread.new{# スレッド1の処理puts"Thread 1 started"sleep(2)puts"Thread 1 ended"}t2=Thread.new{# スレッド2の処理puts"Thread 2 started"sleep(1)puts"Thread 2 ended"}t1.joint2.join
この例では、スレッド1とスレッド2が開始されます。それぞれのスレッドは、sleepメソッドを使用して一定時間待機した後、メッセージを出力します。joinメソッドを呼び出すことで、スレッドが完了するまでプログラムの実行をブロックします。
Rubyにおけるスレッドには、マルチスレッドに関する問題がいくつか存在します。例えば、同一のリソースにアクセスする複数のスレッドによる競合状態や、デッドロックに陥ってしまう可能性があります。これらの問題を回避するため、適切なロック処理や排他制御が必要な場合があります。
Rubyのファイバー(Fiber)は、擬似並行処理を実現するための仕組みです。標準的なスレッドと異なり、Rubyのファイバーはコルーチン的な動作をし、明示的に制御を渡す必要があります。
ファイバーは、Ruby 1.9以降から標準機能として組み込まれています。ファイバーを利用することで、特定の処理を中断したあと再開したり、独自のスケジューリングに基づいた処理を実現したりすることができます。
ファイバーは以下のように作成できます。
fiber=Fiber.newdoputs"start"Fiber.yieldputs"end"end
このコードでは、Fiber.newメソッドにブロックを渡してファイバーを作成します。puts "start"を実行した後、Fiber.yieldで処理を中断しています。この時点でプログラムは停止しており、puts "end"はまだ実行されていません。
ファイバーを再開するには、Fiber#resumeメソッドを呼び出します。
fiber.resume
このコードを実行すると、puts "start"が表示された後にプログラムが停止します。その後、再びfiber.resumeを実行すると、puts "end"が表示されます。
このように、ファイバーを利用することで、特定の処理を中断してから再開することができます。また、ファイバーを複数作成し、独自のスケジューリングに基づいた処理を行うことができます。
以下は、複数のファイバーを作成し、それぞれの処理を交互に実行する例です。
f1=Fiber.newdoloopdoputs"1"Fiber.yieldendendf2=Fiber.newdoloopdoputs"2"Fiber.yieldendendf1.resumef2.resumef1.resumef2.resume# ...
このように、ファイバーを利用することで、擬似的な並行処理を実現することができます。しかし、ファイバーはスレッドと異なり、OSレベルでのスケジューリングを行わないため、適切な制御を行わないとデッドロックや競合状態などの問題が生じることがあります。注意して使用する必要があります。
Rubyには、プロセスを管理するための Process モジュールが用意されています。このモジュールを使用することで、現在のプロセスや子プロセスを管理することができます。以下に、Processモジュールの主な機能をコードを交えて解説します。
Process モジュールに用意されている、 Process.pid メソッドを使用することで、現在のプロセスのIDを取得することができます。以下は、 Process.pid を使用して現在のプロセスIDを取得し、表示する例です。
puts"現在のプロセスID:#{Process.pid}"
Process モジュールには、新しいプロセスを生成するためのメソッドが複数用意されています。ここでは、代表的な2つのメソッドを紹介します。
Process.spawn メソッドを使用することで、外部プログラムを実行する新しいプロセスを生成することができます。以下は、 ls コマンドを実行する新しいプロセスを生成する例です。
pid=Process.spawn("ls")
fork メソッドを使用することで、現在のプロセスの子プロセスを生成することができます。 fork メソッドは、呼び出し元のプロセスからコピーされた新しいプロセスを生成します。以下は、 fork メソッドを使用して子プロセスを生成し、子プロセス内で puts メソッドを呼び出す例です。
pid=forkifpid.nil?puts"子プロセスのID:#{Process.pid}"elseputs"親プロセスのID:#{Process.pid}, 子プロセスのID:#{pid}"end
生成したプロセスを適切に終了することも重要です。以下は、プロセスを終了するためのメソッドを紹介します。
Process.kill メソッドを使用することで、指定したシグナルを送信してプロセスを終了することができます。以下は、 kill メソッドを使用して、先程生成した ls コマンドを実行しているプロセスを終了する例です。
pid=Process.spawn("ls")puts"新しいプロセスのID:#{pid}"sleep1Process.kill("TERM",pid)puts"プロセスを終了しました"
このように、Rubyの Process モジュールを使用することで、プロセスの生成や管理を容易に行うことができます。プロセスを扱う場合は、適切な終了処理も行うようにしましょう。
プログラミング言語Rubyの大きな魅力の1つは、豊富なライブラリが用意されていることです。Rubyには標準添付ライブラリとgemと呼ばれるパッケージ管理システムを通じて配布されるライブラリがあり、開発者はこれらのライブラリを活用することで、効率的で堅牢なプログラミングが可能になります。
ライブラリ編では、Rubyにおけるライブラリの概念と、主要なライブラリの機能およびユースケースを解説します。ライブラリのインストール方法から、具体的なコード例に至るまでカバーし、Rubyでのライブラリ活用の第一歩を支援します。
Rubyでは、数値、文字列、正規表現、シンボル、配列、ハッシュ、範囲などのオブジェクトを直接記述する方法としてリテラル表記が用意されています。
rを補い、複素数は虚数単位iを伴い表記します。420o1770xbad3.14123e4555/7r3+4i
"や'で囲んで記述します。"Hello, World!"'Ruby is fun'
/で開始し/で終了する形式で記述します。/ruby//^start/
:から始まる名前で表します。:symbol:name
[]の中に要素をカンマ区切りで記述します。[1,2,3,4,5]["apple","banana","orange"]
{}の中にキーと値をハッシュロケット=>で対応付けて記述します。{"name"=>"Jane","age"=>25}{:name=>"John",:age=>30}
{name:"John",age:30}
..または...で囲んで記述します。(1..5)# 1から5まで("a"..."d")# "a"から"c"まで
これらのリテラル表記は、対応するクラスのオブジェクトを直接生成するための簡潔な記法となっています。また、配列やハッシュの中でもリテラルを入れ子にすることができます。オブジェクトの初期化を手軽に記述できるため、Rubyのコードの可読性が高まります。
RubyのNumericは、数値を表す抽象クラスです。具体的な数値の型、例えば整数(Integer)や浮動小数点数(Float)などは、このNumericクラスのサブクラスとして実装されています。
Numericクラスは、数値演算や比較などの基本的な操作を提供します。例えば、加算、減算、乗算、除算、比較演算子(<、<=、==、>、>=)などがこのクラスで定義されています(ただし複素数に大小関係は定義できないので大小比較演算子も定義されません)。また、単項の加算と減算を表す+@や-@といったメソッドも定義されています。
このクラスは、Rubyの数値型の共通の振る舞いを定義するための基盤となっています。そのため、IntegerやFloatなどの具体的な数値型が共通のメソッドや振る舞いを持てるように、Numericクラスでこれらのメソッドが定義されています。数値演算や比較などの一般的な操作に加えて、Numericクラスは他の数値型間での演算を可能にするためのメソッドも提供しています。例えば、coerceメソッドを使うことで、異なる数値型同士での演算が可能になります。
Rubyには整数を扱うためのIntegerクラスがあります。
Integerクラスは、以下のような数値リテラルで整数値を表現することができます。
123#=> 1230b1111#=> 150o777#=> 5110xFF#=> 255
また、整数の四則演算や比較演算子なども、Integerクラスで定義されています。
1+2#=> 33-4#=> -15*6#=> 307/8#=> 09%4#=> 11==1#=> true2<3#=> true4>5#=> false
Rubyには浮動小数点数を扱うためのFloatクラスがあります。Floatクラスは、IEEE 754のbinary64をRubyのクラス化したものです。
以下のような数値リテラルで浮動小数点数を表現することができます。
1.23#=> 1.231.2e3#=> 1200.0-4.56#=> -4.564E10#=> 40000000000.0
また、浮動小数点数の四則演算や比較演算子なども、Floatクラスで定義されています。
1.2+3.4#=> 4.65.6-7.8# -2.19999999999999979.0*1.2# 10.83.4/5.6# 0.60714285714285711.2==1.2#=> true2.3<4.5# true6.7>8.9# false1.0/0.0#=> Infinity1.0/-0.0# -Infinity0.0/0.0# NaN
Rubyのcoerceメソッドは、数値演算の際に異なる型のオブジェクトを同じ型に変換するために使用されます。一般的には、coerceメソッドは二項演算子(たとえば+、-、*、/など)が呼び出されたときに使用されます。
例えば、1 + 2.0の場合、整数型の1と浮動小数点数型の2.0が加算されます。このとき、Rubyは1をFloat型に変換してから加算を行います。この変換はcoerceメソッドによって行われます。
具体的には、1 + 2.0の場合、Rubyは以下のように処理を行います:
1がFloat型の2.0に合わせるために、1.coerce(2.0)を呼び出します。coerceメソッドは、[1.0, 2.0]という配列を返します。これは1がFloat型に変換された結果です。1.0 + 2.0(1.coerce(2.0).reduce(&:+))を計算し、結果の3.0を返します。このように、coerceメソッドは異なる型のオブジェクトを同じ型に変換して演算を行う際に使用されます。
RubyのRationalクラスは、有理数を表現するためのクラスです。有理数は、整数のペアとして表現されます:a/b(b>0)、ここでaは分子であり、bは分母です。数学的には、整数aは有理数a/1と等価です。
以下は、Rationalクラスのインスタンスを作成する方法の例です。
p1r#=> (1/1)p3/4r# (3/4)pRational(1)# (1/1)pRational(3,4)# (3/4)pRational(0.3)# (5404319552844595/18014398509481984)pRational('0.3')# (3/10)pRational(3)/10# (3/10)p3.to_r/10# (3/10))
Rationalクラスのインスタンスは、有理数の四則演算や比較演算を行うことができます。
a=2/3rb=4/5rpa+b#=> (22/15)pa-b# (-2/15)pa*b# (8/15)pa/b# (10/9)pa==b#=> falsepa!=b# truepa>b# falsepa>=b# falsepa<=>0.0/0.0#=> nil
RubyのComplexクラスは、複素数を表現するためのクラスです。このクラスを使用すると、複素数を直交座標形式または極座標形式で作成し、演算を行うことができます。
直交座標形式の複素数は、実数部と虚数部のペアで表されます。以下は、複素数を直交座標形式で作成する方法の例です。
c1=3+4iC2=Complex.rect(3,4)c3=Complex(3,4)c4='3 + 4i'.to_cc5=3+4*Complex::I
c1 = 3 + 4i: この行では、虚数単位i を使って直接複素数を表現しています。3 は実部であり、4 は虚部です。c2 = Complex.rect(3, 4): ここでは、Complex クラスのrect メソッドを使用して複素数を作成しています。これは、実部と虚部を指定する方法です。c3 = Complex(3, 4): この行では、Complex クラスのコンストラクタを使用して複素数を作成しています。引数として実部と虚部を指定します。c4 = '3 + 4i'.to_c: ここでは、文字列'3 + 4i' を複素数に変換しています。to_c メソッドは、文字列が複素数の形式である場合に使用できます。c5 = 3 + 4 * Complex::I: 最後の行では、Complex::I 定数を使って複素数を作成しています。Complex::I は虚数単位を表します。極座標形式の複素数は、絶対値(大きさ)と偏角で表されます。以下は、複素数を極座標形式で作成する方法の例です。
Complex.polar(5,Math::PI/4)# 絶対値が5、偏角がπ/4の複素数を作成
これらの方法を使って、異なる形式の入力を扱い、複素数を生成することができます。
直交座標形式と極座標形式の相互変換
c=Complex(3,4)pc.real# 3pc.imaginary# 4pc.rect# [3, 4]pc.polar# [5.0, 0.9272952180016122]z=pComplex.polar(10,Math::PI/4)pz.real# 7.0710678118654755pz.imag# 7.071067811865475
Complex クラスでは、複素数の演算もサポートされています。例えば、加算や乗算、除算などが可能です。
a=Complex(2,3)b=Complex(4,5)c=a+b# 6 + 8id=a-b# -2 - 2ie=a*b# -7 + 22if=a/b# 0.5609756097560976 + 0.0487804878048781i
また、複素数同士の比較も可能です。== 演算子を用いることで、2つの複素数が等しいかを比較することができ、!= 演算子を用いることで、2つの複素数が等しくないを比較することができます。
a=Complex(2,3)b=Complex(2,3)putsa==b# trueputsa!=b# false
複素数の一致・不一致は定義されますが、大小関係はないので<,>,<=,>= や<=> は定義されていません
複素数の演算や比較において、実数と複素数の演算や比較も可能です。この場合、実数は複素数の虚数部が0のものとして扱われます。例えば、以下のような計算が可能です。
a=Complex(2,3)b=4c=a+b# 6 + 3id=a*b# 8 + 12ie=a>b# true
Numericと、そのサブクラスのメソッドはの実装は均質ではなく、整数だけがビット演算が可能であったり、複素数だけが大小の比較が出来なかったりします。
このため実装状況を調べたいのですが、手でテストケースを書くのも手数がかかるので、自動的に表にまとめるプログラムを書いてみました。
defbuild_table(types)headers=types.map(&:to_s)objects=types.mapdo|t|eval("#{t}.new")rescueStandardErroreval("#{t}(0)")end# メソッドの和集合を求めるall_methods=objects.map(&:methods).flatten.sort.uniqall_methods-=Object.methodsall_methods+=Comparable.methodsall_methods.sort!all_methods.uniq!method_str_width=all_methods.map(&:to_s).map(&:length).max# ヘッダーhead=['Method'.rjust(method_str_width),*headers.map{_1.center8}].join(' ')# 各オブジェクトについて、メソッドが存在するかどうかを確認し表を構築するtable=''index=0all_methods.eachdo|method|nextunlessobjects.any?{|obj|obj.methods.include?method}table<<"#{head}\n#{'-'*head.length}\n"if(index%10).zero?table<<method.to_s.rjust(26)<<' 'table<<objects.mapdo|obj|(obj.methods.include?(method)?'o':'-').center(8)end.join(' ')table<<"\n"index+=1endputstableendbuild_table([Numeric,Integer,Float,Rational,Complex])
Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- ! o o o o o != o o o o o !~ o o o o o % o o o o - & - o - - - * - o o o o ** - o o o o + - o o o o +@ o o o o o - - o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- -@ o o o o o / - o o o o < o o o o - << - o - - - <= o o o o - <=> o o o o o == o o o o o === o o o o o =~ o o o o o > o o o o - Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- >= o o o o - >> - o - - - [] - o - - - ^ - o - - - __id__ o o o o o __send__ o o o o o abs o o o o o abs2 o o o o o allbits? - o - - - angle o o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- anybits? - o - - - arg o o o o o between? o o o o - bit_length - o - - - ceil o o o o - chr - o - - - clamp o o o o - class o o o o o clone o o o o o coerce o o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- conj o o o o o conjugate o o o o o define_singleton_method o o o o o denominator o o o o o digits - o - - - display o o o o o div o o o o - divmod o o o o - downto - o - - - dup o o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- enum_for o o o o o eql? o o o o o equal? o o o o o even? - o - - - extend o o o o o fdiv o o o o o finite? o o o o o floor o o o o - freeze o o o o o frozen? o o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- gcd - o - - - gcdlcm - o - - - hash o o o o o i o o o o - imag o o o o o imaginary o o o o o infinite? o o o o o inspect o o o o o instance_eval o o o o o instance_exec o o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- instance_of? o o o o o instance_variable_defined? o o o o o instance_variable_get o o o o o instance_variable_set o o o o o instance_variables o o o o o integer? o o o o o is_a? o o o o o itself o o o o o kind_of? o o o o o lcm - o - - - Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- magnitude o o o o o method o o o o o methods o o o o o modulo o o o o - nan? - - o - - negative? o o o o - next - o - - - next_float - - o - - nil? o o o o o nobits? - o - - - Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- nonzero? o o o o o numerator o o o o o object_id o o o o o odd? - o - - - ord - o - - - phase o o o o o polar o o o o o positive? o o o o - pow - o - - - pred - o - - - Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- prev_float - - o - - private_methods o o o o o protected_methods o o o o o public_method o o o o o public_methods o o o o o public_send o o o o o quo o o o o o rationalize - o o o o real o o o o o real? o o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- rect o o o o o rectangular o o o o o remainder o o o o - remove_instance_variable o o o o o respond_to? o o o o o round o o o o - send o o o o o singleton_class o o o o o singleton_method o o o o o singleton_method_added o o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- singleton_methods o o o o o size - o - - - step o o o o - succ - o - - - taint o o o o o tainted? o o o o o tap o o o o o then o o o o o times - o - - - to_c o o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- to_enum o o o o o to_f - o o o o to_i - o o o o to_int o o o o o to_r - o o o o to_s o o o o o truncate o o o o - trust o o o o o untaint o o o o o untrust o o o o o Method Numeric Integer Float Rational Complex ----------------------------------------------------------------------- untrusted? o o o o o upto - o - - - yield_self o o o o o zero? o o o o o | - o - - - ~ - o - - -
Rubyでは、文字列(String)はダブルクォート(")で囲まれた文字列リテラルで表します。シングルクォート(')で囲まれた文字列リテラルは、単なる文字列であり、式の展開やエスケープシーケンスの展開が行われません。
name="Alice"puts"Hello,#{name}!"#=> "Hello, Alice!"puts'Hello,#{name}!'#=> "Hello, \#{name}!"
Rubyでは、ダブルクォートで囲まれた文字列リテラルの中で式を展開することができます。式を展開する場合は、#{}を使います。式を#{}で囲んで文字列中に埋め込むことができます。
x=10puts"xの値は#{x}です"#=> "xの値は10です"puts"xの二乗は#{x**2}です"#=> "xの二乗は100です"
また、式を展開する場合は、式が評価されてから文字列中に埋め込まれます。そのため、式の中で定義された変数などの値も展開されます。
x=10puts"xの値は#{x}です"#=> "xの値は10です"x=20puts"xの値は#{x}です"#=> "xの値は20です"
なお、シングルクォートで囲まれた文字列リテラルでは、式の展開は行われず、文字列中にそのまま#{...}が表示されます。
x=10puts'xの値は#{x}です'#=> "xの値は\#{x}です"
以下がRubyで使用できる主なエスケープシーケンスです。
| エスケープシーケンス | 意味 |
|---|---|
| \\ | バックスラッシュ |
| \' | シングルクォート |
| \" | ダブルクォート |
| \a | ベル音(BEL) |
| \b | バックスペース |
| \f | 改ページ(FF) |
| \n | 改行 |
| \r | キャリッジリターン(CR) |
| \t | 水平タブ |
| \v | 垂直タブ |
| \e | エスケープ文字(ESC) |
| \s | 空白 |
| \nnn | 8進数表記の文字コード(nは0〜7の数字) |
| \xnn | 16進数表記の文字コード(nは0〜9またはA〜F) |
| \unnnn | Unicode文字コード(nは0〜9またはA〜F) |
| \Unnnnnnnn | Unicode文字コード(nは0〜9またはA〜F) |
なお、Rubyではダブルクォートで囲まれた文字列リテラル内でエスケープシーケンスが展開されますが、シングルクォートで囲まれた文字列リテラル内では展開されません。
Rubyにおける「文字リテラル」は、?を前置した1文字で表します。
p?a#==> "a"p?字#==> "字"p?😈#==> "😈"p?😈.class#==> String
Rubyに文字型はなく、文字リテラルは長さ1文字の文字列です。
Ruby では、文字列のエンコーディングを指定することができます。通常、UTF-8 エンコーディングが使われますが、別のエンコーディングを指定することもできます。
文字列のエンコーディングを指定するには、文字列の前に encoding: を付けて指定します。例えば、UTF-8 エンコーディングの文字列を扱う場合は以下のように書きます。
str="こんにちは".encoding("UTF-8")
別のエンコーディングを指定する場合は、指定したいエンコーディング名を文字列で渡します。
str="こんにちは".encoding("Shift_JIS")
上記のコードでは、文字列 "こんにちは" が Shift_JIS エンコーディングの文字列として扱われます。
文字列のエンコーディングを指定することで、文字列の変換時にエラーが発生することを防ぐことができます。
Rubyにおける「正規表現クラス」と「正規表現リテラル」は、文字列をパターンとして表現するための機能です。
まず、「正規表現クラス」はRegexp クラスを使用して表現されます。以下のように、Regexp.new メソッドを使用して正規表現を作成することができます。
pattern=Regexp.new("Hello, world!")
また、正規表現オプションを指定する場合は、第2引数にオプションを渡すことができます。
pattern=Regexp.new("hello, world!",Regexp::IGNORECASE)
次に、「正規表現リテラル」はスラッシュ/ で囲まれた文字列で表現されます。以下のように、スラッシュで正規表現を作成することができます。
pattern=/Hello, world!/
また、正規表現オプションを指定する場合は、スラッシュの後ろにオプションを付けることができます。
pattern=/hello, world!/i
正規表現クラスと正規表現リテラルはどちらも同じ機能を持っており、どちらを使用しても正規表現を表現することができます。ただし、リテラル表現の方が簡潔に表現することができ、可読性が高くなります。
| 正規表現 | ユースケース |
|---|---|
/pattern/ | 文字列に一致するパターンを検索します。 |
/./ | 任意の一文字に一致します。 |
/^pattern/ | 文字列の先頭に一致するパターンを検索します。 |
/pattern$/ | 文字列の末尾に一致するパターンを検索します。 |
/[abc]/ | a、b、cのいずれかに一致するものを検索します。 |
/[^abc]/ | a、b、c以外のものに一致するものを検索します。 |
/pattern*/ | 0回以上の繰り返しに一致するパターンを検索します。 |
/pattern+/ | 1回以上の繰り返しに一致するパターンを検索します。 |
/pattern?/ | 0回または1回の繰り返しに一致するパターンを検索します。 |
/pattern{n}/ | n回の繰り返しに一致するパターンを検索します。 |
/pattern{n,}/ | n回以上の繰り返しに一致するパターンを検索します。 |
/pattern{n,m}/ | n回以上、m回以下の繰り返しに一致するパターンを検索します。 |
/(pattern)/ | 一致する部分をグループ化します。 |
/(?<name>pattern)/ | 一致する部分を名前でグループ化します。 |
/(?#comment)/ | 正規表現内のコメントを書くことができます。 |
/\Apattern/ | 文字列の先頭に一致するパターンを検索します。 |
/pattern\z/ | 文字列の末尾に一致するパターンを検索します。 |
/\bpattern/ | 単語の先頭に一致するパターンを検索します。 |
/pattern\b/ | 単語の末尾 |
/pattern/i | 大文字小文字を区別せずに文字列に一致するパターンを検索します。 |
/pattern/m | 複数行の文字列に一致するパターンを検索します。 |
/pattern/x | 正規表現内で空白を無視します。 |
/pattern/o | 正規表現を一度だけコンパイルします。 |
Rubyの「シンボル(Symbol)」は、一度定義されたら変更できないイミュータブルなオブジェクトで、文字列に似たデータ型です。シンボルは、コロン(:)の後ろに名前を付けて表します。例えば、:appleや:bananaなどがシンボルの例です。
シンボルは、主にキーとして使われることが多く、文字列と比較して以下のようなメリットがあります。
シンボルを使う場合、通常はコロン(:)をつけて定義しますが、:"apple"のようにダブルクォーテーション(")で囲って文字列から生成することもできます。また、文字列からシンボルを生成するには、to_symメソッドまたはinternメソッドを使います。
例えば、以下のようにハッシュのキーとしてシンボルを使うことができます。
fruits={apple:100,banana:200,orange:300}putsfruits[:banana]# => 200
Rubyの配列(Array)は、複数の要素を順序付けて格納するためのデータ構造です。配列は、異なるデータ型の要素を混在させることができます。例えば、整数、文字列、オブジェクトなどを含めることができます。
Rubyの配列は、角かっこ[ ] で囲まれた要素のコンマ区切りのリストで表されます。以下は、Rubyの配列の例です。
numbers=[1,2,3,4,5]fruits=["apple","banana","orange"]mixed_array=[1,"hello",true,3.14]
配列内の要素は、0から始まるインデックスを使用してアクセスできます。例えば、numbers配列の最初の要素にアクセスするには、numbers[0]とします。
Rubyの配列は動的で、サイズを事前に宣言する必要はありません。要素を追加、削除、置換することができます。また、Rubyには多くの配列関連のメソッドが組み込まれており、配列を操作するための便利な機能が提供されています。
Rubyの配列には、さまざまな操作が可能です。以下にいくつかの一般的な操作を示します。
<<演算子やpushメソッドを使用して要素を配列の末尾に追加できます。また、popメソッドを使用して末尾の要素を削除できます。他にも、unshiftメソッドを使って先頭に要素を追加したり、shiftメソッドを使って先頭の要素を削除したりすることもできます。numbers<<6# numbersは[1, 2, 3, 4, 5, 6]fruits.push("grape")# fruitsは["apple", "banana", "orange", "grape"]numbers.pop# numbersは[1, 2, 3, 4, 5]fruits.unshift("kiwi")# fruitsは["kiwi", "apple", "banana", "orange", "grape"]fruits.shift# fruitsは["apple", "banana", "orange", "grape"]
putsnumbers[2]# 3を出力fruits[1]="pear"# fruitsは["apple", "pear", "orange", "grape"]
+演算子を使用して配列を結合したり、concatメソッドを使用したりして配列を結合できます。また、sliceメソッドを使用して配列を部分的に分割することもできます。combined_array=numbers+fruits# combined_arrayは[1, 2, 3, 4, 5, "apple", "pear", "orange", "grape"]part_of_fruits=fruits.slice(1,2)# part_of_fruitsは["pear", "orange"]
fruits=["apple","banana","orange"]fruits.eachdo|fruit|putsfruitend
eachメソッドを用いて、fruits配列の要素を順番に取り出しています。そして、ブロック引数fruitに格納された各要素をputsメソッドで出力しています。fruits=["apple","banana","orange"]fruits.each_with_indexdo|fruit,i|puts"#{i+1}:#{fruit}"end
each_with_indexメソッドを用いて、fruits配列の要素とインデックスを順番に取り出しています。そして、ブロック引数fruitに格納された各要素とブロック引数iに格納された各インデックスを使って、putsメソッドで出力しています。include?メソッドを使用して特定の要素が配列に含まれているかどうかを確認したり、selectメソッドを使用して条件に一致する要素のみを抽出したりすることができます。putsnumbers.include?(3)# trueを出力even_numbers=numbers.select{|num|num.even?}# even_numbersは[2, 4]
map、filter、reduceは特によく使われるメソッドです。numbers=[1,2,3,4,5]doubled_numbers=numbers.mapdo|n|n*2endpdoubled_numbers#=> [2, 4, 6, 8, 10]even_numbers=numbers.filterdo|n|n.even?endpeven_numbers#=> [2, 4]sum=numbers.reducedo|result,n|result+nendputssum#=> 15# ブロックの代わりにシンボルを使って、次のように書くこともできます。putsnumbers.reduce(&:+)#=> 15
これらはRubyの配列を操作するための基本的な操作の一部です。Rubyの配列にはさらに多くの機能がありますが、これらの基本操作から始めることができます。
Rubyには、複数のメソッドを連鎖して呼び出す「メソッドチェイン」という機能があります。メソッドチェインを使うことで、1つのオブジェクトに対して複数のメソッドを簡潔につなげて処理することができます。
例えば、以下のような配列があるとします。
fruits=["apple","banana","orange"]
この配列に対して、mapメソッドで各要素を大文字にし、さらにjoinメソッドでカンマ区切りの文字列に変換する場合、通常の記法では以下のように書くことができます。
fruits=["apple","banana","orange"]upcase_fruits=fruits.mapdo|fruit|fruit.upcaseendresult=upcase_fruits.join(",")putsresult#=> "APPLE,BANANA,ORANGE"
しかし、メソッドチェインを使うと以下のように簡潔に書くことができます。
fruits=["apple","banana","orange"]result=fruits.map{|fruit|fruit.upcase}.join(",")putsresult#=> "APPLE,BANANA,ORANGE"
さらにブロックにシンボルを使うと以下のように簡潔に書くことができます。
fruits=["apple","banana","orange"]result=fruits.map(&:upcase).join(",")putsresult#=> "APPLE,BANANA,ORANGE"
このように、複数のメソッドを簡潔につなげて処理することができます。ただし、メソッドチェインは読みやすさを損なうことがあるため、適切に使うようにしましょう。
tapメソッドは、オブジェクト自体を受け取り、ブロックをそのオブジェクトのコンテキストで実行し、最終的にそのオブジェクトを返すメソッドです。主な目的は、オブジェクトの特定の状態をデバッグやログ出力などで調査することです。例えば、以下のように使用できます。
some_object.tap{|obj|pobj}.method1.method2.method3
このコードでは、some_object に対してmethod1 からmethod3 までのメソッドを呼び出す前に、tapブロック内でその状態を出力しています。tapメソッドは常にそのオブジェクトを返すため、メソッドチェインを中断することなく、そのまま次のメソッド呼び出しを行うことができます。
これらの機能を組み合わせることで、効果的にコードを記述し、デバッグやログ出力などの目的に役立てることができます。
Rubyの「ハッシュ(Hash)」は、キー(key)と値(value)のペアを保持するデータ構造であり、連想配列やマップとも呼ばれます。
ハッシュは波括弧({})で囲まれたキーと値のペアのリストで表され、キーと値の間には=>を使って対応付けます。例えば、以下のように定義することができます。
my_hash={"key1"=>"value1","key2"=>"value2"}
この場合、my_hashは、"key1"というキーに対応する"value1"、"key2"というキーに対応する"value2"という値を持ちます。
ハッシュは、キーを使って値を取得することができます。例えば、上記のmy_hashから値を取得するには、以下のように書きます。
my_hash["key1"]# => "value1"
また、Hashクラスのeachメソッドを使うことで、ハッシュ内の全てのキーと値に対して繰り返し処理を行うことができます。以下は、my_hashの全てのキーと値を出力する例です。
my_hash.eachdo|key,value|puts"#{key}:#{value}"end
出力結果は以下のようになります。
key1: value1key2: value2
このように、ハッシュはRubyで非常に重要なデータ構造の一つであり、プログラムで様々な用途に利用されます。
例えば、以下のようなハッシュを考えます。
my_hash={:key1=>"value1",:key2=>"value2"}
このハッシュでは、キーとしてシンボルを使っています。ただし、この記法は少し冗長であり、Rubyでは以下のように短縮して書くことができます。
my_hash={key1:"value1",key2:"value2"}
このように、キーをシンボルで表す場合は、コロン(:)をキー名の前に付けることで簡潔に表現できます。これにより、より見やすく、読みやすいコードを書くことができます。
また、シンボルを使うことで、文字列を使う場合に比べて、ハッシュの検索速度が速くなることがあります。これは、シンボルはRuby内部で一度だけ生成され、それ以降は同じオブジェクトを使い回すため、検索時にオブジェクトの比較を高速に行えるためです。
なお、シンボルは、文字列と異なりイミュータブルなオブジェクトであり、文字列と同様に、to_symメソッドを使って文字列からシンボルを生成することもできます。
Rubyの「範囲(Range)」は、ある値から別の値までの連続する要素を表すオブジェクトです。Rubyでは、範囲は..または...の範囲演算子を使って作成することができます。以下に例を示します。
range1=1..5# 1から5までの範囲を表すRangeオブジェクトを作成range2=1...5# 1から5未満までの範囲を表すRangeオブジェクトを作成
..を使うと、範囲に終端の値が含まれますが、...を使うと、範囲に終端の値は含まれません。
範囲は主に繰り返し処理に使われ、以下のように使うことができます。
range=1..5range.each{|i|putsi}# 1から5までの値を1つずつ表示する
このコードでは、eachメソッドを使って、範囲内の値を1つずつ取り出し、ブロック内で処理しています。
また、範囲を使うことで、配列の一部を切り出すこともできます。例えば、以下のように範囲を使って、配列から一部の要素を取り出すことができます。
array=[1,2,3,4,5]sub_array=array[1..3]# 2番目から4番目までの要素を取り出すputssub_array# => [2, 3, 4]
このように、範囲はRubyで様々な場面で使われ、プログラミングをより効率的に行うための重要な要素の1つです。
Rubyにはファイル操作を行う標準ライブラリFile があります。これを使用することで、ファイルの読み込み・書き込み、存在確認・削除、ディレクトリの作成・削除などが簡単に行えます。
以下は、ファイル操作の基本的な使い方の例です。
# ファイルの読み込みFile.open('sample.txt','r')do|file|putsfile.readend# ファイルの書き込みFile.open('output.txt','w')do|file|file.write('Hello, world!')end# ファイルの存在確認putsFile.exist?('sample.txt')#=> true# ファイルの削除File.delete('output.txt')# ディレクトリの作成Dir.mkdir('mydir')# ディレクトリの削除Dir.delete('mydir')
RubyのMathモジュールは、数学関数を提供するライブラリです。このモジュールに含まれる関数を使用することで、三角関数、指数関数、対数関数などの計算を簡単に行うことができます。
例えば、sin関数を使用する場合は、以下のように記述します。
Math.sin(30*Math::PI/180)#=> 0.5
この例では、30度の正弦(sin)を計算しています。Math::PIは円周率を表しており、角度をラジアンに変換しています。
他にも、cos関数、tan関数、exp関数、log関数などがあります。詳細は公式ドキュメントを参照してください。
Rubyの標準ライブラリには、多数のクラスやモジュールが含まれています。これらのクラスやモジュールは、Rubyプログラムで頻繁に使用される共通機能を提供しています。例えば、文字列(String)クラス、配列(Array)クラス、ファイル操作(File)クラスなどがあります。
また、RubyではMix-inという概念もあります。Mix-inとは、あるクラスに他のモジュールを取り込んで機能を追加することです。これにより、クラスを継承することなく、複数のモジュールから機能を組み合わせて利用することができます。
例えば、Enumerableモジュールは、Rubyの多くのクラスにEnumerableという機能を提供するためのモジュールです。Enumerableモジュールには、eachメソッドやmapメソッド、selectメソッドなどが含まれており、これらのメソッドを利用することで、配列やハッシュなどのオブジェクトに対して繰り返し処理を簡単に行うことができます。
以下に、標準ライブラリからいくつかの代表的なクラスやMix-inを挙げます。
これらのクラスやMix-inは、Rubyプログラムの中で頻繁に使用されるため、覚えておくと便利です。詳細は公式ドキュメントを参照してください。
RubyのEnumeratorクラスは、反復処理を行うためのクラスです。配列やハッシュなどのオブジェクトに対して繰り返し処理を行うことができます。
例えば、以下のようなコードを考えてみましょう。
array=[1,2,3,4,5]array.eachdo|num|putsnumend
この場合、eachメソッドを使用して、配列の要素を順番に取り出していることがわかります。しかし、この方法では要素の途中で処理を中断したり、要素を複数回利用することができません。
ここでEnumeratorクラスを使用すると、以下のように書くことができます。
array=[1,2,3,4,5]enum=array.to_enumloopdonum=enum.nextputsnumend
この場合、to_enumメソッドを使用して、Enumeratorオブジェクトを生成しています。そして、nextメソッドを使用して、順番に要素を取り出しています。この方法を使用することで、要素の途中で処理を中断することができますし、要素を複数回利用することもできます。
defprimegenn=2sieve=[]Enumerator.newdo|y|loopdoifsieve[n].nil?||sieve[n]==0y<<nsieve[n*n]=nelsefactor=sieve[n]sieve[n+factor]=factorsieve[n]=0endn+=1endend.lazyendpprimegen.first(10)#=> [2, 3, 5, 7, 11, 13, 15, 17, 19, 21]primegen.each_with_indexdo|prime,index|breakifindex>5puts"#{index}:#{prime}"end# => 0: 2# 1: 3# 2: 5# 3: 7# 4: 11# 5: 13
Rubyの%記法は、文字列や正規表現、配列、シンボルなどを作成するための簡潔な書き方のことを指します。%記法を使うことで、コードの可読性を高めることができます。
以下に、Rubyの%記法の使い方を表にまとめました。
| 記号 | 役割 | 例 |
|---|---|---|
| %q | シングルクォートで囲んだ文字列を作成する | %q(Hello, world!) =>"Hello, world!" |
| %Q | ダブルクォートで囲んだ文字列を作成する | %Q(Hello, #{name}!) =>"Hello, Alice!" |
| %r | 正規表現リテラルを作成する | %r(\d+-\d+-\d+) =>/\\d+-\\d+-\\d+/ |
| %w | 単語の配列を作成する | %w(apple banana orange) =>["apple", "banana", "orange"] |
| %W | ダブルクォートで展開される単語の配列を作成する | %W(#{fruit1} #{fruit2} #{fruit3}) =>["apple", "banana", "orange"] |
| %s | シンボルを作成する | %s(hello) =>:hello |
| %i | シンボルの配列を作成する | %i(apple banana orange) =>[:apple, :banana, :orange] |
上の例ではパッターンを丸括弧()で囲みましたが、波括弧{}・角括弧[]で囲むことも出来、括弧でない文字(例えば!)で始めたら、同じ文字(この場合は!)で閉じることが出来ます。
# {} を使う例puts%q{Hello, #{name}!}# => Hello, Ruby!# [] を使う例puts%w[apple banana orange]# => ["apple", "banana", "orange"]puts%i[foobarbaz]# => [:foo, :bar, :baz]# ! を使う例puts%Q!It's raining outside!# => It's raining outsideputs%w!apple banana orange!# => ["apple", "banana", "orange"]# | を使う例puts%Q|Hello,#{name}!|# => Hello, Ruby!puts%i|foobarbaz|# => [:foo, :bar, :baz]
これらの例では、%記法の丸括弧()の部分を、波括弧{}、角括弧[]、感嘆符!、縦棒| に置き換えています。%記法では、括弧以外にも様々な文字を使うことができますが、適切な文字を選んで使用することが重要です。
defprint_subclasses(klass,indent=0)subclasses=klass.subclassesreturnifsubclasses.empty?subclasses.to_a.sort{|a,b|a.to_s<=>b.to_s}.eachdo|subclass|puts"#{"\t"*indent}#{subclass}"print_subclasses(subclass,indent+1)endend# <code>Object</code>から再帰的にサブクラスを表示print_subclasses(Object.superclass)
print_subclassesメソッドは、与えられたクラス(klass)のサブクラスを再帰的に表示します。indentはインデントレベルを表し、デフォルト値は0です。subclassesメソッドは、指定されたクラスの直接のサブクラスを返します。sortメソッドは、サブクラスをアルファベット順に並べ替えます。eachメソッドは、サブクラスを1つずつ処理し、その名前を表示します。print_subclassesメソッドを再帰的に呼び出すことで、サブクラスのサブクラスも同様に処理されます。Object.superclassは、すべてのクラスの祖先であるObjectクラスから始まります。print_subclasses(Object.superclass)を呼び出すことで、すべてのクラスの階層構造を表示します。次は、Rubyの標準クラス階層を再帰的に表示するものです。各クラスはその直接のサブクラスとしてインデントされて表示されます。以下に各部分の解説を示します:
ObjectクラスはRubyのほとんどのクラスの祖先となる基本クラスです。ARGF.class: コマンドライン引数やファイルからの入力を扱うクラスです。Array: 配列を表すクラスです。Binding: メソッド呼び出しのコンテキストを保持するオブジェクトを表すクラスです。Complex::compatible: 複素数を表すクラスです。DidYouMean以下のクラス群: ユーザーの入力ミスに対する修正候補を提案する機能を提供するモジュールです。Dir: ディレクトリを扱うクラスです。Encoding以下のクラス群: 文字エンコーディングを扱うクラスです。Enumerator以下のクラス群: 列挙可能なオブジェクトを生成するためのクラスです。ErrorHighlight以下のクラス群: エラーメッセージをハイライトする機能を提供するモジュールです。Exception以下のクラス群: 例外を扱うためのクラスです。FalseClass: 真でない値を表すクラスです。Fiber: コルーチンを表すクラスです。File::Stat: ファイルの状態を表すクラスです。Gem以下のクラス群: RubyGemsを扱うためのクラスです。Hash: ハッシュを表すクラスです。IO以下のクラス群: 入出力を扱うクラスです。MatchData: 正規表現のマッチング結果を表すクラスです。Method: メソッドオブジェクトを表すクラスです。Module以下のクラス群: モジュールを表すクラスです。Monitor以下のクラス群: スレッドセーフな処理を実現するためのクラスです。NameError::message: 名前エラーのメッセージを表すクラスです。NilClass:nilを表すクラスです。Numeric以下のクラス群: 数値を表すクラスです。Integer整数を表すクラスです。Complex複素数を表すクラスです。Rational有理数を表すクラスです。ObjectSpace::WeakMap: 弱参照を使ったハッシュマップを表すクラスです。Proc: プロシージャやラムダを表すクラスです。Process::Status: プロセスのステータスを表すクラスです。Ractor以下のクラス群: Rubyの並行処理を実現するためのクラスです。Random::Base以下のクラス群: 乱数生成を行うためのクラスです。Range: 範囲を表すクラスです。Regexp: 正規表現を表すクラスです。RubyVM以下のクラス群: Rubyの仮想マシンを扱うクラスです。String: 文字列を表すクラスです。Struct: 構造体を表すクラスです。Symbol: シンボルを表すクラスです。Thread以下のクラス群: スレッドを扱うクラスです。ThreadGroup: スレッドグループを表すクラスです。Time以下のクラス群: 時刻を表すクラスです。TracePoint: トレースポイントを表すクラスです。TrueClass: 真の値を表すクラスです。UnboundMethod: バインドされていないメソッドを表すクラスです。Ractor::MovedObject: 移動されたオブジェクトを表すクラスです。これはRubyの標準ライブラリで定義されたクラス階層を表しており、各クラスがどのような階層構造にあるかを示しています。
ObjectARGF.classArrayBindingComplex::compatibleDidYouMean::ClassNameCheckerDidYouMean::DeprecatedMappingDidYouMean::FormatterDidYouMean::KeyErrorCheckerDidYouMean::MethodNameCheckerDidYouMean::NullCheckerDidYouMean::PatternKeyNameCheckerDidYouMean::RequirePathCheckerDidYouMean::SpellCheckerDidYouMean::TreeSpellCheckerDidYouMean::VariableNameCheckerDirEncodingEncoding::ConverterEnumeratorEnumerator::ArithmeticSequenceEnumerator::ChainEnumerator::LazyEnumerator::GeneratorEnumerator::ProducerEnumerator::YielderErrorHighlight::DefaultFormatterErrorHighlight::SpotterExceptionErrorHighlight::Spotter::NonAsciiNoMemoryErrorScriptErrorLoadErrorGem::LoadErrorGem::ConflictErrorGem::MissingSpecErrorGem::MissingSpecVersionErrorNotImplementedErrorSyntaxErrorSecurityErrorSignalExceptionInterruptStandardErrorArgumentErrorGem::Requirement::BadRequirementErrorUncaughtThrowErrorEncodingErrorEncoding::CompatibilityErrorEncoding::ConverterNotFoundErrorEncoding::InvalidByteSequenceErrorEncoding::UndefinedConversionErrorFiberErrorIOErrorEOFErrorIndexErrorKeyErrorStopIterationClosedQueueErrorRactor::ClosedErrorLocalJumpErrorMath::DomainErrorNameErrorNoMethodErrorNoMatchingPatternErrorNoMatchingPatternKeyErrorRangeErrorFloatDomainErrorRegexpErrorRuntimeErrorFrozenErrorGem::ExceptionGem::CommandLineErrorGem::DependencyErrorGem::DependencyResolutionErrorGem::UnsatisfiableDependencyErrorGem::DependencyRemovalExceptionGem::DocumentErrorGem::EndOfYAMLExceptionGem::FilePermissionErrorGem::FormatExceptionGem::GemNotFoundExceptionGem::SpecificGemNotFoundExceptionGem::GemNotInHomeExceptionGem::ImpossibleDependenciesErrorGem::InstallErrorGem::RuntimeRequirementNotMetErrorGem::InvalidSpecificationExceptionGem::OperationNotSupportedErrorGem::RemoteErrorGem::RemoteInstallationCancelledGem::RemoteInstallationSkippedGem::RemoteSourceExceptionGem::RubyVersionMismatchGem::UninstallErrorGem::UnknownCommandErrorGem::VerificationErrorGem::WebauthnVerificationErrorIO::Buffer::AccessErrorIO::Buffer::AllocationErrorIO::Buffer::InvalidatedErrorIO::Buffer::LockedErrorRactor::ErrorRactor::IsolationErrorRactor::MovedErrorRactor::RemoteErrorRactor::UnsafeErrorSystemCallErrorErrno::E2BIGErrno::EACCESErrno::EADDRINUSEErrno::EADDRNOTAVAILErrno::EADVErrno::EAFNOSUPPORTErrno::EAGAINIO::EAGAINWaitReadableIO::EAGAINWaitWritableErrno::EALREADYErrno::EBADEErrno::EBADFErrno::EBADFDErrno::EBADMSGErrno::EBADRErrno::EBADRQCErrno::EBADSLTErrno::EBFONTErrno::EBUSYErrno::ECANCELEDErrno::ECHILDErrno::ECHRNGErrno::ECOMMErrno::ECONNABORTEDErrno::ECONNREFUSEDErrno::ECONNRESETErrno::EDEADLKErrno::EDESTADDRREQErrno::EDOMErrno::EDOTDOTErrno::EDQUOTErrno::EEXISTErrno::EFAULTErrno::EFBIGErrno::EHOSTDOWNErrno::EHOSTUNREACHErrno::EHWPOISONErrno::EIDRMErrno::EILSEQErrno::EINPROGRESSIO::EINPROGRESSWaitReadableIO::EINPROGRESSWaitWritableErrno::EINTRErrno::EINVALErrno::EIOErrno::EISCONNErrno::EISDIRErrno::EISNAMErrno::EKEYEXPIREDErrno::EKEYREJECTEDErrno::EKEYREVOKEDErrno::EL2HLTErrno::EL2NSYNCErrno::EL3HLTErrno::EL3RSTErrno::ELIBACCErrno::ELIBBADErrno::ELIBEXECErrno::ELIBMAXErrno::ELIBSCNErrno::ELNRNGErrno::ELOOPErrno::EMEDIUMTYPEErrno::EMFILEErrno::EMLINKErrno::EMSGSIZEErrno::EMULTIHOPErrno::ENAMETOOLONGErrno::ENAVAILErrno::ENETDOWNErrno::ENETRESETErrno::ENETUNREACHErrno::ENFILEErrno::ENOANOErrno::ENOBUFSErrno::ENOCSIErrno::ENODATAErrno::ENODEVErrno::ENOENTErrno::ENOEXECErrno::ENOKEYErrno::ENOLCKErrno::ENOLINKErrno::ENOMEDIUMErrno::ENOMEMErrno::ENOMSGErrno::ENONETErrno::ENOPKGErrno::ENOPROTOOPTErrno::ENOSPCErrno::ENOSRErrno::ENOSTRErrno::ENOSYSErrno::ENOTBLKErrno::ENOTCONNErrno::ENOTDIRErrno::ENOTEMPTYErrno::ENOTNAMErrno::ENOTRECOVERABLEErrno::ENOTSOCKErrno::ENOTSUPErrno::ENOTTYErrno::ENOTUNIQErrno::ENXIOErrno::EOVERFLOWErrno::EOWNERDEADErrno::EPERMErrno::EPFNOSUPPORTErrno::EPIPEErrno::EPROTOErrno::EPROTONOSUPPORTErrno::EPROTOTYPEErrno::ERANGEErrno::EREMCHGErrno::EREMOTEErrno::EREMOTEIOErrno::ERESTARTErrno::ERFKILLErrno::EROFSErrno::ESHUTDOWNErrno::ESOCKTNOSUPPORTErrno::ESPIPEErrno::ESRCHErrno::ESRMNTErrno::ESTALEErrno::ESTRPIPEErrno::ETIMEErrno::ETIMEDOUTErrno::ETOOMANYREFSErrno::ETXTBSYErrno::EUCLEANErrno::EUNATCHErrno::EUSERSErrno::EXDEVErrno::EXFULLErrno::NOERRORThreadErrorTypeErrorZeroDivisionErrorSystemExitGem::SystemExitExceptionSystemStackErrorfatalFalseClassFiberFile::StatGem::BasicSpecificationGem::SpecificationGem::StubSpecificationGem::DependencyGem::ErrorReasonGem::PlatformMismatchGem::SourceFetchProblemGem::ListGem::PathSupportGem::PlatformGem::RequirementGem::StubSpecification::StubLineGem::UnknownCommandSpellCheckerGem::VersionHashIOFileIO::BufferMatchDataMethodModuleClassRefinementMonitorMonitorMixin::ConditionVariableNameError::messageNilClassNumericComplexFloatIntegerRationalObjectSpace::WeakMapProcProcess::StatusRactorRandom::BaseRandomRangeRational::compatibleRegexpRubyVMRubyVM::AbstractSyntaxTree::NodeRubyVM::InstructionSequenceStringDidYouMean::ClassNameChecker::ClassNameWarning::bufferStruct#<Class:0x0000798f28922be8>#<Class:0x0000798f2902c5b0>Process::TmsSymbolThreadProcess::WaiterThread::BacktraceThread::Backtrace::LocationThread::ConditionVariableThread::MutexThread::QueueThread::SizedQueueThreadGroupTimeTime::tmTracePointTrueClassUnboundMethodRactor::MovedObject
RDocとは、Rubyドキュメンテーションの生成ツールであり、Rubyのプログラムに対する文書化を支援するためのツールです。RDocを使用すると、Rubyのプログラムに関する情報をドキュメントとして出力することができます。これにより、ユーザーは作成されたコードを理解するための情報が提供され、開発者は自分たちのコードを文書化することができます。RDocは、Rubyの標準ライブラリに含まれています。
RDocは、Rubyのための文書作成システムで、コード内で注釈コメントを書くことで、自動的にドキュメントを生成します。以下はRDocの構文についての解説です。
RDocの注釈コメントは、通常のRubyコメントとして記述されますが、一つ上に「#」の代わりに「=begin」と「=end」を使ってマークアップを開始し、終了します。
以下は、RDocのマークアップの例です。
=beginThis class does some cool things.== ExampleAn example of how to use this class: MyClass.new.do_cool_thing== CopyrightCopyright (c) 2022 John Doe. All rights reserved.=end
RDocは、以下の形式のマークアップタグをサポートしています。
#: コメント=begin/=end: ドキュメントの開始/終了==: セクションの開始*: 箇条書きの開始+: リストの開始-: リストの終了!>: コードブロックの開始<pre>/</pre>: コードブロックの開始/終了+methodName+(args)+: メソッドの定義(arg),(arg=default): 引数の指定returns (type): 戻り値の指定\: ラインコンティニューRDocの実行には、次のコマンドを使用します。
$rdoc[options][names...]
オプションには、ドキュメンテーションを生成するFormattersや、ドキュメント化するファイルの指定などが含まれます。namesには、ドキュメンテーションを生成するファイルまたはディレクトリを指定します。
以上が、RDocの構文解説です。
Ruby 3.3.0では、多くの重要な変更が導入されました。その中には、言語機能の改善、コアクラスの更新、標準ライブラリの更新、C APIの更新、実装の改善などが含まれます。
# Ruby 3.3.0での変更点の例# 匿名の可変長引数とキーワード可変長引数が引数として渡せるようになりました。defexample(*args,**kwargs)pargspkwargsendexample(1,2,3,key1:'value1',key2:'value2')# Procオブジェクトのdupとcloneメソッドが#initialize_dupおよび#initialize_cloneフックを呼び出すようになりました。proc_object=proc{|x|x*2}cloned_proc=proc_object.clonepcloned_proc.call(5)# => 10# Range#overlap?メソッドが追加されました。range1=1..5range2=4..8prange1.overlap?(range2)# => true
これらの変更により、Ruby 3.3.0はより高速で効率的なバージョンになりました。
Ruby 3.2.0では、いくつかの重要な変更と新機能が導入されています。以下に概要を示します:
*argsを通じてキーワード引数を委譲する場合、ruby2_keywordsでマークする必要があります。# Ruby 3.2.0の言語の変更# 匿名の可変長引数とキーワード可変長引数が引数として渡せるようになりました。# 例:defexample(*args)pargsendexample(1,2,3)# => [1, 2, 3]# 単一の位置引数とキーワードを受け入れるProcは、自動的にスプラットしなくなりました。# 例:proc_example=proc{|a,**kw|[a,kw]}pproc_example.call(1,x:2,y:3)# => [1, {:x=>2, :y=>3}]# 明示的なオブジェクトに対する定数の割り当て評価順序が一貫性を持つようになりました。# 例:classFoo;endBAR=:bazFoo::BAR=BARpFoo::BAR# => :baz# "Find pattern" が実験的ではなくなりました。# restパラメータを持つメソッドが*argsを通じてキーワード引数を委譲する場合、ruby2_keywordsでマークする必要があります。# 例:deftarget(**kw)pkwendruby2_keywordsdefdelegate(*args)target(*args)enddelegate(a:1,b:2)# => {:a=>1, :b=>2}
Fiber.[]とFiber.[]=が導入されました。Kernel#autoloadにおける競合状態が解消されました。これらの変更と改善により、Rubyプログラミングのさまざまな側面でパフォーマンス、安定性、機能性が向上しました。ユーザーは、より良いエクスペリエンスと改善されたパフォーマンスのために、Ruby 3.2.0へのアップグレードを検討することが推奨されます。
Ruby 3.1.0の変更点は以下の通りです:
言語の変更点:
# ブロック引数が、ブロックが別のメソッドに渡される場合は匿名であってもよくなりました。deffoo(&)bar(&)end# ピン演算子は、式を受け取れるようになりました。result=Prime.each_cons(2).lazy.find_all{_1in[n,^(n+2)]}.take(3).to_a#=> [[3, 5], [5, 7], [11, 13]]# ピン演算子は、インスタンス、クラス、グローバル変数をサポートするようになりました。@n=5result=Prime.each_cons(2).lazy.find{_1in[n,^@n]}#=> [3, 5]# 1行のパターンマッチングが実験的ではなくなりました。# Parenthesesを省略できるようになりました。[0,1]=>_,x{y:2}=>y:x#=> 1y#=> 2# 複数の代入の評価順序が単一の代入の評価順序と一貫性が持たされるようになりました。# Ruby 3.1.0以前foo[0],bar.baz=a,b# 評価順序: a, b, foo, []= on foo, bar, []= on bar, baz= on bar# Ruby 3.1.0以降# 評価順序: foo, bar, a, b, []= on foo, baz= on barfoo[0],bar.baz=a,b# ハッシュリテラルとキーワード引数では値を省略できるようになりました。# 例: {x: x, y: y} を {x:, y:} と書けます。
C APIの更新、実装の改善、JIT(MJIT、YJIT)、静的解析、デバッガ、エラーハイライト、IRBの自動補完、その他の変更が含まれます。
Ruby 3.0.0には、多くの言語の変更点が含まれています。主な変更点を以下に示します:
# キーワード引数と位置引数の分離defexample(arg,kwarg:)putsargputskwargend# ArgumentErrorが発生します# example(123, kwarg: 456)# Procの動作変更# autosplattingの対象外となりましたproc=->(*rest,**kwargs){[rest,kwargs]}# [rest, kwargs]がそのまま返されますpproc.call(1,2,a:3,b:4)# Arguments forwardingの改善defforwarder(*args,**kwargs)receiver(*args,**kwargs)enddefreceiver(a,b:,c:nil)puts"a:#{a}, b:#{b}, c:#{c}"endforwarder(1,b:2,c:3)# => a: 1, b: 2, c: 3# パターンマッチングの改善case[1,2]in[1,x]putsxend# => 2# Endless method definitionの導入deffoo=puts"Hello"foo# => Hello# 文字列補間リテラルの変更# # frozen-string-literal: trueを使用している場合、文字列補間リテラルが凍結されなくなりましたfoo="foo#{Time.now}"# 静的解析の導入# 型定義言語RBSが導入され、型解析ツールTypeProfが提供されます# 非推奨の警告の変更# デフォルトで表示されなくなりました# $SAFEと$KCODEの変更# 通常のグローバル変数として振る舞い、関連するC-APIメソッドが削除されました# yieldの変更# メソッド内のシングルトンクラス定義でのyieldは、警告ではなくSyntaxErrorとなりました# クラス変数のアクセスの変更classMyClass@@var=1end# RuntimeErrorが発生します# MyClass.class_eval { @@var = 2 }# 番号付きパラメータへの代入の変更# SyntaxErrorが発生します# _1 = 10
これらの変更点は、Ruby 3.0.0で導入された主な機能です。これにより、より安全で効率的なコーディングが可能になり、言語の進化が促進されました。
Rubyの改廃された技術や利用が推奨されない技術は、言語の進化、パフォーマンスの向上、シンプルさの追求、新しいプログラミングパラダイムへの対応により置き換えられます。以下に、代表的な技術を示します。
taint メソッドtaintとtrustメカニズムは非推奨となり削除されました。セキュリティモデルが変更されたことに伴います。$== グローバル変数ThreadまたはFiberローカル変数を使用してください。Object#==~ メソッドObject#=~は削除されました。#===や明示的なメソッドを実装してください。Fixnum とBignum クラスIntegerに統一されました。Integerクラスを使用してください。Flip-flop 演算子DL ライブラリFiddleライブラリに置き換えられました。Fiddleライブラリを使用してください。sprintf における% フォーマット指定子%の意味があいまいで、誤用される可能性があるため削除されました。%dや%s)を使用してください。String#byteslice の旧引数形式String#byteslice(start, length))を使用してください。proc { |x| ... })。safe_loadメソッドを使用し、Symbolのロードを手動で有効化してください。エラトステネスの篩を、若干 Ruby らしく書いてみました。
deferatosthenes(n)is_prime=[true]*(n+1)is_prime[0]=falseis_prime[1]=false(2..Math.sqrt(n)).eachdo|i|ifis_prime[i](i*i..n).step(i)do|j|is_prime[j]=falseendendendprimes=(2..n).select{|i|is_prime[i]}returnprimesendperatosthenes(100)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
最大公約数と最小公倍数を、若干 Ruby らしく書いてみました。
defgcd2(m,n)=n==0?m:gcd2(n,m%n)defgcd(*ints)=ints.reduce(&method(:gcd2))deflcm2(m,n)=m*n/gcd2(m,n)deflcm(*ints)=ints.reduce(&method(:lcm2))puts"gcd2(30, 45) =>#{gcd2(30,45)}"puts"gcd(30, 72, 12) =>#{gcd(30,72,12)}"puts"lcm2(30, 72) =>#{lcm2(30,72)}"puts"lcm(30, 42, 72) =>#{lcm(30,42,72)}"
gcd2(30, 45) => 15gcd(30, 72, 12) => 6lcm2(30, 72) => 360lcm(30, 42, 72) => 2520
二分法を、若干 Ruby らしく書いてみました。
defbisection(low,high,&f)x=(low+high)/2.0fx=f[x]returnxiffx.abs<1.0e-10casefx<=>0when-1thenlow=xwhen1thenhigh=xendreturnbisection(low,high,&f)endputsbisection(0,3){|x|x-1}putsbisection(0,3){|x|x*x-1}
0.99999999994179231.0000000000291038