이 프로그램은 모든 정수에 대해 종료됩니까?


14

GATE 준비를위한 부품 테스트에서 질문이있었습니다.

f(n):
     if n is even: f(n) = n/2
     else f(n) = f(f(n-1))

"모든 정수에 대해 종료됩니다"라고 대답했습니다. 일부 음의 정수에 대해서도 스택 오버플로 오류 로 종료되기 때문입니다 .

그러나 내 친구는 이것이 코드가 아니라 의사 코드이기 때문에 일부 음수의 경우 무한 재귀가 될 것이라는 의견에 동의하지 않았습니다.

어떤 대답이 옳고 그 이유는 무엇입니까?


8
n = -1에 대해서는 종료되지 않습니다. 이러한 경우에 대부분 이론적 인 한계가 고려됩니다.
Deep Joshi

9
스택 오버플로가 종료로 간주되면 모든 프로그램이 종료
되고이

10
@ xuq01 while (true);은 종료되지 않거나 합리적인 것으로 스택 오버플로를 유발하지 않습니다.
TripeHound

3
아마 "사용하지 말았어야 @leftaroundabout 분별 아무것도 는 완전히 다른 수준이기 때문에" " 재치 ... 안보와 꼬리 재귀를 구현하는"입니다 좋은 (또는 분별 ),하지만 그렇게하지 만 약간 "입니다 분별하지 ". 구현하는 것이 무엇이든 while(true);사용하는 방법으로 어떤 가장 확실히 될 것 스택을 분별하지 . 요점은 의도적으로 어색해 while(true);지지 않는 한 스택 오버플로를 종료하거나 트리거하지 않습니다.
TripeHound

14
@ xuq01 나는 "우주의 파괴"가 멈추는 문제에 대한 해결책이라고 생각하지 않습니다.
TripeHound

답변:


49

정답은이 함수가 모든 정수에 대해 종료되지 않는다는 것입니다 (특히 -1에서 종료되지 않습니다). 친구는 이것이 의사 코드이며 의사 코드는 스택 오버플로에서 종료되지 않는다고 말한 것이 맞습니다. 의사 코드는 공식적으로 정의되어 있지 않지만 주석에서 말하는 것을 수행한다는 아이디어입니다. 코드에 "스택 오버플로 오류로 종료"라고 표시되지 않으면 스택 오버플로 오류가없는 것입니다.

이것이 실제 프로그래밍 언어 인 경우에도 스택 사용이 언어 정의의 일부가 아닌 한 정답은 여전히 ​​"종료되지 않습니다"입니다. 대부분의 언어는 프로그램이 얼마나 많은 스택을 사용할지 정확하게 알기 어렵 기 때문에 스택을 오버플로 할 수있는 프로그램의 동작을 지정하지 않습니다.

실제 인터프리터 또는 컴파일러에서 코드를 실행하면 많은 언어에서 스택 오버플로가 발생하는 것이 언어의 형식적 의미와 구현 간의 불일치입니다. 언어의 구현은 유한 한 메모리를 가진 콘크리트 컴퓨터에서만 수행 될 수있는 것으로 일반적으로 이해된다. 프로그램이 스택 오버플로로 죽으면 더 큰 컴퓨터를 구입하고 필요한 경우 모든 메모리를 지원하기 위해 시스템을 다시 컴파일 한 다음 다시 시도해야합니다. 프로그램이 종료되지 않으면이 작업을 계속 수행해야 할 수 있습니다.

꼬리 호출 최적화 및 메모리 와 같은 일부 최적화 는 일정한 범위의 스택 공간에서 무한한 함수 호출 체인을 허용 할 수 있기 때문에 프로그램이 스택을 오버플로하거나 오버플로하지 않을 것이라는 사실조차 잘 정의되어 있지 않습니다 . 일부 언어 사양에서는 구현시 가능한 경우 테일 콜 최적화를 수행해야합니다 (기능 프로그래밍 언어에서 일반적 임). 이 기능을 위해, f(-1)다음과 같이 확장 f(f(-2)); 에 대한 외부 호출 f은 테일 호출이므로 스택에 아무것도 밀어 넣지 않으므로 스택에만 f(-2)들어가고을 반환 -1하므로 스택은 처음과 동일한 상태로 돌아갑니다. 따라서 꼬리 호출 최적화 f(-1)를 사용하면 일정한 메모리에서 영원히 반복됩니다.


3
코드가 프로그래밍 언어로 변환되어 스택 오버플로가 발생하지 않는 예는 Haskell입니다. 그것은 무한정 반복됩니다 :let f :: Int -> Int; f n = if even n then n `div` 2 else f (f (n - 1)) in f (-1)
JoL

5

우리가 이것을 C 언어로 본다면, 구현이 원본이 정의되지 않은 행동을 유발하지 않는 모든 경우에 동일한 결과를 생성하는 코드로 코드를 자유롭게 대체 할 수 있습니다. 그래서 대체 할 수 있습니다

f(n):
   if n is even: f(n) = n/2
   else f(n) = f(f(n-1))

f(n):
   if n is even: f(n) = n/2
   else f(n) = f((n-1) / 2)

이제 구현은 꼬리 재귀를 적용 할 수 있습니다 :

f(n):
   while n is not even do n = (n-1) / 2
   f(n) = n/2

그리고 이것은 n = -1 인 경우에만 영원히 반복됩니다.


C에서 호출 f(-1)은 정의되지 않은 동작이라고 생각합니다 (구현은 모든 스레드가 종료 되거나이 함수가 수행하지 않는 짧은 활동 목록에서 다른 것을 수행한다고 가정 할 수 있음). 그래서 컴파일러는 실제로 원하는 것을 수행 할 수 있습니다 케이스!
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.