Ruby 블록과 Lambda
Ruby의 블록(Block)과 Lambda에 대해 알아봅니다.
블록 (Blocks)
블록은 메서드 호출과 함께 코드 블록을 전달하는 방법입니다.
기본 사용법
def call_block
puts 'Start of method'
yield
yield
puts 'End of method'
end
call_block { puts 'In the block' }
파라미터가 있는 블록
def call_block
yield('hello', 99)
end
call_block { |str, num| puts str + ' ' + num.to_s }
블록 존재 여부 확인
def try
if block_given?
yield
else
puts "no block"
end
end
try # "no block"
try { puts "hello" } # "hello"
try do puts "hello" end # "hello"
블록을 통한 반복
x = 10
5.times do |x|
puts "x inside the block: #{x}"
end
puts "x outside the block: #{x}"
Lambda
Lambda는 익명 함수를 만드는 방법입니다.
기본 사용법
prc = lambda { puts 'Hello' }
prc.call
# 여러 줄 Lambda
toast = lambda do
'Cheers'
end
puts toast.call
Lambda를 메서드 파라미터로 전달
def some_mtd(some_proc)
puts 'Start of mtd'
some_proc.call
puts 'End of mtd'
end
say = lambda do
puts 'Hello'
end
some_mtd(say)
파라미터가 있는 Lambda
a_Block = lambda { |x| "Hello #{x}!" }
puts a_Block.call('World') # "Hello World!"
Proc
Proc은 블록을 객체로 저장한 것입니다. Lambda와 비슷하지만 몇 가지 중요한 차이점이 있습니다.
Proc 생성
my_proc = Proc.new { |x| puts "Hello, #{x}!" }
my_proc.call('World') # "Hello, World!"
# 또 다른 생성 방법
my_proc = proc { |x| puts "Hello, #{x}!" }
Lambda와 Proc의 차이점
두 가지 핵심 차이점을 이해하는 것이 중요합니다.
1. 인자 검사:
# Lambda는 인자 개수를 엄격하게 검사
my_lambda = lambda { |x, y| x + y }
my_lambda.call(1, 2) # 3
# my_lambda.call(1) # ArgumentError 발생
# Proc은 인자 개수가 달라도 에러가 나지 않음
my_proc = Proc.new { |x, y| "#{x}, #{y}" }
my_proc.call(1) # "1, " (y는 nil)
my_proc.call(1, 2, 3) # "1, 2" (3은 무시)
2. return 동작:
# Lambda의 return은 Lambda 자체에서 복귀
def test_lambda
my_lambda = lambda { return 10 }
result = my_lambda.call
puts "Lambda returned: #{result}" # 실행됨
return 20
end
# Proc의 return은 감싸고 있는 메서드에서 복귀
def test_proc
my_proc = Proc.new { return 10 }
my_proc.call
puts "이 줄은 실행되지 않습니다" # 실행되지 않음
return 20
end
test_lambda # "Lambda returned: 10", 반환값 20
test_proc # 반환값 10
화살표 Lambda (Stabby Lambda)
Ruby 1.9부터 더 간결한 Lambda 문법을 제공합니다.
# 기존 Lambda
multiply = lambda { |x, y| x * y }
# 화살표 Lambda (Stabby Lambda)
multiply = ->(x, y) { x * y }
puts multiply.call(3, 4) # 12
# .call 대신 다른 호출 방법
multiply.(3, 4) # 12
multiply[3, 4] # 12
메서드를 Proc으로 변환
기존 메서드를 method 메서드로 Proc 객체로 변환할 수 있습니다.
def double(x)
x * 2
end
# 메서드를 Proc으로 변환
double_proc = method(:double)
[1, 2, 3, 4].map(&double_proc) # [2, 4, 6, 8]
# 심볼을 Proc으로 변환하는 & 연산자
['hello', 'world'].map(&:upcase) # ["HELLO", "WORLD"]
[1, 2, 3, 4].select(&:even?) # [2, 4]
&:method_name 패턴은 Ruby에서 매우 자주 사용되는 관용구입니다. Symbol#to_proc을 호출하여 각 요소에 해당 메서드를 적용하는 Proc을 자동으로 생성합니다.
실무에서의 블록 활용
블록은 Ruby의 많은 디자인 패턴에서 핵심적으로 사용됩니다.
리소스 관리 패턴
# 파일을 열고 블록이 끝나면 자동으로 닫기
File.open('data.txt', 'r') do |file|
file.each_line { |line| puts line }
end
# 여기서는 file이 이미 닫힌 상태
# 데이터베이스 트랜잭션
ActiveRecord::Base.transaction do
user.save!
order.save!
# 예외 발생 시 자동 롤백
end
설정 패턴 (Configuration Block)
class Server
attr_accessor :host, :port
def initialize
yield(self) if block_given?
end
end
server = Server.new do |s|
s.host = 'localhost'
s.port = 8080
end
이 패턴은 Rails의 configuration이나 RSpec의 테스트 정의 등 Ruby 생태계 전반에서 광범위하게 사용됩니다.
Comments