루아(Lua)에 없는 continue 이야기

Twitter icon류광, 2012-04-22 19:04
루아에 continue가 왜 없는지, 대안은 무엇인지 적어 보았습니다. 5.2.x의 goto 이야기도 조금.

루아의 반복 구조는 for(두 종류), while, repeat입니다. 그런데 이들 모두 break는 지원하지만 continue는 지원하지 않습니다. 이에 대해 루아의 설계, 구현자 중 한 명인 Roberto Ierusalimschy는 다음과 같이 해명한 적이 있습니다.

"... Our main concern with "continue" is that there are several other control structures that (in our view) are more or less as important as "continue" and may even replace it. (E.g., break with labels [as in Java] or even a more generic goto.) "continue" does not seem more special than other control-structure mechanisms, except that it is present in more languages."

이를 "Go to Statement Considered Harmful"과 연관시켜서, continue가 goto보다 나을 것이 없으니 당연히 지원 안한다 정도로 이해하고 넘어갈 수도 있겠습니다. 그러나 사실은 글자 그대로, continue가 다른 제어 구조보다 더 특별하지 않기 때문에 지원하지 않았던 것일 뿐임이 밝혀졌습니다. 루아 5.2에서 goto를 지원하기 시작한 것입니다.

따라서 루아 5.2부터는 continue 기능을 이런 식으로 구현할 수 있습니다.

while cond do

  ...

  if another_cond then goto continue end

  ...

  @continue: 
end

goto가 없는 5.2 미만의 루아에서는 다른 방식으로 continue를 흉내낼 수 있습니다. 그런데 continue가 goto와 다를 바 없다는 주장과 관련해서, 관점에 따라서는 continue를 goto가 아니라 '이른 return', 즉 함수의 끝이 아닌 곳에 있는 return 문으로 생각할 수도 있습니다. 루프라는 것을, 루프 본문이 하나의 함수이고 그것을 반복해서 '호출'하는 것으로 본다면 말이죠. 그리고 이른 return은 goto에 비해 혐오감(?)이 덜하고, 심지어는 가독성이나 코드의 복잡도 측면에서 권장되기도 합니다. 특히 이른 return은 복잡한 if 문 중첩을 줄이는 데 효과적인데요. if 문 단순화는 continue의 주된 장점이기도 합니다.

각설하고, 다음처럼 루프의 본문을 하나의 함수로 구현한다면,

while not cond_var do
  cond_var = loop_body()
end

함수 loop_body() 안의 이른 return은 곧 continue와 같은 효과를 냅니다. 그리고 반환값을 적절히 설정함으로써 break의 효과도 낼 수 있습니다. 이제 loop_body를 익명 함수로 대체하면 루아 5.1에서 break와 continue를 지원하는 while 루프가 됩니다:

while not cond_var do
  cond_var = (function()

    .. 루프 본문 ...

    if some_cond then return end -- continue

    ...

    if some_cond then return "break" end -- break

    ...

  end)()
end

(참고: 익명 함수를 즉석에서 호출하려면 반드시 함수 전체를 괄호로 감싸야 합니다. 그리고 문자열 'break'를 돌려준 것은 가독성을 위한 것일 뿐, 참으로 판정되는 값이면 어떤 것이든 가능합니다.)

for ..in 루프처럼 종료 조건을 외부에서 강제하기가 힘들거나 불가능한 경우에는 함수 호출 밖에서 break를 명시적으로 실행할 필요가 있습니다.

for i, v in ipairs(t) do  if (function()

  .. 루프 본문 ...

  if some_cond then return end -- continue

  ...

  if some_cond then return "break" end -- break

  ...

end)() then break end; end

(참고: for-if 가 논리적으로 하나의 루프 구조임을 강조하기 위해 같은 줄에 표기했습니다.)

마지막으로 익명 함수의 생성과 호출이 성능에 부담이 될 것 같아서 꺼려지는 경우라면 다른 방법들도 있습니다. 다음은 http://lua-users.org/wiki/ContinueProposal 에서 가져온 예들입니다.

-- repeat를 이용한 방법, break 지원 안함.
for line in io.lines() do repeat
    if string.find(line,"c") then break end
    print("-->",line)
until 1 end

-- repeat를 이용한 방법, break 지원.
for line in io.lines() do

   local continue
   repeat
      -- start BODY
      if string.find(line,"c") then continue=true; break end

      if string.find(line,"b") then break end

      print("-->",line)
      -- end BODY

      continue=true
   until 1
   if not continue then break end

end

-- error()를 이용한 방법
while cond do
  local b, err = pcall(function()
    ...some code...
    error("continue") -- the 'continue' equivalent :
    ...some code...
  end)
  -- if there is another error :
  if not b and err ~= "continue" then error(err) end
end

대안들을 살펴보면 '루아 개발자님들아 그냥 continue를 지원해 주라'라는 말이 나올 수도 있겠습니다... 그러나 goto가 등장한 만큼 조만간 continue가 지원될 가능성은 적어 보입니다.

태그: 프로그래밍 Lua

comments powered by Disqus