루아에는 왜“계속”진술이 없습니까?


144

지난 몇 달 동안 Lua를 많이 다루어 왔으며 대부분의 기능을 정말로 좋아하지만 여전히 그중 일부가 빠져 있습니다.

  • continue없습니까?
  • 어떤 해결 방법이 있습니까?

12
이 질문이 제기 된 후 Lua는 goto계속 구현하는 데 사용할 수 있는 진술을 받았습니다 . 아래 답변을 참조하십시오.
lhf

답변:


71

Lua 5.2에서 가장 좋은 해결 방법은 goto를 사용하는 것입니다.

-- prints odd numbers in [|1,10|]
for i=1,10 do
  if i % 2 == 0 then goto continue end
  print(i)
  ::continue::
end

버전 2.0.1 이후 LuaJIT에서 지원됩니다.


47
나는 그들이 실제로 continue하루 를 포함하기를 바랍니다 . goto교체는 아주 좋은 모양과 더 라인을 필요로하지 않습니다. 또한 하나의 함수 에서이 작업을 수행하는 루프가 두 개 이상인 경우 문제가 발생하지 않습니다 ::continue::. 루프 당 이름을 만드는 것은 괜찮은 일처럼 들리지 않습니다.
동부 표준시

66

언어는 어휘 범위를 관리하는 방법은 모두를 포함하여 문제 생성 gotocontinue. 예를 들어

local a=0
repeat 
    if f() then
        a=1 --change outer a
    end
    local a=f() -- inner a
until a==0 -- test inner a

local a내부 루프 본문 선언은 명명 된 외부 변수를 숨기고 a해당 로컬의 범위는 until명령문 의 조건에 걸쳐 확장 되므로 조건이 가장 안쪽을 테스트합니다 a.

continue존재하는 경우 조건에 사용 된 모든 변수가 범위에 들어간 후에 만 ​​의미 적으로 유효하도록 제한되어야합니다. 이것은 사용자에게 문서화하고 컴파일러에서 적용하기 어려운 조건입니다. 이 문제를 해결 다양한 제안 허용하지의 간단한 대답을 포함하여 논의되었다 continuerepeat ... until루프 스타일. 지금까지 아무도 언어에 포함시킬만큼 충분한 유스 케이스가 없었습니다.

해결 방법은 일반적으로 a continue를 실행 하게하는 조건을 반전시키고 해당 조건 에서 나머지 루프 본문을 수집하는 것입니다. 따라서 다음 루프

-- not valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
  if isstring(k) then continue end
  -- do something to t[k] when k is not a string
end

쓸 수 있었다

-- valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
  if not isstring(k) then 
    -- do something to t[k] when k is not a string
  end
end

루프 작업을 제어하는 ​​일련의 정교한 컬이없는 한 충분히 명확하며 일반적으로 부담이되지 않습니다.


5
파이썬 배경에서 오는 것은 혼란스러운 대답입니다. 왜냐하면 모든 범위는 실행하기 전에 로컬 변수가 무엇인지 이미 알고 있기 때문입니다. 즉,에 도달하는 경우 언 바운드 로컬 변수 오류가 예상되었습니다 until....
ubershmekel

2
gotoLua 5.2에 도입하기 전에 Lua 커뮤니티에서 이에 대해 많은 논의가있었습니다 . 당연히 goto같은 문제가 있습니다. 그들은 결국 런타임 및 / 또는 코드 생성 비용을 막기 위해 무엇이든 멀티 레벨과 goto에뮬레이션에 모두 사용할 수 있는 유연성 을 갖는 이점이 있다고 판단했습니다 . 세부 정보를 얻으려면 관련 스레드에 대한 Lua 목록 아카이브 를 검색해야합니다 . 그들이 도입했기 때문에 분명히 극복 할 수 없었습니다. continuebreakgoto
RBerteig

