while

while (반복문)

어떤 코드를 조건이 참인 동안 반복해서 실행하고 싶을 때 쓰는 게 while이에요. 조건을 먼저 검사하고, 참이면 몸체를 실행해요. 조건이 처음부터 거짓이라면 몸체는 한 번도 실행되지 않을 수 있어요.

출처: Crystal 공식 문서 - 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

더 알아보기

  • 루프를 빠져나가는 방법은 break 문서에서 다뤄요.
  • 반복을 건너뛰는 방법은 next 문서에서 확인할 수 있어요.