💎
【Ruby 31日目】基本文法 - 引数の種類(デフォルト/可変長)
はじめに
Rubyの引数の種類(デフォルト引数/可変長引数)について、Ruby 3.4の仕様に基づいて詳しく解説します。
この記事では、基本的な概念から実践的な使い方まで、具体的なコード例を交えて説明します。
基本概念
Rubyのメソッド引数には以下の種類があります:
- 必須引数 - 呼び出し時に必ず指定する必要がある引数
- デフォルト引数 - 省略時にデフォルト値が使用される引数
-
可変長引数(
*args) - 任意の数の引数を受け取る - オプション引数 - デフォルト値を持つ引数
これらを組み合わせることで、柔軟なメソッドインターフェースを設計できます。
基本的な使い方
必須引数
def greet(name)
"Hello, #{name}!"
end
puts greet("Alice") #=> Hello, Alice!
# greet() # ArgumentError: wrong number of arguments (given 0, expected 1)
デフォルト引数
def greet(name, greeting = "Hello")
"#{greeting}, #{name}!"
end
puts greet("Alice") #=> Hello, Alice!
puts greet("Bob", "Hi") #=> Hi, Bob!
puts greet("Charlie", "Hey") #=> Hey, Charlie!
複数のデフォルト引数
def create_user(name, age = 20, country = "Japan")
"Name: #{name}, Age: #{age}, Country: #{country}"
end
puts create_user("Alice") #=> Name: Alice, Age: 20, Country: Japan
puts create_user("Bob", 25) #=> Name: Bob, Age: 25, Country: Japan
puts create_user("Charlie", 30, "USA") #=> Name: Charlie, Age: 30, Country: USA
可変長引数(*args)
def sum(*numbers)
numbers.reduce(0, :+)
end
puts sum(1, 2, 3) #=> 6
puts sum(1, 2, 3, 4, 5) #=> 15
puts sum() #=> 0
必須引数とデフォルト引数の組み合わせ
def build_url(host, path = "/", protocol = "https")
"#{protocol}://#{host}#{path}"
end
puts build_url("example.com") #=> https://example.com/
puts build_url("example.com", "/api") #=> https://example.com/api
puts build_url("example.com", "/api", "http") #=> http://example.com/api
可変長引数と通常引数の組み合わせ
def format_list(title, *items)
result = "#{title}:\n"
items.each_with_index do |item, index|
result += " #{index + 1}. #{item}\n"
end
result
end
puts format_list("My Tasks", "Buy groceries", "Clean room", "Study Ruby")
#=> My Tasks:
# 1. Buy groceries
# 2. Clean room
# 3. Study Ruby
よくあるユースケース
ケース1: ログメッセージのフォーマット
デフォルト引数を使用してログレベルを指定します。
class Logger
def log(message, level = :info, timestamp = Time.now)
"[#{timestamp.strftime('%Y-%m-%d %H:%M:%S')}] [#{level.to_s.upcase}] #{message}"
end
end
logger = Logger.new
puts logger.log("Application started")
#=> [2025-11-07 10:00:00] [INFO] Application started
puts logger.log("Something went wrong", :error)
#=> [2025-11-07 10:00:01] [ERROR] Something went wrong
custom_time = Time.new(2025, 1, 1, 0, 0, 0)
puts logger.log("Happy New Year!", :info, custom_time)
#=> [2025-01-01 00:00:00] [INFO] Happy New Year!
ケース2: データベースクエリビルダー
可変長引数を使用して複数の条件を受け取ります。
class QueryBuilder
def where(*conditions)
if conditions.empty?
"SELECT * FROM users"
else
"SELECT * FROM users WHERE #{conditions.join(' AND ')}"
end
end
end
query = QueryBuilder.new
puts query.where
#=> SELECT * FROM users
puts query.where("age > 18")
#=> SELECT * FROM users WHERE age > 18
puts query.where("age > 18", "country = 'Japan'", "active = true")
#=> SELECT * FROM users WHERE age > 18 AND country = 'Japan' AND active = true
ケース3: 設定オブジェクトの初期化
デフォルト値を使用して、柔軟な設定を可能にします。
class ServerConfig
attr_reader :host, :port, :timeout, :ssl
def initialize(host, port = 3000, timeout = 30, ssl = true)
@host = host
@port = port
@timeout = timeout
@ssl = ssl
end
def url
protocol = ssl ? "https" : "http"
"#{protocol}://#{host}:#{port}"
end
end
# 最小限の設定
server1 = ServerConfig.new("localhost")
puts server1.url #=> https://localhost:3000
# カスタム設定
server2 = ServerConfig.new("api.example.com", 8080, 60, false)
puts server2.url #=> http://api.example.com:8080
ケース4: 数学関数の計算
可変長引数を使って複数の値を処理します。
class MathUtils
def self.average(*numbers)
return 0 if numbers.empty?
numbers.sum.to_f / numbers.size
end
def self.max(*numbers)
return nil if numbers.empty?
numbers.max
end
def self.stats(*numbers)
return {} if numbers.empty?
{
count: numbers.size,
sum: numbers.sum,
average: average(*numbers),
min: numbers.min,
max: numbers.max
}
end
end
puts MathUtils.average(1, 2, 3, 4, 5) #=> 3.0
puts MathUtils.max(10, 5, 20, 15) #=> 20
puts MathUtils.stats(1, 2, 3, 4, 5).inspect
#=> {:count=>5, :sum=>15, :average=>3.0, :min=>1, :max=>5}
ケース5: テキストフォーマッター
複数のオプションを持つメソッドで、デフォルト値を活用します。
class TextFormatter
def format(text,
uppercase: false,
trim: true,
max_length: nil,
suffix: "...")
result = text
result = result.strip if trim
result = result.upcase if uppercase
if max_length && result.length > max_length
result = result[0...max_length] + suffix
end
result
end
end
formatter = TextFormatter.new
puts formatter.format(" hello world ")
#=> hello world
puts formatter.format("hello world", uppercase: true)
#=> HELLO WORLD
puts formatter.format("This is a very long text", max_length: 10)
#=> This is a ...
puts formatter.format(" ruby ", trim: false, uppercase: true)
#=> RUBY
注意点とベストプラクティス
注意点
- 引数の順序に注意
# BAD: デフォルト引数の後に必須引数を置けない(Ruby 2.0以前)
# def bad_example(a = 1, b) # SyntaxError
# end
# GOOD: Ruby 2.0以降では可能だが、推奨されない
def confusing_example(a = 1, b, c = 2)
"a=#{a}, b=#{b}, c=#{c}"
end
puts confusing_example(10, 20) #=> a=10, b=20, c=2
# 分かりにくいので避けるべき
# BETTER: 必須引数を先に配置
def clear_example(b, a = 1, c = 2)
"a=#{a}, b=#{b}, c=#{c}"
end
puts clear_example(20) #=> a=1, b=20, c=2
- デフォルト値の評価タイミング
# BAD: デフォルト値は毎回評価される
def add_item(item, list = [])
list << item
list
end
# 予期しない動作:デフォルト値が再利用される
# puts add_item("a").inspect #=> ["a"]
# puts add_item("b").inspect # Ruby 2.xでは問題が発生する可能性
# GOOD: nilをデフォルトにして内部で初期化
def add_item_safe(item, list = nil)
list ||= []
list << item
list
end
puts add_item_safe("a").inspect #=> ["a"]
puts add_item_safe("b").inspect #=> ["b"]
- 可変長引数の位置
# 可変長引数の後に通常引数を配置する場合
def complex_method(*items, last_item)
"Items: #{items.inspect}, Last: #{last_item}"
end
puts complex_method(1, 2, 3, 4) #=> Items: [1, 2, 3], Last: 4
# 可変長引数の前後に引数を配置
def very_complex(first, *middle, last)
"First: #{first}, Middle: #{middle.inspect}, Last: #{last}"
end
puts very_complex(1, 2, 3, 4, 5) #=> First: 1, Middle: [2, 3, 4], Last: 5
ベストプラクティス
- デフォルト値は不変オブジェクトを使用
# GOOD: 不変オブジェクト(数値、文字列、シンボル、true/false/nil)
def create_user(name, role = :guest, active = true, retry_count = 3)
{ name: name, role: role, active: active, retry_count: retry_count }
end
user = create_user("Alice")
puts user.inspect #=> {:name=>"Alice", :role=>:guest, :active=>true, :retry_count=>3}
- 多くのオプションがある場合はキーワード引数を検討
# 引数が多い場合はキーワード引数の方が分かりやすい
def create_connection(
host:,
port: 3306,
username: "root",
password: "",
database: "myapp",
timeout: 30
)
"Connecting to #{username}@#{host}:#{port}/#{database}"
end
puts create_connection(host: "localhost", password: "secret")
#=> Connecting to root@localhost:3306/myapp
- 可変長引数は意図を明確に
# GOOD: 可変長引数の用途が明確
def join_paths(*paths)
paths.join('/')
end
puts join_paths("home", "user", "documents") #=> home/user/documents
def calculate_total(tax_rate = 0.1, *prices)
subtotal = prices.sum
total = subtotal * (1 + tax_rate)
{ subtotal: subtotal, tax: subtotal * tax_rate, total: total }
end
puts calculate_total(0.08, 100, 200, 300).inspect
#=> {:subtotal=>600, :tax=>48.0, :total=>648.0}
Ruby 3.4での改善点
- Prismパーサーによる引数解析の最適化 - デフォルト引数や可変長引数の解析が高速化
- YJITの最適化 - デフォルト引数を持つメソッド呼び出しのパフォーマンスが向上
- エラーメッセージの改善 - 引数の数が合わない場合のエラーメッセージがより詳細に
- 引数転送(...)との組み合わせ - Ruby 2.7で導入された引数転送構文がより安定的に動作
まとめ
この記事では、引数の種類(デフォルト/可変長)について以下の内容を学びました:
- 基本概念と重要性 - 必須引数、デフォルト引数、可変長引数の違い
- 基本的な使い方と構文 - 各種引数の定義方法と組み合わせ
- 実践的なユースケース - ログ、クエリビルダー、設定管理、数学関数、テキストフォーマット
- 注意点とベストプラクティス - 引数の順序、デフォルト値の評価、キーワード引数の活用
柔軟な引数設計により、使いやすく保守性の高いメソッドを実装できます。
Discussion