やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rubyまとめ3(構文)

 これだけ知っておけば十分。(2万字)

情報源

予約語

BEGIN    class    ensure   nil      self     when
END      def      false    not      super    while
alias    defined? for      or       then     yield
and      do       if       redo     true     __LINE__
begin    else     in       rescue   undef    __FILE__
break    elsif    module   retry    unless   __ENCODING__
case     end      next     return   until

番号指定パラメータ

_1 _2 _3 _4 _5 _6 _7 _8 _9

1行コメント

# コメント

複数行コメント

=begin
複数行の
コメント
=end

シンボル

:some_symbole
:[_a-zA-Z][_a-zA-Z0-9]+

識別子

 名前の付け方に制約がある。

種別 規約
定数 先頭1字目が英大文字 Const = 1, CONST = 1
モジュール 先頭1字目が英大文字 module Mod; end
クラス 先頭1字目が英大文字 class Cls; end
メソッド - def met; end, def Met; end

 変数は先頭1字目が英小文字。

種別 規約
ローカル変数 - local = 1
グローバル変数 接頭辞$ $global = 1
インスタンス変数 接頭辞@ @ins = 1
クラス変数 接頭辞@@ @cls = 1
  • 再定義するとどうなるか
  • 未定義を参照するとどうなるか
項目 再定義 未定義
定数 警告&代入 エラー
モジュール 追加、上書きされる エラー
クラス 追加、上書きされる エラー
メソッド 上書きされる エラー
ローカル変数 代入 エラー
グローバル変数 代入 エラー
インスタンス変数 代入 nilを返す
クラス変数 代入 エラー

 警告は処理が継続されるが、エラーは中断される。

組込ライブラリ

Kernelモジュール

require

 外部ファイルをロードする。

require 'bundler'

 ふつう式といえば評価したとき値が定まるものを指す。たとえば以下のようなもの。

1 + 2 #=> 3

 Rubyはほぼすべて式である。ifでさえ式だ。returnの引数で値を指定しないかぎり、最後に実行した式の戻り値を返す。

x = if true
  1
else
  2
end
p x #=> 1

 C言語などではifは文であるが、Rubyでは式である。

 式は()でグループ化し優先度をあげたり、;で立て続けに書いてワンライナーにできる。

0 == (13 % 2)
a = 1; b = 2

 メソッドの引数に指定できない式のことを「文」と呼び分けることがある。

  • and, or, not
  • if/unless/rescue 修飾式など
def m(a); p a; end
m(1 and 2) #=> syntax error, unexpected `and', expecting ')'

 でも()で囲うと式として使える。

m((1 and 2)) #=> 2

メソッド

 メソッドは複数の式をまとめたもの。

定義

def m
end

呼出

m

 戻り値は最後に実行した式の戻り値を返す。

def m
  :m
end
m #=> :m

 returnに引数を渡せばその値を返せる。

def m
  return :m
end
m #=> "A"

 引数を受け取れる。

def m(a)
  p a
end
m 'A' #=> "A"

 引数の種類は次の通り。

種類 キー デフォルト式 複数
引数 a 位置 なし
引数 a = 1 位置 あり
可変長引数 *args 位置 有無
キーワード引数 k: 名前 なし
キーワード引数 k:'v' 名前 あり
キーワード可変長引数 **kwargs 名前 有無
ブロック引数 &block 名前 有無

 定義順は以下のとおり。

  • デフォルト式のない引数(複数指定可)
  • デフォルト式のある引数(複数指定可)
  • *を伴う引数(1つだけ指定可)
  • デフォルト式のない引数(複数指定可)
  • キーワード引数(複数指定可)
  • **を伴う引数(1つだけ指定可)
  • &を伴う引数(1つだけ指定可)

 処理のまとまりには以下の種類がある。

呼び方 概要
関数 レシーバがない。別名スタティックメソッド。 func(p1)
メソッド レシーバがある。 ins.met(p1), Cls::met(p1)
クロージャ 匿名関数、関数ポインタ、デリゲート等ともいう。 block.call(p1)

 メソッド一覧。

