while
while (반복문)
어떤 코드를 조건이 참인 동안 반복해서 실행하고 싶을 때 쓰는 게 while이에요. 조건을 먼저 검사하고, 참이면 몸체를 실행해요. 조건이 처음부터 거짓이라면 몸체는 한 번도 실행되지 않을 수 있어요.
본문
while은 조건이 _truthy_한 동안 몸체를 실행해요.
while some_condition
do_this
end
조건을 먼저 검사하고, _truthy_하면 몸체를 실행해요. 즉 몸체가 한 번도 실행되지 않을 수도 있어요.
if와 비슷하게, while의 조건이 변수라면 그 변수는 몸체 안에서 nil이 아님을 보장받아요. 조건이 var.is_a?(Type) 검사라면 var는 몸체 안에서 타입 Type임을 보장받아요. 그리고 조건이 var.responds_to?(:method)라면 var는 그 메서드에 응답하는 타입임을 보장받아요.
while이 끝난 뒤의 변수 타입은 while 이전의 타입과 while의 몸체를 떠나기 직전의 타입에 따라 달라져요.
a = 1
while some_condition
# a : Int32 | String
a = "hello"
# a : String
a.size
end
# a : Int32 | String
반복이 끝났을 때 몸체를 안 들어갔을 수도 있고, 몸체 안에서 String을 할당하고 나왔을 수도 있으니, while 이후의 타입은 초기 타입과 몸체에서의 타입을 합친 유니온이 되는 거예요.
루프 끝에서 조건 확인하기
몸체를 적어도 한 번은 실행한 뒤에 빠져나갈 조건을 확인하고 싶다면 이렇게 할 수 있어요.
while true
do_something
break if some_condition
end
또는 표준 라이브러리의 loop를 사용해도 돼요.
loop do
do_something
break if some_condition
end
표현식으로서의 while
while의 값은 while의 몸체를 빠져나가는 break 표현식의 값이에요.
a = 0
x = while a < 5
a += 1
break "four" if a == 4
break "three" if a == 3
end
x # => "three"
만약 while 루프가 정상적으로 끝나면(조건이 거짓이 되어서) 그 값은 nil이에요.
x = while 1 > 2
break 3
end
x # => nil
인자 없이 쓰인 break 표현식도 nil을 반환해요.
x = while 2 > 1
break
end
x # => nil
여러 인자를 가진 break 표현식은 Tuple 인스턴스로 묶여요.
x = while 2 > 1
break 3, 4
end
x # => {3, 4}
typeof(x) # => Tuple(Int32, Int32)
while의 타입은 몸체 안의 모든 break 표현식 타입들의 유니온에 Nil을 더한 값이에요. 조건이 거짓이 될 수 있기 때문이에요.
x = while 1 > 2
if rand < 0.5
break 3
else
break '4'
end
end
typeof(x) # => (Char | Int32 | Nil)
하지만 조건이 정확히 true 리터럴이라면, 그 영향은 반환값과 반환 타입에서 제외돼요.
x = while true
break 1
end
x # => 1
typeof(x) # => Int32
특히, break가 하나도 없는 while true 표현식은 같은 스코프에서 절대 빠져나갈 수 없으므로 NoReturn 타입을 가져요.
x = while true
puts "yes"
end
x # unreachable
typeof(x) # => NoReturn