72
계속하지 않고 코드를 작성하는 것에 대해 "충분히 명확한"것은 없습니다. 계속이 사용되어야하는 조건부 안에 코드를 중첩시키는 것은 초보적인 실수이며, 이와 같이 추한 코드를 작성해야 할 필요는 없습니다. 변명의 여지가 없습니다.
Glenn Maynard

4
이 설명은 의미가 없습니다. local컴파일러 전용 지시문입니다-런타임 인스트루먼트 local와 변수 사용법 사이에 상관 이 없습니다. 동일한 범위 지정 동작을 유지하기 위해 컴파일러에서 아무것도 변경할 필요가 없습니다. 그렇습니다. 이것은 분명하지 않으며 추가 문서가 필요하지만 다시 반복하려면 컴파일러에서 ZERO 변경이 필요합니다. repeat do break end until true내 대답의 예는 이미 컴파일러가 계속하는 것과 동일한 바이트 코드를 생성 하지만 유일한 차이점은 continue사용하기 위해 추악한 추가 구문이 필요하지 않다는 것입니다.
Oleg V. Volkov

7
내부 변수를 테스트 할 수 있다는 것은 결함있는 디자인에 대한 것입니다. 조건이 내부 범위를 벗어 났으므로 해당 범위 내의 변수에 액세스 할 수 없습니다. C :의 do{int i=0;}while (i == 0);실패 또는 C ++ 의 동등도 고려하십시오 do int i=0;while (i==0);( "이 범위에서 선언되지 않았습니다"). 불행히도 Lua에서 지금 변경하기에는 너무 늦었습니다.
Pedro Gimeno

47

루프 바디를 추가로 포장 repeat until true한 다음 do break end계속 을 위해 내부를 사용할 수 있습니다 . 물론 실제로 break루프를 벗어나 려면 추가 플래그를 설정해야합니다 .

5 번 반복되며 매번 1, 2, 3을 인쇄합니다.

for idx = 1, 5 do
    repeat
        print(1)
        print(2)
        print(3)
        do break end -- goes to next iteration of for
        print(4)
        print(5)
    until true
end

이 구조 JMP는 루아 바이트 코드 에서 문자 그대로 하나의 opcode 로 변환됩니다 !

$ luac -l continue.lua 

main <continue.lua:0,0> (22 instructions, 88 bytes at 0x23c9530)
0+ params, 6 slots, 0 upvalues, 4 locals, 6 constants, 0 functions
    1   [1] LOADK       0 -1    ; 1
    2   [1] LOADK       1 -2    ; 3
    3   [1] LOADK       2 -1    ; 1
    4   [1] FORPREP     0 16    ; to 21
    5   [3] GETGLOBAL   4 -3    ; print
    6   [3] LOADK       5 -1    ; 1
    7   [3] CALL        4 2 1
    8   [4] GETGLOBAL   4 -3    ; print
    9   [4] LOADK       5 -4    ; 2
    10  [4] CALL        4 2 1
    11  [5] GETGLOBAL   4 -3    ; print
    12  [5] LOADK       5 -2    ; 3
    13  [5] CALL        4 2 1
    14  [6] JMP         6   ; to 21 -- Here it is! If you remove do break end from code, result will only differ by this single line.
    15  [7] GETGLOBAL   4 -3    ; print
    16  [7] LOADK       5 -5    ; 4
    17  [7] CALL        4 2 1
    18  [8] GETGLOBAL   4 -3    ; print
    19  [8] LOADK       5 -6    ; 5
    20  [8] CALL        4 2 1
    21  [1] FORLOOP     0 -17   ; to 5
    22  [10]    RETURN      0 1

4
이 답변은 훌륭하지만 여전히 한 줄 대신 3 줄이 필요합니다. ( "continue"가 올바르게 지원 된 경우) goto 레이블보다 조금 더 안전하고 안전합니다. 중첩 루프에서는 해당 이름의 충돌을 피해야하기 때문입니다.
동부 표준시