種別 概要 呼出例
メソッド トップレベルに定義したメソッド m
モジュール関数 モジュールにmodule_functionで定義したメソッド M
モジュールクラスメソッド モジュールにselfで定義したメソッド M::m
モジュールインスタンスメソッド モジュールに定義したメソッド クラスにMixinする
特異メソッド 特定のインスタンス専用メソッド ins.m
インスタンスメソッド クラスに定義したインスタンスがもつメソッド ins.m
クラスメソッド クラスに定義したクラスがもつメソッド ``

 クロージャ一覧。

種別 定義例
イテレータ {|i| ...}
proc proc {|i| ...}
lambda lambda {|i| ...}

 なお{...}do ... endに変更できる。それはproclambdaでも同じ。このときクロージャ内ローカル変数iは外側からも参照できるようになる。ほかにもブロック引数を省略しつつ呼び出したり、呼出をyieldでなくブロック変数からcallすることで行うなど複数の記法ができる。以下にパターン例を示す。

種別 定義例 呼出例
イテレータ1 {...} def m; yield
イテレータ1 {...} def m(&block); block.call
イテレータ1 `{ p1,p2| ...}|def m; yield p1, p2`
イテレータ1 `{ p1,p2| ...}|def m(&block); block.call p1, p2`
イテレータ2 do ... end def m; yield
イテレータ2 do ... end def m(&block); block.call
イテレータ2 `do p1,p2| ... end|def m; yield p1, p2`
イテレータ2 `do p1,p2| ... end|def m(&block); block.call p1, p2`
proc proc {...} def m(p); p.call
proc proc {...} def m(&block); block.call
proc `proc { p1,p2| ...}|def m; yield p1, p2`
proc `proc { p1,p2| ...}|def m(&block); block.call p1, p2`
proc proc do ... end def m(p); p.call
proc proc do ... end def m(&block); block.call
proc `proc do p1,p2| ... end|def m; yield p1, p2`
proc `proc do p1,p2| ... end|def m(&block); block.call p1, p2`
lambda lambda {...} def m(p); p.call
lambda lambda {...} def m(&block); block.call
lambda `lambda { p1,p2| ...}|def m; yield p1, p2`
lambda `lambda { p1,p2| ...}|def m(&block); block.call p1, p2`
lambda lambda do ... end def m(p); p.call
lambda lambda do ... end def m(&block); block.call
lambda `lambda do p1,p2| ... end|def m; yield p1, p2`
lambda `lambda do p1,p2| ... end|def m(&block); block.call p1, p2`

モジュール

module Mod; end
module Mod
end

 クラスと違ってインスタンス化できない。newメソッドがない。

 名前空間にしたり、Mixin(クラスやインスタンスへメソッドを提供)する。

名前空間

 名前空間として使う。とくに定数の入れ物として。

module Mod
  MAX = 100
end
p Mod::MAX #=> 100

モジュール関数

定義

module Mod
  def some
    p 'モジュール関数'
  end
  module_function :some
end

呼出

Mod::some
Mod.some

クラスメソッド

 モジュールなのにクラスメソッドとはこれいかに。便宜上の名前である。

定義

module Mod
  def self.some
    p 'クラス関数'
  end
end

呼出

Mod::some
Mod.some

 ⚠後述するMixinしたクラスやインスタンスからは呼び出せない!

インスタンスメソッド

 モジュールはインスタンス化できないのにインスタンスメソッドとはこれいかに。便宜上の名前である。

定義

module Mod
  def some
    p 'モジュール関数'
  end
end

 ⚠後述するMixin(include,prepend,extend)しなければ呼び出せない!

クラス

class C; end
class C
end

コンストラク

定義

class C
  def initialize
    p 'コンストラクタ'
  end
end

呼出

C.new

インスタンスメソッド

定義

class C
  def m
    p :m
  end
end

呼出

C.new.m

クラスメソッド

 他言語でいうクラスメソッドは、Rubyでいう「クラスの特異メソッド」である。ちなみに「クラスの」とつかない単なる「特異メソッド」は「インスタンスの特異メソッド」を指す。

定義

class C
  def self.m
    p :self_m
  end
end
C.public_methods false #=> [:m, :allocate, :superclass, :new]

呼出

C::m
C.m

 以下のような記法がある。

