0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Procとlambdaの違いとRails開発で使われる場面

Last updated at Posted at 2024-08-24

はじめに

Proc.newlambdaはどちらもProcクラスのオブジェクトを作成する方法です。

しかし、両者で作られるオブジェクトには挙動の違いがあります。

そこでこの記事では、Proc.newlambdaそれぞれで生成されるProcオブジェクトの違いをまとめます。

この記事におけるバージョンはRuby 3.3です。

【おさらい】Proc.newlambdaの使い方

最初にサラッとProc.newlambdaの使い方を確認しておきます。

Proc.newの使い方

次のようにProc.newを使います。

p = Proc.new { |n| n * 2 }
p.call(5)  #=> 10

またProc.newの代わりにprocメソッドを用いても、同じ機能のProcオブジェクトを生成できます。

p = proc { |n| n * 2 }
p.call(5)  #=> 10

lambdaの使い方

Proc.newにより生成されるProcオブジェクトと挙動は異なりますが、使い方はlambdaも同じです。

l = lambda { |n| n * 2 }
l.call(5)  #=> 10

こちらもlambdaの代わりに->という記号を用いることができます。

l = ->(n) { n * 2 }
l.call(5)  #=> 10

Proc.newlambdaの違い

冒頭で書いたように、Proc.newlambdaはどちらもProcクラスのオブジェクトを生成します。

しかし生成されるオブジェクトには挙動の異なる点がいくつかあります。

そのため、次のように呼称を区別するケースがしばしば見受けられます。

  • Proc.newで生成されたProcクラスのオブジェクト: Procと呼ばれる
  • lambdaで生成されたProcクラスのオブジェクト: lambdaと呼ばれる

Proclambdaには大きく分けて2つの違いがあります。

  1. 引数チェックの厳格さ
  2. returnbreakの挙動

順番に説明します。

違い①: 引数チェックの厳格さ

Proclambdaは引数の数が一致しない場合の挙動が異なります。

  • Proc: 引数の数に過不足があってもエラーが発生しない
  • lambda: 引数の数に過不足があるとArgumentErrorが発生

Procの場合、引数の数が少ない場合は足りない引数にnilを割り当ててくれます。

逆に数が多すぎる場合は余分な引数を無視してくれるのです。

p = Proc.new { |a, b| [a, b] }

p.call(1)  #=> [1, nil]
p.call(1, 2)  #=> [1, 2]
p.call(1, 2, 3) #=> [1, 2]

一方でlambdaは引数のチェックがより厳格です。

1つでも過不足がある場合はエラーを発生させます。

l = lambda { |a, b| [a, b] }

l.call(1)  #=> wrong number of arguments (given 1, expected 2) (ArgumentError)
l.call(1, 2)  #=> [1, 2]
l.call(1, 2, 3) #=> wrong number of arguments (given 3, expected 2) (ArgumentError)

Procには1つの配列で複数の引数を与えることができる

Procの場合は、次のように1つの配列で複数の引数をまとめて与えることができます。

p = Proc.new { |a, b| [a, b] }
p.call(1, 2)  #=> [1, 2]
p.call([1, 2])  #=> [1, 2]

lambdaではこの挙動が実現できません。

l = lambda { |a, b| [a, b] }
l.call(1, 2)  #=> [1, 2]
l.call([1, 2])  #=> wrong number of arguments (given 1, expected 2) (ArgumentError)

このようにProcはより柔軟な引数の扱いが可能です。

一方でlambdaの方が厳格であるため、予期せぬ動作を防ぎやすいと言えます。

違い②: returnbreakの挙動

Proclambdaの中でreturnbreakをした場合の挙動も異なります。

  • Proc: Procを呼び出したメソッド全体の処理が終了する
  • lambda: lambdaの実行だけが終わり、呼び出し元のメソッドには影響しない

これは実際のコードで確認した方がわかりやすいと思います。

まずはProcから。

# Proc.new の中で return すると proc_return_method 全体が終了する
def proc_return_method
  p = Proc.new { return "Procから抜けます" }
  p.call
  "メソッドの終わりに到達しました"
end

proc_return_method  #=> Procから抜けます


# Proc.new の中で break すると LocalJumpError が発生する
def proc_break_method
  p = Proc.new { break }
  p.call
  "メソッドの終わりに到達しました"
end

proc_break_method  #=> break from proc-closure (LocalJumpError)

このようにProcの中でreturnすると呼び出し元のメソッドの処理も終了します。

breakをした場合はLocalJumpErrorが発生します。

続いてlambdaです。

# lambda の中で return すると lambda_return_method 自体は続行する
def lambda_return_method
  l = lambda { return "Procから抜けます" }
  l.call
  "メソッドの終わりに到達しました"
end

lambda_return_method  #=> メソッドの終わりに到達しました


# lambda の中で break すると lambda_break_method 自体は続行する
def lambda_break_method
  l = lambda { break }
  l.call
  "メソッドの終わりに到達しました"
end

lambda_break_method  #=> メソッドの終わりに到達しました

lambdaの場合はreturnbreakをしても呼び出し元のメソッドには影響せず、そのまま処理が続きます。

そのため、Procと比べてlambdaの方がより通常のメソッドと似た感覚で使えると言えます。

Rails開発でProcオブジェクトが使われる場面

Rails開発においてはProcオブジェクト、特にlambdaが広く使われています。

lambdaは引数の数に厳格であり、returnbreakの挙動が予期しやすいため、安全性が求められる場面で多用されているのです。

典型的な例を確認してみます。

app/models/post.rb
class Post < ApplicationRecord
  scope :published, -> { where(published: true) }
end

これはActiveRecordのスコープです。

scopeメソッドの第2引数に、->を使って生成されたlambdaが渡されています。

その他に、コールバックの設定や条件付きバリデーションなどでもlambdaが使用されています。

app/models/user.rb
class User < ApplicationRecord
  before_save -> { self.name = name.capitalize }
end
app/models/order.rb
class Order < ApplicationRecord
  validates :card_number, presence: true, if: -> { payment_type == 'credit_card' }
end

このように、Rails開発では主にlambdaがいたる所で活用されています。

おわりに

この記事ではProc.newlambdaで生成されるProcクラスのオブジェクトの挙動の違いについてまとめました。

またRails開発においても、モデルを中心に様々な場面で主にlambdaが活用されていることを確認しました。

間違いなどありましたら、ご指摘いただけると幸いです。

【09/01追記】挙動の異なる2種類のProcオブジェクトの呼称について

コメントいただきましたので追記します。

記事の中で以下の内容を書きました。

  • Proc.newlambdaはどちらもProcクラスのオブジェクトを生成する。
  • ただし挙動が異なるため、Proc.newで作られたオブジェクトはProclambdaで作られたオブジェクトはlambdaと呼称を区別することがある

たとえば『メタプログラミングRuby 第2版』p94では

lambda で作られた Proc オブジェクトは lambda と呼ばれる。もう一方は、単純に Proc と呼ばれる

と記載されています。

一方でこの呼び分けを避けている人も多いです。

引数にProcを渡すといった言い回しが出てきた場合に、Procという表現がProcクラスのオブジェクトProc.newで作られたオブジェクトのどちらを指しているかが分かりにくいからです。

業務や書籍の中でProcという表現が出てきたら、それがProcクラスのオブジェクト全体を指しているのか、それともProc.newで作られたオブジェクトを指しているのか注意深く確認し、混乱を防ぎましょう。

参考資料

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?