3
그러나 각 유사-연속마다 새로운 식별자 / 라벨을 만들 필요가없고 시간이 지남에 따라 코드가 수정 될 때 오류가 덜 발생한다는 점에서 goto의 "실제"문제를 피하십시오. 나는 continue가 유용 할 것이라는 데 동의 하지만,이 IMO는 차선책 일 것입니다. (그리고 더 공식적인 "continue;"에 대해 반복 / 완료를 위해 실제로는 두 줄이 필요합니다. : 카운트 당신은 항상 예를 들어, "사실이 끝날 때까지" "반복을"쓸 수 gist.github.com/wilson0x4d/f8410719033d1e0ef771를 )
숀 윌슨

1
사람들이 실제로 성능을 고려하고 심지어 luac출력을 제공하는 것을 보게 되어 기쁩니다! 잘 받아
들여진 공감대를 가지십시오

17

루아 자신의 디자이너로부터 직접 :

"계속"에 대한 우리의 주요 관심사는 (계속해서) "계속"만큼 중요하거나이를 대체 할 수있는 몇 가지 다른 제어 구조가 있다는 것입니다. (예를 들어, [Java에서와 같이] 레이블 또는보다 일반적인 레이블로 분리하십시오.) "계속"은 더 많은 언어로 존재한다는 점을 제외하고 다른 제어 구조 메커니즘보다 더 특별 해 보이지 않습니다. (Perl에는 실제로 "next"와 "redo"라는 두 개의 "continue"문이 있습니다. 둘 다 유용합니다.)


5
나는 "우리는 그렇게하지 않을 것이다"라는 설명 직후에 "둘 다 유용하다"라는 입장을 좋아한다
David Ljung Madison Stellar

2
그것은 그들이 그 범위를 주목했다 되었다 가 때 주소로보고 했다 그것을 5.2에서 "고토"구조를 (어떤이 답변이 작성된 경우 공개되지 않은)를 추가하여. 5.2.0이 출시 된 후 2012 년이 답변을 참조하십시오 .
스튜어트 P. 벤틀리

3
맞습니다- 'goto'는 적절한 프로그래밍 구조로 잘 알려져 있기 때문입니다. (잘 말해서) 아 잘.
David Ljung Madison Stellar

2
그러나 " continue루아에 빠뜨리는 것을 잊어 버렸습니다 ." 보다 더 합리적으로 들리지 않았습니다 .
neoedmund

17

첫 번째 부분은에 대한 답변 자주 묻는 질문 으로 죽임을 지적 아웃.

해결 방법은 루프 본문을 함수로 감싸서 return일찍 시작할 수 있습니다.

-- Print the odd numbers from 1 to 99
for a = 1, 99 do
  (function()
    if a % 2 == 0 then
      return
    end
    print(a)
  end)()
end

또는 기능 breakcontinue기능을 모두 원한다면 로컬 기능으로 테스트를 수행하십시오.

local a = 1
while (function()
  if a > 99 then
    return false; -- break
  end
  if a % 2 == 0 then
    return true; -- continue
  end
  print(a)
  return true; -- continue
end)() do
  a = a + 1
end

16
제발 하지마 각 반복마다 클로저 환경을 생성하면 메모리 및 GC주기가 크게 낭비됩니다.
Oleg V. Volkov

4
collectgarbage("count")당신의 간단한 100 번 시도 후에도 가면 우리가 이야기 할 것입니다. 이러한 "미숙 한"최적화는 지난 주에 1 분마다 하나의 하이로드 프로젝트가 재부팅되는 것을 막았습니다.
Oleg V. Volkov

4
@ OlegV.Volkov는이 예제가 GC에 상대적으로 높은 부하를 가하는 반면 누출되지 않습니다. 모든 임시 폐쇄가 수집됩니다. 프로젝트에 대해 잘 모르지만 IME가 가장 많이 재부팅되는 것은 누수 때문입니다.
finnw