class C
  def C.m
    p :self_m
  end
end
C.public_methods false #=> [:m, :allocate, :superclass, :new]
class C; end
def C.m; :self_m; end
C.public_methods false #=> [:m, :allocate, :superclass, :new]
class C; end
class << C
  def m
    p :self_m
  end
end
C.public_methods false #=> [:m, :allocate, :superclass, :new]
module M
  def m; p :self_m; end
end
class C
  extend M
end
C.public_methods false #=> [:m, :allocate, :superclass, :new]
module M
  def m; p :self_m; end
end
class C; end
C.extend M
C.public_methods false #=> [:m, :allocate, :superclass, :new]

インスタンス変数

定義

class C
  def initialize
    @age = 0
  end
end

呼出

class C
  def initialize
    @age = 0
    some
    p @age
  end
  def some
    @age += 1
  end
end
class D < C
  def initialize
    @age += 1
  end
end
C.new #=> 1
C.new #=> 1
C.new.some #=> 2
D.new #=> undefined method `+' for nil:NilClass (NoMethodError)

クラス変数

定義

class C
  @@ins_count = 0
end

呼出

class C
  @@ins_count = 0
  def initialize
    @@ins_count += 1
    p @@ins_count
  end
end
class D < C
  def initialize
    @@ins_count += 1
    super
  end
end
C.new #=> 1
C.new #=> 2
D.new #=> 4
C.new #=> 5
  • クラス変数はクラス定義内?で宣言する
  • クラス変数は接頭辞@@がつく
  • クラス変数はクラス内か親クラス内でしか参照できない
  • クラス変数はインスタンス間で共有する

アクセサ

 インスタンス変数はprivateである。ふつうは参照できないが、接頭辞@を省いたゲッター/セッターメソッドを自動作成する糖衣構文がある。

糖衣構文 ゲッター セッター 推奨 補足
attr Ruby3.2で廃止予定
attr_reader
attr_writer
attr_accessor

 以下は同等のコードである。

class C
  attr_accessor :name
end
class C
  def initialize
    @name = nil
  end
  def name; @name; end
  def name=(v); @name = v; end
end

 試してみる。

class C
  attr_accessor :name, :age
end
c = C.new
p c.name #=> nil
c.name = 'ytyaru'
p c.name #=> "ytyaru"

アクセス修飾子

 クラス定義したメンバのスコープを定義する。

アクセス修飾子|概要 --------------|---- private|自分のクラス定義内からしか参照できない protected|自分またはサブクラスからしか参照できない public`|上記に加え、クラスのインスタンスからも参照できる

 デフォルトはpublic

 以下コードは同じ。

class C
  public
  def a; end
  def b; end
  protected
  def c; end
  def d; end
  private
  def e; end
  def f; end
end
C.new.public_methods false    #=> [:b, :a]
C.new.protected_methods false #=> [:c, :d]
C.new.private_methods false   #=> [:e, :f]
class C
  def a; end
  def b; end
  def c; end
  def d; end
  def e; end
  def f; end
  public :a, :b
  protected :c, :d
  private :e, :f
end
C.new.public_methods false    #=> [:b, :a]
C.new.protected_methods false #=> [:c, :d]
C.new.private_methods false   #=> [:e, :f]

継承

 継承とは、あるクラスの実装を別のクラスに受け継ぐことである。

class C
  def m; :m; end
end
class D < C
end
D.new.m

 DCを継承している。このときの関係を以下のように呼称する。

クラス 呼び方
C 親,基底,スーパー
D 子,派生,サブ

特異クラス

 特異クラスとはすべてのオブジェクトがひとつだけ持てる。特異メソッドを定義できる。singleton_classで参照する。

class C; end
p C.singleton_class #=> #<Class:C>
p C.new.singleton_class #=> #<Class:#<C:0x014ef278>>

 上記コードのとおり、クラスオブジェクトも、インスタンスオブジェクトも、どちらもそれぞれひとつずつ特異クラスsingleton_classを持っている。

特異メソッド

 ある特定のオブジェクトのみが持つメソッド。

class C; end
C.new.m #=> undefined method `m' for #<C:0x0171e598> (NoMethodError)