10

나는 전에 Lua를 사용한 적이 없지만, 구글 검색하여 이것을 생각해 냈습니다.

http://www.luafaq.org/

질문 1.26을 확인하십시오 .

이것은 일반적인 불만입니다. Lua의 저자는 계속이 여러 가지 새로운 제어 흐름 메커니즘 중 하나 일 뿐이라고 생각했습니다 (반복 / 정지의 범위 규칙으로 작동 할 수 없다는 사실이 보조 요인이었습니다).

Lua 5.2에는 동일한 작업을 수행하는 데 쉽게 사용할 수있는 goto 문이 있습니다.


8

우리는 아래와 같이 달성 할 수 있으며 짝수를 건너 뜁니다.

local len = 5
for i = 1, len do
    repeat 
        if i%2 == 0 then break end
        print(" i = "..i)
        break
    until true
end

O / P :

i = 1
i = 3
i = 5

6

이 시나리오는 여러 번 발생했으며 플래그를 사용하여 계속을 시뮬레이션합니다. 우리는 goto 구문도 사용하지 않도록 노력합니다.

예 : 코드는 i = 3을 제외하고 i = 1에서 i = 10까지 명령문을 인쇄하려고합니다. 또한 "loop start", loop end ","if start "및"if end "를 인쇄하여 코드에 존재하는 다른 중첩 된 명령문을 시뮬레이션합니다.

size = 10
for i=1, size do
    print("loop start")
    if whatever then
        print("if start")
        if (i == 3) then
            print("i is 3")
            --continue
        end
        print(j)
        print("if end")
    end
    print("loop end")
end

테스트 플래그로 루프의 끝 범위까지 남은 모든 명령문을 묶으면됩니다.

size = 10
for i=1, size do
    print("loop start")
    local continue = false;  -- initialize flag at the start of the loop
    if whatever then
        print("if start")
        if (i == 3) then
            print("i is 3")
            continue = true
        end

        if continue==false then          -- test flag
            print(j)
            print("if end")
        end
    end

    if (continue==false) then            -- test flag
        print("loop end")
    end
end

나는 이것이 최선의 접근 방법이라고 말하지는 않지만 우리에게 완벽하게 작동합니다.


5

Lua는 가능한 한 작게 만들고 싶은 가벼운 스크립팅 언어입니다. 예를 들어, 사전 / 사후 증가와 같은 많은 단항 연산을 사용할 수 없습니다

계속하는 대신 goto를 사용할 수 있습니다

arr = {1,2,3,45,6,7,8}
for key,val in ipairs(arr) do
  if val > 6 then
     goto skip_to_next
  end
     # perform some calculation
  ::skip_to_next::
end

4

다시 뒤집 으면 다음 코드를 간단히 사용할 수 있습니다.

for k,v in pairs(t) do
  if not isstring(k) then 
    -- do something to t[k] when k is not a string
end

반전의 문제점은 일련의 여러 조건 (예 : 사용자 입력 확인)이없는 경우가 많다는 것입니다. 그리고 도중에 어떤 시점에서도 단락이 필요할 수 있기 때문에, 반전은 조건을 연속적으로 중첩시켜야한다는 것을 의미합니다 ( "이것이 나쁜가? 그러면 탈출; 그렇지 않으면 나쁜가? 그런 다음 탈출"). 이것은 매우 간단합니다. 당신은 "이것은 괜찮습니까? 그렇다면 괜찮아요? 그렇다면 괜찮아요? 그러면 이렇게하세요"와 같은 코드로 끝납니다.
Leslie Krause

-2

왜 계속하지 않습니까?

불필요하기 때문에 ¹. 개발자가 필요로하는 상황은 거의 없습니다.

A) 1 또는 2 라이너와 같이 매우 간단한 루프가 있으면 루프 조건을 돌리면 여전히 읽을 수 있습니다.

B) 간단한 절차 적 코드 (일명 지난 세기에 코드를 작성한 방법)를 작성할 때 구조적 프로그래밍 (일명 지난 세기에 더 나은 코드를 작성한 방법)을 적용해야합니다.

C) 객체 지향 코드를 작성하는 경우 루프 본문은 하나 또는 두 개의 라이너로 표현할 수없는 경우 하나 또는 두 개의 메소드 호출로 구성되어야합니다 (이 경우 A 참조).

D) 기능 코드를 작성하는 경우 다음 반복에 대한 일반 꼬리 호출을 반환하십시오.

continue키워드 를 사용하려는 유일한 경우 는 파이썬처럼 Lua를 코딩하고 싶을 때뿐입니다 .²

어떤 해결 방법이 있습니까?

A)가 적용되지 않는 한, 어떤 경우에도 해결 방법이 필요하지 않으면 구조적, 객체 지향적 또는 기능적 프로그래밍을 수행해야합니다. 이것들은 루아가 만든 패러다임입니다. 따라서 패턴을 피하기 위해 벗어나면 언어에 맞서 싸울 것입니다 .³


몇 가지 설명 :

¹ 루아는 매우 최소한의 언어입니다. 가능한 한 적은 수의 기능을 사용하려고 시도 continue하며 그 의미에서 진술은 필수적인 기능이 아닙니다.

이 미니멀리즘 철학은 2019 년 인터뷰 에서 Roberto Ierusalimschy 가 잘 포착 한 것 같습니다.

우리는 최종 결론이 대부분의 사람들을 만족시키지 않을 것이며 모든 사람이 원하는 모든 옵션을 넣지 않을 것이라는 것을 이해하므로 아무것도 넣지 않습니다. 결국, 엄격 모드는 합리적인 절충안입니다.

² 스크립트를 작성하려는 모든 프로그램에서이를 사용하기 때문에 많은 언어가 다른 언어에서 루아로 온 많은 프로그래머가있는 것 같습니다. "루아에 X 기능이없는 이유는 무엇입니까?"

Matz최근 인터뷰 에서 Ruby와 비슷한 상황을 설명했습니다 .

가장 인기있는 질문은 "저는 언어 X 커뮤니티에서 왔으며 언어 X에서 루비로 기능을 소개 할 수 없습니까?"라는 것입니다. 그리고 우리가 다른 언어 디자인과 다른 언어 개발 정책을 가지고 있기 때문에 이러한 요청에 대한 나의 일반적인 대답은…“아니요.

³이 문제를 해결하기위한 몇 가지 방법이 있습니다. 일부 사용자는을 사용하도록 제안 goto했는데, 이는 대부분의 경우에 충분한 근사치이지만 매우 빠르게 추악 해지고 중첩 루프로 완전히 중단됩니다. 또한 gotos를 사용하면 다른 사람에게 코드를 보여줄 때마다 SICP 사본이 발생할 위험이 있습니다.


1
첫 번째 문장은 분명히 거짓이어서 나머지 답변은 도움이되지 않기 때문에 나는 하향 투표했다.
bfontaine

도움이되지 않는? 아마도; 다소 의견에 기반한 답변입니다. 첫 번째 문장은 분명히 사실입니다. continue편리한 기능 일 수도 있지만 반드시 필요한 것은 아닙니다 . 많은 사람들이 루아를 사용하지 않고 잘 사용하므로 프로그래밍 언어에 필수적이지 않은 깔끔한 기능 이외의 다른 사례는 없습니다.
DarkWiiPlayer

그것은 논쟁이 아닙니다. 사람들은 선택의 여지가 없을 때 "그것없이 훌륭하다"고 주장 할 수 없습니다.
bfontaine

그때 우리는 "필수"에 대한 다른 정의를 가지고 있다고 생각합니다.
DarkWiiPlayer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.