c = C.new
def c.m; '特異メソッド'; end
c.m #=> "特異メソッド"

 上記はインスタンスオブジェクトが持つ特異メソッドである。ふつう特異メソッドといえば、インスタンスオブジェクトが持つ特異クラスに定義された特異メソッドのことを指す。

 それに対して、クラスオブジェクトが持つ特異クラスに定義された特異メソッドのことをクラスメソッドと呼ぶ。

class C; end
def C.m; :self_m; end
p C::m #=> :self_m

Mixin

 Mixinとは、モジュールの実装をクラスへ組み込むことである。

module Mod
  def m; :m; end
end
class C
  include Mod
end
C.new.m
Mixin 概要
include モジュールのインスタンスメソッドをクラスに組み込む
prepend includeと同じだがクラスに同名シンボルがあればモジュール側を優先する
exntend includeと同じだが特異メソッドとして組み込む

継承リスト

 継承リストとは、クラスやモジュールの一覧である。参照メソッドを特定するためのもので、継承やMixinされたときの優先度がそのままリストになっている。

継承 継承リスト 位置 呼出
class C C.ancestors [C, ...] C.new.m
class D < C D.ancestors [D, C, ...] D.new.m
class C; end
class << C
  def m; :m; end
end
p C.singleton_class.ancestors #=> [#<Class:C>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, PP::ObjectMixin, Kernel, BasicObject]
p C.method :m #=> #<Method: C.m() (irb):3>
p C.singleton_class.method :m #=> undefined method `m' for class `#<Class:#<Class:C>>' (NameError)

 Rubyにおけるメソッド作成のすべて

  • トップレベmainオブジェクトにメソッドを追加する
  • モジュールオブジェクトにメソッドを追加する
  • インスタンスオブジェクトにメソッドを追加する
  • クラスオブジェクトにメソッドを追加する
  • 特異クラスオブジェクトにメソッドを追加する
  • クロージャとしてメソッドを追加する
    • BEGIN,END
    • イテレータ
      • {}(ブロック、ブロックつきメソッド呼出)
      • do-end
    • procProc.new
    • lambda

 以下はすべてCクラスオブジェクトにメソッドを追加する。

class C
  def self.m; :m; end
end

class C
  def C.m; :m; end
end

class C; end
class << C
  def m; :m; end
end

 特異クラスにメソッドを追加する

Mixin 継承リスト 位置 呼出
include C.ancestors [C, M, ...] C.new.m
prepend C.ancestors [M, C, ...] C.new.m
exntend C.singleton_class.ancestors [#<Class:C>, M, ...] C::m

 継承リストの順序は次の通り。

  1. 特異クラス
  2. 自クラス
  3. prepend(後からprependするほど高)
  4. include(後からincludeするほど高)
  5. 親クラス

 クラスオブジェクトがもつメソッドの探索優先順位。

module M; def m; :m; end; end
class C; end

C.singleton_class.prepend M       # 1
class C; def self.m; :c; end; end # 2
C.singleton_class.include M       # 3 == C.new.exntend M

 インスタンスオブジェクトがもつメソッドの探索優先順位。

module M; def m; :m; end; end
class C; end
ins = C.new

def ins.m; :ins_m; end             # 1
C.new.prepend M                    # 2
class C; def self.m; :c; end; end  # 3
C.new.include M                    # 4
class D < C; self.m; :d; end; end  # 5

継承

class C; end
class D < C; end
p C.ancestors #=> [C, Object, PP::ObjectMixin, Kernel, BasicObject]
p D.ancestors #=> [D, C, Object, PP::ObjectMixin, Kernel, BasicObject]

 もし同名のメソッドがあれば、継承リストの先頭から探す。

class C; def m; :m; end; end
class D < C;  def m; :n; end; end
p C.ancestors #=> [C, Object, PP::ObjectMixin, Kernel, BasicObject]
p D.ancestors #=> [D, C, Object, PP::ObjectMixin, Kernel, BasicObject]
p C.new.m #=> m
p D.new.m #=> n

 C.new.mは、C.newの継承リスト[C, Object, ...]の先頭Cにメソッドmがあるのでそれを呼び出す。もしCになければ次のObjectmメソッドがないか探索する。

 D.new.mは、D.newの継承リスト[D, C, Object, ...]の先頭Dにメソッドmがあるのでそれを呼び出す。Cのそれとは違い、:mでなく:nを返していることから違うメソッドが呼ばれたことがわかる。

super

 superは親メソッドを参照する。

class C; def m; :m; end; end
class D < C;  def m; super; end; end
p C.ancestors #=> [C, Object, PP::ObjectMixin, Kernel, BasicObject]
p D.ancestors #=> [D, C, Object, PP::ObjectMixin, Kernel, BasicObject]
p C.new.m #=> m
p D.new.m #=> m

 他言語でのsuperは親クラスを指すが、Rubyは親メソッドを指す。

include

 includeはモジュールのインスタンスメソッドをクラスに追加する。

module M; def m; :m; end; end
class C
  include M
end
p C.new.m #=> :m

 includeは継承リスト項目としてモジュールを追加する。その順序は、includeしたクラス自身の後ろである。

module M; def m; :m; end; end
class C
  include M
  def m; :c; end
end
p C.new.m #=> :c
p C.ancestors #=> [C, M, Object, PP::ObjectMixin, Kernel, BasicObject]

 もし同名メソッドがあれば継承リストの先頭から探索する。この場合はCmが最初にみつかる。次にMm。なので最初に見つかったC.mを呼び出す。

prepend

 prependはモジュールのインスタンスメソッドをクラスに追加する。

module M; def m; :m; end; end
class C
  prepend M
end
p C.new.m #=> :m

 prependは継承リスト項目としてモジュールを追加する。その順序は、prependしたクラス自身の前である。

module M; def m; :m; end; end
class C
  prepend M
  def m; :c; end
end
p C.new.m #=> :m
p C.ancestors #=> [M, C, Object, PP::ObjectMixin, Kernel, BasicObject]

 もし同名メソッドがあれば継承リストの先頭から探索する。この場合はMmが最初にみつかる。次にCm。なので最初に見つかったM.mを呼び出す。

extend

 extendはモジュールを特異クラスの継承リストに追加する。

 extendはモジュールのインスタンスメソッドを特異クラスの特異メソッドとして追加する。

module M; def m; :m; end; end
class C
  extend M
end
p C::m #=> :m
p C.m  #=> :m

p C.singleton_class.ancestors #=> [#<Class:C>, M, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, PP::ObjectMixin, Kernel, BasicObject]

p C.method :m #=> #<Method: #<Class:C>(M)#m() (irb):1>
p C.singleton_class.method :m #=> undefined method `m' for class `#<Class:#<Class:C>>' (NameError)
p C.new.singleton_class.method :m #=> #<Method: #<Class:C>(M)#m() (irb):1>

p C.ancestors #=> [C, Object, PP::ObjectMixin, Kernel, BasicObject]
p C.new.singleton_class.ancestors #=> [#<Class:#<C:0x01088790>>, C, Object, PP::ObjectMixin, Kernel, BasicObject]

 特異クラスはすべてのオブジェクトがひとつずつ持てる。singleton_classで取得できる。

 extendしたらそのクラスのクラスオブジェクト、インスタンスオブジェクトのうち、どちらの特異クラスの特異メソッドとして追加されるのか。上記コードをみてわかるとおり、:mメソッドはインスタンスオブジェクトがもつ特異クラスの特異メソッドとして追加される。クラスオブジェクトがもつ特異クラスの特異メソッドとしては追加されていない。undefined methodエラーになる。

 extendは継承リスト項目として特異クラスを追加する。その順序は最前列である。

module M; def m; :m; end; end
class C
  extend M
  def self.m; :c; end
end
p C::m #=> :c
p C.m #=> :c
p C.new.m #=> undefined method `m' for #<C:0x00845358> (NoMethodError)

p C.ancestors #=> [C, Object, PP::ObjectMixin, Kernel, BasicObject]
p C.new.singleton_class.ancestors #=> [#<Class:#<C:0x00a9b870>>, C, Object, PP::ObjectMixin, Kernel, BasicObject]

 extendしたとき特異クラスの特異メソッドはインスタンスオブジェクトに追加される。

 もし同名メソッドがあれば継承リストの先頭から探索する。この場合はMmが最初にみつかる。次にCm。なので最初に見つかったM.mを呼び出す。

オブジェクト

 オブジェクトとは状態と振る舞いをもったメモリ実体である。

 Rubyリテラルもオブジェクトである。Javaリテラルはプリミティブ型といい、単なる状態にすぎずメソッドを持っていない。それと比べてRubyリテラルもオブジェクトであり、メソッドを持っている。

 たとえば3は数値リテラルである。Integer型でありtimesメソッドを持っている。3回ブロックを実行する。

3.times {|i| p i}

インスタンス

 インスタンスもオブジェクトの一種である。ただし、自分で作成したクラスをnewして返ってきたものを特にインスタンスと呼ぶ。なぜならRubyはすべてがオブジェクトであるから。そしてnewできるのはクラスだけなので特別に呼び分けるのだと思われる。

3             # 3 もIntegerクラスのインスタンスと言えるが「リテラル」と呼ぶほうが一般的
a = 4         # a もIntegerクラスのインスタンスと言えるが「変数」と呼ぶほうが一般的
class C; end  # C もClassクラスのインスタンスと言える。また定数の一種とも言える。だがクラスオブジェクトともいう。だが定義である「クラス」と呼ぶほうが一般的
c1 = C.new    # c をインスタンスと呼ぶ。オブジェクトの一種ではあるが、自作クラスをnewした戻り値を特に「インスタンス」と呼び分ける。

リテラル

種類 コード例
数値 3
文字列 'str'
バックスラッシュ記法 \n
配列 [1,2,3]
ハッシュ [key: 'value', key2: 'v2']
範囲 1 .. 7, (1..)
シンボル :my_symbol

 以下は式やシェルを展開できる。

種類 コード例
式展開 "#{var}"
コマンド展開 `date`, %x{date}
ヒアドキュメント <<EOS ... EOS
正規表現 /^ruby$/, %r|ruby|
%記法 概要
%!STRING! ダブルクォート文字列
%Q!STRING! 同上
%q!STRING! シングルクォート文字列
%x!STRING! コマンド出力
%r!STRING! 正規表現
%w!STRING! 要素が文字列の配列(空白区切り)
%W!STRING! 要素が文字列の配列(空白区切り)。式展開、バックスラッシュ記法が有効
%s!STRING! シンボル。式展開、バックスラッシュ記法は無効
%i!STRING! 要素がシンボルの配列(空白区切り)
%I!STRING! 要素がシンボルの配列(空白区切り)。式展開、バックスラッシュ記法が有効

 !の部分には改行を含めた任意の非英数字を使える(%w%W%i%Iは区切りに空白、改行を用いるため、!の部分には使えない)。!(),[],{},<>にすることもできる。

%w(ab cd ef) #=> ["ab", "cd", "ef"]
%w(ab\ cd ef) #=> ["ab cd", "ef"]

定数

 頭文字が英大文字の変数は定数になる。命名規則的にはすべて大文字のスネークケースが望ましい。

Const = 100
CONST = 100

 なお、再代入することができてしまう。警告は出るが代入は妨げられない。値が変わってしまう。なので本来の定数とは言い難い。

CONST = 100
CONST = 101 #=> warning: already initialized constant CONST

変数

演算子

高い   ::
       []
       +(単項)  !  ~
       **
       -(単項)
       *  /  %
       +  -
       << >>
       &
       |  ^
       > >=  < <=
       <=> ==  === !=  =~  !~
       &&
       ||
       ..  ...
       ?:(条件演算子)
       =(+=, -= ... )
       not
低い   and or

残念ながらC言語のインクリメント演算子++などはない。

種別 演算子
スコープ演算子 ::
算術演算子 **, *, /, %, +, -, ,, ,, ``,
ビット演算子 !, ~, <<, >>, &, |, ^, not,and,or
比較演算子 >, >=, <, <=, ===, !=, !~, ,, ``,
宇宙船演算子 <=>
代入演算子 ==
正規表現演算子 =~
論理演算子 &&, ||
範囲演算子 .., ...
条件演算子 (条件) ? 真値 : 偽値
自己代入演算子 +=, -=, *=, /=, %=, **=, ||=, ...
配列演算子 [], []=
+(単項), -(単項)
1 <=> 2   #  -1  左 < 右
2 <=> 1   #   1  左 > 右
2 <=> 2   #   0  左 == 右
2 <=> '2' # nil  比較不可
a = nill
a ||= 1
p a #=> 1

b = 2
b ||= 1
p b #=> 2

代入

a = 1

自己代入

a ||= 1
a += 1
a -= 1

多重代入

a, b = 1, 2

制御式

種類 予約語
分岐 (条件) ? 真 : 偽, if/unless, case-when, case-in
繰り返し while/until, for, each, loop, times, updo, downdo, break/next/redo
例外処理 raise, begin-rescue-else-ensure-end, retry
開始処理 BEGIN
終了処理 END
制御返し return

 1行で書ける書式がある。

(1 == 1) ? 'A' : 'B' #=> "A"
p 'A' if 1 == 1      #=> "A"
p 'A' unless 1 == 2  #=> "A"
break while true
break until false
p 'A' rescue raise   #=> "A"

条件分岐

  • (条件) ? 真 : 偽
  • if/unless
  • case-when
  • case-in
(1 == 1) ? 'A' : 'B' #=> "A"
p 'A' if 1 == 1      #=> "A"
p 'A' unless 1 == 2  #=> "A"
if 1 == 1
  'A'
elsif 1 == 2
  'B'
else
  'C'
end
#=> "A"
v = unless 1 == 2
  'A'
else
  'B'
end
#=> "A"
case 7
when 1,3,5,7,9
  '奇数'
when 0,2,4,6,8
  '偶数'
else
  '範囲外'
end
case 7
in 1|3|5|7|9
  '奇数'
in 0|2|4|6|8
  '偶数'
else
  '範囲外'
end
  • unlesselsifが使えない
  • case-inは網羅的に書かねばならない

条件式としての範囲式

 条件式としての範囲式には以下のような記法がある。

$age = 10
case $age
when 0 .. 2
  "baby"
when 3 .. 6
  "little child"
when 7 .. 12
  "child"
when 13 .. 18
  "youth"
else
  "adult"
end
$age = 10
case $age
in 0 .. 2
  "baby"
in 3 .. 6
  "little child"
in 7 .. 12
  "child"
in 13 .. 18
  "youth"
else
  "adult"
end

フリップフロップ

10.times {|i|
  if (i==2)..(i==4)
    p "#{i}"
  end
}

 フリップフロップには以下のような縛りがある。

  • ブロック内でないと動作しない?
  • else, elsifが使えない

 以下は思ったように実行されなかった。

age = 10
if (age==0)..(age==100)
  p '死ぬまで働け!'
end
#=> nil
#"死ぬまで働け!"が期待値だった
$age = 10
if    ($age==0)..($age==2); 'baby';
elsif ($age==3)..($age==6); 'little child';
elsif ($age==7)..($age==12); 'child';
elsif ($age==13)..($age==18); 'youth';
else; 'adult';
end
#=> "adult"
# "child"が期待値だった

 一度廃止にしたけど廃止にするのを廃止にしたらしい。なんだかなぁ。使いこなせない。どうなっているのかよくわからん。ネットにもフリップフロップが意味不明というお嘆きの声があがっていた。

繰り返し

  • while/until
  • for
  • each
  • loop
  • times
  • updo
  • downdo
  • break/next/redo
break while true
break until false
while true
  p 'while'
  break
end

until false
  p 'until'
  break
end

 一度だけ実行する形式。C言語でいうdo-while

begin
  p 'do-while'
  break
end while true

begin
  p 'do-until'
  break
end until false

例外処理

  • raise
  • begin-rescue-else-ensure-end
  • retry

 例外を発生させる。

raise

 例外をキャッチする。

p 'A' rescue raise   #=> "A"

 例外処理。

begin
  raise
rescue => e
  p e
else
  p 'else'
ensure
  p 'ensure'
end

開始処理

BEGIN { p 'BEGIN' }

終了処理

END { p 'END' }

制御返し

def m
  return 5
end
p m #=> 5

対象環境

$ uname -a
Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux