while 루프는 본질적으로 재귀입니까?


37

while 루프가 본질적으로 재귀인지 궁금합니다.

while 루프는 마지막에 자신을 호출하는 함수로 볼 수 있기 때문이라고 생각합니다. 재귀가 아닌 경우 차이점은 무엇입니까?



13
재귀를 반복으로 변환하거나 그 반대로 변환 할 수 있습니다. 그것은 그들이 같은 것을 의미하는 것이 아니라 단지 동일한 기능을 가지고 있다는 것입니다. 재귀가 더 자연스러운 시간이 있고 반복이 더 자연스러운 시간이 있습니다.
Polygnome

18
@MooingDuck 모든 재귀를 반복으로 작성할 수 있고 그 반대의 경우도 가능하다는 사실을 증명할 수 있습니다. 예, 매우 다르게 보일 수 있지만 그럼에도 불구하고 할 수 있습니다.
Polygnome

6
여기서 본질적으로 동일한 의미 는 무엇입니까 ? 프로그래밍에서 재귀를 사용한다는 것은 반복 (루프)과 다른 특정 것을 의미합니다. CS에서, 당신이 이론적 인 수학 측면에 가까워지면, 이것들은 약간 다른 것을 의미하기 시작합니다.
hyde

3
@MooingDuck 재귀에서 반복으로의 변환은 실제로 매우 사소합니다. 함수 호출 매개 변수 스택과 함수 호출에 대한 결과 스택 만 유지하면됩니다. 호출 스택에 매개 변수를 추가하여 재귀 호출을 대체합니다. 알고리즘의 구조를 약간 깨뜨리는 스택 처리가 있는지 확인하지만 일단 이해하면 코드가 똑같은 일을하는 것이 매우 쉽습니다. 기본적으로 재귀 정의에 암시적인 호출 스택을 명시 적으로 작성하고 있습니다.
Bakuriu

답변:


116

루프는 재귀가 아닙니다 . 실제로, 그것들은 반대 메커니즘 의 주요 예인 반복 입니다.

재귀의 요점은 처리의 한 요소 다른 인스턴스를 호출 한다는 것입니다. 루프 제어 기계는 단지 점프 가 시작 지점으로 돌아.

코드에서 뛰어 내리고 다른 코드 블록을 호출하는 것은 다른 작업입니다. 예를 들어, 루프 시작으로 점프 할 때 루프 제어 변수는 여전히 점프 이전과 동일한 값을 갖습니다. 그러나 현재 사용중인 루틴의 다른 인스턴스를 호출하면 새 인스턴스에는 모든 관련 변수의 새로운 관련없는 사본이 있습니다. 효과적으로, 하나의 변수는 첫 번째 처리 레벨에서 하나의 값을 가질 수 있고 낮은 레벨에서 다른 값을 가질 수 있습니다.

이 기능은 많은 재귀 알고리즘이 작동하는 데 중요하므로 이러한 모든 값을 추적하는 호출 된 프레임 스택을 관리하지 않고 반복을 통해 재귀를 에뮬레이션 할 수 없습니다.


10
@Giorgio 사실 일 수도 있지만, 답변이되지 않은 주장에 대한 의견입니다. 이 답변에 "임의로"는 존재하지 않으며 그 의미를 크게 바꿀 것입니다.
hvd

12
@hvd 원칙적으로 꼬리 재귀는 다른 것과 마찬가지로 완전 재귀입니다. 인텔리전트 컴파일러는 생성 된 코드가 루프와 매우 유사하도록 실제 "새 스택 프레임 생성"부분을 최적화 할 수 있지만 우리가 이야기하는 개념은 소스 코드 수준에 적용됩니다. 나는 알고리즘이 소스 코드 로서 중요한 형태라고 생각 하므로 여전히 재귀라고 부릅니다.
Kilian Foth

15
@Giorgio "이것은 재귀가하는 일이다. 새로운 인수로 호출하라"– 호출을 제외하고. 그리고 논쟁.
hobbs

12
@Giorgio 가장 많은 단어를 여기에서 사용하고 있습니다. 단어는 의사 소통의 기초입니다. 이것은 CS 스택 교환이 아닌 프로그래머입니다. "argument", "call", "function"등과 같은 단어를 제안한 방식으로 사용하면 실제 코드에 대해 논의 할 수 없습니다.
하이드

6
@Giorgio 나는 추상적 인 개념을보고있다. 반복되는 개념과 반복되는 개념이 있습니다. 그것들은 다른 개념입니다. 홉은 인수가없고 호출이 없다는 것을 100 % 정확합니다. 그것들은 기본적으로 추상적으로 다릅니다. 그리고 그들은 다른 문제를 해결하기 때문에 좋습니다. 반면에, 당신은 유일한 도구가 재귀 일 때 루프를 구현하는 방법을보고 있습니다. 아이러니하게도 홉스에게 구현에 대한 생각을 멈추고 방법론이 실제로 재평가가 필요한 방법 일 때 개념을 살펴 보라고 말하고 있습니다.
corsiKa

37

X가 본질적으로 Y라고 말하는 것은 X를 표현한다는 (공식) 시스템을 염두에 둔 경우에만 의미가 있습니다. while람다 미적분학 의 의미를 정의하면 재귀 *를 언급 할 수 있습니다. 레지스터 머신으로 정의하면 아마 그렇지 않을 것입니다.

두 경우 모두 while 루프가 포함되어 있기 때문에 재귀 함수를 호출하면 사람들이 아마 당신을 이해하지 못할 것입니다.

* 아마도 간접적이지만, 예를 들어으로 정의한 경우 fold.


4
공정하게 말하면, 함수는 어떤 정의에서도 재귀 적이 지 않습니다. 재귀 적 요소 인 루프 만 포함합니다.
Luaan

@Luaan : 확실히 그렇게 되긴하지만, while구조 재귀를 가진 언어에서는 일반적으로 함수의 속성이기 때문에이 문맥에서 "재귀"라고 설명 할 다른 것을 생각할 수 없습니다.
Anton Golov

36

이것은 귀하의 관점에 따라 다릅니다.

계산 성 이론 을 살펴보면 반복과 재귀는 똑같이 표현 적 입니다. 이것이 의미하는 것은 무언가를 계산하는 함수를 작성할 수 있으며, 재귀 적으로 또는 반복적으로 수행하는지 여부에 관계없이 두 가지 방법을 모두 선택할 수 있다는 것입니다. 재귀 적으로 계산할 수있는 것은 없으며 반복적으로 계산할 수 없으며 그 반대도 마찬가지입니다 (프로그램의 내부 작업은 다를 수 있음).

많은 프로그래밍 언어는 재귀와 반복을 동일하게 취급하지 않으며 정당한 이유가 있습니다. 일반적 으로 재귀는 언어 / 컴파일러가 호출 스택을 처리한다는 것을 의미하며 반복은 스택 처리를 직접 수행해야 할 수도 있음을 의미합니다.

그러나 루프 (for, while)와 같은 것은 실제로 재귀를위한 구문 설탕 일뿐이며 그런 식으로 무대 뒤에서 구현 되는 언어 , 특히 기능적 언어 가 있습니다. 함수형 언어에서는 종종 루핑이라는 개념이 없기 때문에 바람직하다.이를 추가하면 실질적인 이유 때문에 미적분학을 더 복잡하게 만들 수있다.

아니, 그들은 본질적으로 동일 하지 않습니다 . 그것들은 똑같이 표현 적입니다 . 즉, 반복적으로 계산할 수 없으며 반복적으로 계산할 수 없으며 반대의 경우도 마찬가지입니다. 그러나 일반적인 경우 (교회-튜링 논문에 따르면)에 관한 것입니다.

여기서는 재귀 프로그램 에 대해 이야기 하고 있습니다. 데이터 구조 (예 : 트리)와 같은 다른 형태의 재귀가 있습니다.


당신이에서 보면 보기의 구현 지점 , 다음 재귀와 반복이 거의 동일하지 않습니다. 재귀는 모든 호출에 대해 새 스택 프레임을 만듭니다. 재귀의 모든 단계는 자체 포함되어 있으며 수신자로부터 계산에 대한 인수를 얻습니다 (자체).

반면에 루프는 호출 프레임을 생성하지 않습니다. 이들을 위해 각 단계에서 컨텍스트가 유지되지 않습니다. 루프의 경우, 프로그램은 루프 조건이 실패 할 때까지 루프 시작으로 되돌아갑니다.

현실 세계에서 근본적인 차이를 만들 수 있기 때문에이 점을 알아야합니다. 재귀를 위해서는 모든 컨텍스트에서 전체 컨텍스트를 저장해야합니다. 반복을 위해 메모리에있는 변수와 저장 위치에 대한 정확한 제어가 가능합니다.

그런 식으로 보면 대부분의 언어에서 반복과 재귀가 근본적으로 다르고 다른 속성을 가지고 있음을 빨리 알 수 있습니다. 상황에 따라 일부 속성이 다른 속성보다 더 바람직합니다.

재귀는 프로그램을보다 간단하고 쉽게 테스트하고 증명할 수있게합니다 . 재귀를 반복으로 변환하면 일반적으로 코드가 더 복잡해져 실패 가능성이 높아집니다. 반면에 반복으로 변환하고 호출 스택 프레임의 양을 줄이면 필요한 메모리를 많이 절약 할 수 있습니다.


로컬 변수와 재귀가 있지만 배열이없는 언어는 로컬 변수가 있고 배열이없는 반복 언어로는 수행 할 수없는 작업을 수행 할 수 있습니다. 예를 들어, 입력에 길이가 알 수없는 영숫자 문자열 뒤에 공백이 있고 원래 문자열의 문자가 역순으로 포함되는지보고하십시오.
supercat

3
언어가 완전히 완성되는 한 가능합니다. 예를 들어, 배열을 (의심하게) 연결된 목록으로 쉽게 바꿀 수 있습니다. 반복 또는 재귀에 대해 말하면, 두 개의 완전한 언어를 비교하는 경우에만 의미가 있습니다.
Polygnome

나는 즉, 단순한 정적 또는 자동 변수 이외의 아무것도없는 것을 의미 하지 튜링 완성되고. 순전히 반복적 인 언어는 단순한 결정 론적 유한 오토 마톤을 통해 달성 할 수있는 작업으로 제한되는 반면, 재귀 언어는 최소한 푸시 다운 결정 론적 유한 오토 마톤을 요구하는 작업을 수행 할 수있는 기능을 추가합니다.
supercat

1
언어가 완전하지 않으면 처음부터 무의미합니다. DFA는 임의 반복이나 재귀 btw를 수행 할 수 없습니다.
Polygnome

2
실제로 Turing-complete를 구현 한 것은 없으며 Turing-complete가 아닌 언어는 많은 목적에 유용 할 수 있습니다. 유한 범위를 갖는 유한 한 변수 수로 실행될 수있는 모든 프로그램은 가능한 모든 값 조합이 이산 상태 인 DFA에 의해 수용 될 수 있습니다.
supercat

12

차이점은 암시 적 스택과 의미입니다.

"마지막에 호출"하는 while 루프에는 완료시 다시 크롤링 할 스택이 없습니다. 마지막 반복은 종료되는 상태를 설정합니다.

그러나 이전의 작업 상태를 기억하는이 암시 적 스택이 없으면 재귀를 수행 할 수 없습니다.

스택에 명시 적으로 액세스하면 반복과 관련된 재귀 문제를 해결할 수 있다는 것은 사실입니다. 그러나 그렇게하는 것은 같지 않습니다.

의미 상 차이점은 재귀 코드를 보면 아이디어가 반복 코드와 완전히 다른 방식으로 전달된다는 사실과 관련이 있습니다. 반복 코드는 한 번에 한 단계 씩 작업을 수행합니다. 그것은 이전에 온 상태를 받아들이고 다음 상태를 만들기 위해서만 작동합니다.

재귀 코드는 문제를 프랙탈로 나눕니다. 이 작은 부분은 큰 부분처럼 보이므로이 부분과 그 부분을 같은 방식으로 수행 할 수 있습니다. 문제를 생각하는 다른 방법입니다. 매우 강력하고 익숙해집니다. 몇 줄로 많은 것을 말할 수 있습니다. 스택에 액세스 할 수 있다고해도 while 루프에서 벗어날 수는 없습니다.


5
"암시 적 스택"이 잘못된 것이라고 생각합니다. 재귀는 구현 세부 사항이 아니라 언어 의미의 일부입니다. ( 많은 언어가 최적화를 지원하기 때문에 , 대부분의 재귀 지원 언어는 호출 스택을 사용하지만 첫째, 그러한 언어 중 일부는 그렇지 않은 언어 에서조차도 모든 재귀 호출이 반드시 호출 스택에 추가되는 것은 아닙니다 로 꼬리 호출 제거 .) 일반 / 간단한 구현을 이해하는 것은 추상화에 대한 핸들을 얻기에 유용 할 수 있지만, 당신은 전체 이야기의 생각으로 자신을 속여서는 안된다.
ruakh

2
@ruakh 나는 꼬리 호출 제거를 사용하여 일정한 공간에서 실행되는 함수가 실제로 루프라고 주장합니다. 여기서 스택은 구현 세부 사항이 아니며, 여러 수준의 재귀에 대해 서로 다른 상태를 누적 할 수있는 추상화입니다.
Cimbali

@ruakh : 재귀를 컴파일러가 반복 루프로 변환 할 수 없으면 단일 재귀 호출 내의 모든 상태가 암시 적 스택에 저장됩니다. tail call elimination 구현 세부 사항으로, 함수를 tail-recursive로 재구성하려는 경우 알아야 할 세부 사항입니다. 또한 "이런 언어는 거의 없습니다" -재귀 호출을 위해 스택이 필요없는 언어의 예를 제공 할 수 있습니까?
Groo


@ruakh : CPS 자체는 동일한 암시 적 스택을 생성하므로 테일 콜 제거에 의존하여 이해해야합니다 (구성 방식으로 인해 사소한 것입니다). 테크 콜 최적화 (TCO)없이 CPS를 사용하면 재귀 중에 생성 된 연속성이 증가 할뿐만 아니라 콜 스택도 발생할 수 있습니다 .
Groo

7

그것은 모두 당신이 본질적 으로 그 용어를 사용하는 것에 달려 있습니다 . 프로그래밍 언어 수준에서는 구문과 의미가 다르며 성능과 메모리 사용량이 상당히 다릅니다. 그러나 당신이 이론을 충분히 깊이 파고 들면 그것들은 서로의 관점에서 정의 될 수 있고, 따라서 어떤 이론적 의미에서 "동일"합니다.

진짜 질문은 : 반복 (루프)과 재귀를 구별하는 것이 언제 합리적이며, 그것을 같은 것으로 생각하는 것이 언제 유용합니까? 답은 실제로 수학적 증명을 작성하는 것과는 반대로 프로그래밍 할 때 반복과 재귀를 구별하는 것이 중요하다는 것입니다.

재귀는 새로운 스택 프레임, 즉 각 호출에 대한 새로운 로컬 변수 세트를 만듭니다. 오버 헤드가 발생하고 스택의 공간을 차지하므로 충분한 재귀가 스택을 오버플로하여 프로그램이 중단 될 수 있습니다. 반면에 반복은 기존 변수 만 수정하므로 일반적으로 더 빠르며 일정한 양의 메모리 만 차지합니다. 따라서 이것은 개발자에게 매우 중요한 차이점입니다!

꼬리 호출 재귀가있는 언어 (일반적으로 기능적인 언어)에서 컴파일러는 일정한 양의 메모리 만 차지하는 방식으로 재귀 호출을 최적화 할 수 있습니다. 이러한 언어에서 중요한 차이점은 반복 대 재귀가 아니라 비 꼬리 전화 재귀 버전의 꼬리 전화 재귀와 반복입니다.

결론 : 차이점을 말할 수 있어야합니다. 그렇지 않으면 프로그램이 중단됩니다.


3

while루프는 재귀의 한 형태입니다. 예를 들어이 질문에 대한 대답을 참조하십시오 . 이들은 계산 이론에서 μ 연산자에 해당합니다 (예 : 여기 참조 ).

for숫자, 유한 컬렉션, 배열 등을 반복하는 모든 루프 변형은 기본 재귀에 해당합니다 (예 : herehere 참조) . 점을 유의 forC, C의 루프 ++, 자바, 등등, 사실에 대한 문법 설탕 있습니다 while루프, 따라서 그것을 대응 원시 재귀하지 않습니다. 파스칼 for루프는 원시 재귀의 예입니다.

중요한 차이점은 기본 재귀는 항상 종료되지만 일반 재귀 ( while루프)는 종료되지 않을 수 있다는 것입니다.

편집하다

의견 및 기타 답변에 대한 일부 설명. "물건 자체 또는 유형으로 정의 된 경우 재귀가 발생합니다." ( wikipedia 참조 ). 그래서,

while 루프는 본질적으로 재귀입니까?

while자체 관점 에서 루프를 정의 할 수 있기 때문에

while p do c := if p then (c; while p do c))

다음 하는 while루프는 재귀의 한 형태이다. 재귀 함수는 또 다른 형태의 재귀입니다 (재귀 정의의 다른 예). 리스트와 트리는 다른 형태의 재귀입니다.

많은 답변과 의견에 의해 암시 적으로 가정되는 또 다른 질문은

while 루프와 재귀 함수는 동일합니까?

이 질문에 대한 답은 아니오입니다 . while루프는 꼬리 재귀 함수에 해당합니다. 루프에서 액세스하는 변수는 암시 적 재귀 함수의 인수에 해당하지만 다른 사람들이 지적했듯이 꼬리 비 재귀 함수 while추가 스택을 사용하지 않으면 루프 로 모델링 할 수 없습니다 .

따라서 " while루프는 재귀의 형태"라는 사실이 "일부 재귀 함수는 while루프 로 표현할 수 없다는"사실과 모순되지 않습니다 .


2
@morbidCode : 원시 재귀 및 μ 재귀는 예를 들어 계산 이론에서 연구 된 특정 제한 (또는 그 부족)을 가진 재귀의 형태입니다. 결과적으로 FOR루프 만있는 언어는 모든 기본 재귀 함수를 WHILE정확하게 계산할 수 있고 , 루프 만있는 언어는 모든 µ- 재귀 함수를 정확하게 계산할 수 있습니다. 그리고 µ- 재귀 함수는 정확히 그 함수입니다. 튜링 머신이 계산할 수 있음). 간단히 말해 : 원시 재귀와 µ 재귀는 수학 / 계산 이론에서 기술적 인 용어입니다.
Jörg W Mittag

2
"재귀"는 자신을 호출하는 함수를 암시하여 현재 실행 상태가 스택으로 푸시되는 것으로 생각했습니다. 따라서 대부분의 머신에는 재귀 할 수있는 레벨 수에 대한 실질적인 제한이 있습니다. 루프는 내부적으로 "JMP"와 같은 것을 사용하고 스택을 사용하지 않기 때문에 그러한 제한은 없습니다. 내 이해는 틀릴 수 있습니다.
Jay

13
이 답변은 OP가 사용하는 것과 "재귀"라는 단어에 대해 완전히 다른 정의를 사용하고 있기 때문에 오해의 소지가 있습니다.
Mooing Duck

2
@DavidGrinberg : 인용 : "C, C ++, Java for 루프는 기본 재귀의 예가 아닙니다. 기본 재귀는 최대 반복 횟수 / 재귀 깊이가 루프를 시작하기 전에 고정됨을 의미합니다." 조르지오 (Giorgio)는 계산 이론 프리미티브 에 대해 이야기하고있다 . 프로그래밍 언어와 관련이 없습니다.
Mooing Duck

3
Mooing Duck에 동의해야합니다. 계산 이론이 이론적 CS에서는 흥미로울 수 있지만 OP가 프로그래밍 언어 개념에 대해 이야기하고 있다는 데 모두가 동의한다고 생각합니다.
Voo

2

꼬리 호출 (또는 꼬리 재귀 호출이) 정확하게 (모든 밀어없이 "인수 고토 '로 구현 추가 온 전화 프레임 호출 스택 ) 일부 기능 언어 (OCaml의는 특히) 루프의 일반적인 방법입니다.

따라서 while 루프 (언어가있는 언어)는 본문 (또는 헤드 테스트)에 대한 꼬리 호출로 끝나는 것으로 볼 수 있습니다.

마찬가지로 일반 (비 꼬리 호출) 재귀 호출은 루프 (일부 스택 사용)로 시뮬레이션 할 수 있습니다.

연속연속 전달 스타일에 대해서도 읽어보십시오 .

따라서 "재귀"와 "반복"은 상당히 동일합니다.


1

재귀와 제한되지 않은 while 루프는 계산 표현 측면에서 동일합니다. 즉, 재귀 적으로 작성된 프로그램은 대신 루프를 사용하여 동등한 프로그램으로 재 작성할 수 있으며 그 반대도 마찬가지입니다. 두 방법 모두 turing-complete 이며, 계산 가능한 함수를 계산하는 데 사용할 수 있습니다.

프로그래밍 측면에서 근본적인 차이점은 재귀를 통해 호출 스택에 저장된 데이터를 사용할 수 있다는 것입니다. 이를 설명하기 위해 루프 또는 재귀를 사용하여 단일 연결 목록의 요소를 인쇄한다고 가정합니다. 예제 코드에 C를 사용하겠습니다.

 typedef struct List List;
 struct List
 {
     List* next;
     int element;
 };

 void print_list_loop(List* l)
 {
     List* it = l;
     while(it != NULL)
     {
          printf("Element: %d\n", it->element);
          it = it->next;
     }
 }

 void print_list_rec(List* l)
 {
      if(l == NULL) return;
      printf("Element: %d\n", l->element);
      print_list_rec(l->next);
 }

간단 하죠? 이제 약간의 수정을하겠습니다 :리스트를 역순으로 인쇄하십시오.

재귀 변형의 경우 원래 함수를 거의 수정하지 않습니다.

void print_list_reverse_rec(List* l)
{
    if (l == NULL) return;
    print_list_reverse_rec(l->next);
    printf("Element: %d\n", l->element);
}

루프 함수의 경우 문제가 있습니다. 우리의 목록은 단독으로 연결되어 있으므로 순회 만 가능합니다. 그러나 반대로 인쇄하기 때문에 마지막 요소 인쇄를 시작해야합니다. 마지막 요소에 도달하면 더 이상 마지막 요소로 돌아갈 수 없습니다.

따라서 우리는 많은 재전송을 수행하거나 방문한 요소를 추적하여 효율적으로 인쇄 할 수있는 보조 데이터 구조를 구축해야합니다.

왜 재귀와 관련하여이 문제가 발생하지 않습니까? 재귀에는 이미 보조 데이터 구조가 있으므로 함수 호출 스택입니다.

재귀를 사용하면 재귀 호출의 이전 호출로 돌아갈 수 있기 때문에 모든 지역 변수와 해당 호출의 상태는 그대로 유지되므로 반복적 인 경우 모델링하는 데 지루한 유연성이 있습니다.


1
물론 두 번째 재귀 함수는 꼬리 재귀가 아닙니다. TCO를 사용하여 스택을 재사용 할 수 없으므로 공간을 최적화하는 것이 훨씬 어렵습니다. 이중 연결리스트를 구현하면 요소 당 포인터 / 레퍼런스 공간을 희생시키면서 두 알고리즘을 모두 사소하게 만들 수 있습니다.
Baldrickk

@Baldrickk 꼬리 재귀에 대한 재미있는 점은 루프 버전과 비슷하게 호출 버전에 상태를 저장하는 기능을 다시 제거하므로 루프 버전과 훨씬 유사한 버전으로 끝나는 것입니다. 이중으로 연결된 목록이이를 해결하지만이 문제가 발생할 때 데이터 구조를 다시 디자인하는 것은 종종 옵션이 아닙니다. 여기의 예는 다소 인위적으로 제한되어 있지만 재귀 대수 유형의 맥락에서 기능적 언어로 자주 나타나는 패턴을 보여줍니다.
ComicSansMS

내 요점은이 문제에
Baldrickk

0

루프는 특정 작업 (주로 반복)을 달성하기위한 특별한 형태의 재귀입니다. 여러 언어에서 동일한 성능 [1]으로 재귀 스타일로 루프를 구현할 수 있습니다. SICP [2]에서 for 루프가 "syntastic sugar"로 설명되어 있음을 알 수 있습니다. 대부분의 명령형 프로그래밍 언어에서 for 및 while 블록은 부모 함수와 동일한 범위를 사용합니다. 그럼에도 불구하고, 대부분의 기능적 프로그래밍 언어에는 for 루프가 존재하지 않고 for 루프도 존재하지 않습니다.

명령형 언어에 for / while 루프가있는 이유는 상태를 변경하여 상태를 처리하기 때문입니다. 그러나 실제로 다른 관점에서 보면 while 블록을 함수 자체로 생각하고 매개 변수를 취해 처리하고 새로운 상태를 반환하면 다른 매개 변수로 동일한 함수를 호출 할 수 있습니다. 루프를 재귀로 생각할 수 있습니다.

세계는 또한 가변 또는 불변으로 정의 될 수 있습니다. 세계를 규칙 집합으로 정의하고 모든 규칙과 현재 상태를 매개 변수로 사용하는 궁극적 인 함수를 호출하고 동일한 기능을 가진 이러한 매개 변수에 따라 새 상태를 반환하면 방법), 우리는 그것이 재귀와 루프라고 말할 수 있습니다.

다음 예제에서 life is 함수는 "rules"및 "state"라는 두 개의 매개 변수를 사용하며 다음 번 틱에 새 상태가 구성됩니다.

life rules state = life rules new_state
    where new_state = construct_state_in_time rules state

[1] : 테일 호출 최적화는 함수형 프로그래밍 언어에서 기존 함수 스택을 새 호출을 만드는 대신 재귀 호출에 사용하는 일반적인 최적화입니다.

[2] : 컴퓨터 프로그램의 구조와 해석, MIT. https://mitpress.mit.edu/books/structure-and-interpretation-computer-programs


4
@Giorgio 내 공감대는 아니지만 추측 : 대부분의 프로그래머는 재귀가 재귀 함수 호출이 있음을 암시한다고 생각합니다. 루프에서는 재귀 함수 호출이 없습니다. 따라서 재귀 함수 호출이없는 루프는이 재귀의 특수한 형태의 재귀라고 말하면 명백히 잘못 될 것입니다.
hyde

1
글쎄, 아마도 더 추상적 인 관점에서 보았을 때, 다른 것으로 보이는 것은 실제로 개념적으로 동일합니다. 사람들이 무언가를 배울 수있는 기회로 삼기보다는 자신의 기대치에 맞지 않기 때문에 답을 공감한다고 생각하는 것은 꽤 실망스럽고 슬프다. "이봐 요, 이것들은 표면에서 다르게 보이지만 실제로는 더 추상적 인 수준에서 동일합니다."
조르지오

3
@Georgio :이 사이트의 목적은 질문에 대한 답변을 얻는 것입니다. 도움이되고 정답 인 답변은 공감할만한 가치가 있습니다. 혼란스럽고 도움이되지 않는 답변은 공감할만한 가치가 있습니다. 어떤 다른 정의가 사용되는지 명확하게하지 않고 공통 용어에 대한 다른 정의를 미묘하게 사용하는 대답 은 혼란스럽고 도움이되지 않습니다. 답변을 이미 알고있는 경우에만 의미가있는 답변은 도움이되지 않으며, 작가에게 용어에 대한 탁월한 이해력을 보여줄뿐입니다.
JacquesB

2
@JacquesB : "답변을 이미 알고있는 경우에만 이해가되는 답변은 도움이되지 않습니다 ..." 답변에 명확하지 않은 용어가 도입 된 경우, 하향 투표 전에 자세한 내용을 요구하는 의견을 작성할 수 있습니다.
조르지오

4
루프는 특별한 형태의 재귀 가 아닙니다 . 계산 이론, 예를 들어 이론적 WHILE 언어 및 µ- 미적분을보십시오. 그렇습니다. 일부 언어는 루프를 구문 설탕으로 사용 하여 실제로 장면 뒤에서 재귀를 사용하지만 재귀와 반복이 동일 하지 않기 때문에 똑같이 표현 하기 때문에 그렇게 할 수 있습니다 .
Polygnome

-1

while 루프는 재귀와 다릅니다.

함수가 호출되면 다음이 발생합니다.

  1. 스택에 스택 프레임이 추가됩니다.

  2. 코드 포인터가 함수의 시작 부분으로 이동합니다.

while 루프가 끝나면 다음이 발생합니다.

  1. 조건이 맞는지 묻습니다.

  2. 그렇다면 코드가 특정 지점으로 이동합니다.

일반적으로 while 루프는 다음 의사 코드와 유사합니다.

 if (x)

 {

      Jump_to(y);

 }

무엇보다 중요한 것은 재귀와 루프는 서로 다른 어셈블리 코드 표현과 기계 코드 표현을 가지고 있습니다. 이것은 그들이 같지 않다는 것을 의미합니다. 결과는 같을 수 있지만 다른 기계 코드는 100 % 같은 것이 아님을 증명합니다.


2
프로 시저 호출과 while 루프의 구현에 대해 이야기하고 있으며, 다르게 구현되었으므로 서로 다르다는 결론을 내립니다. 그러나 개념적으로는 매우 유사합니다.
Giorgio

1
컴파일러에 따라 최적화 된 인라인 재귀 호출은 일반 루프와 동일한 어셈블리를 생성 할 수 있습니다.
hyde

@hyde ... 이것은 하나가 다른 하나를 통해 표현 될 수 있다는 잘 알려진 사실에 대한 예일뿐입니다. 그것들이 동일하다는 의미는 아닙니다. 질량과 에너지와 비슷합니다. 물론 동일한 출력을 생성하는 모든 방법이 "동일"하다고 주장 할 수 있습니다. 세계가 유한하다면, 모든 프로그램은 결국 constexpr 일 것입니다.
피터-모니카 복원 복원

@Giorgio Nah, 이것은 구현이 아니라 논리적 설명입니다. 우리는 두 가지가 동등하다는 것을 알고 있습니다 . 그러나 등가는 정체성 이 아닙니다 . 질문과 답은 정확히 우리가 결과를 얻는 방법 하므로 알고리즘 설명 (스택 및 변수 등으로 표현할 수 있음)을 포함해야합니다.
피터-복원 모니카

1
@ PeterA.Schneider 예, 그러나이 답변에는 "모두의 가장 중요합니다 ... 다른 어셈블리 코드"라고 표시되어 있습니다.
hyde

-1

그냥 반복 재귀 일반적으로 해당하는 것으로 충분하지만, 스택과 함께 반복은 일반적으로 동일합니다. 모든 재귀 함수는 스택과 함께 반복 루프로 다시 프로그래밍 할 수 있으며 그 반대도 마찬가지입니다. 그러나 이것이 실용적이라는 것을 의미하지는 않으며, 특정 상황에서 하나 또는 다른 형태가 다른 버전에 비해 확실한 이점을 가질 수 있습니다.

왜 이것이 논쟁의 여지가 있는지 잘 모르겠습니다. 스택을 사용한 재귀 및 반복은 동일한 계산 프로세스입니다. 말하자면 같은 현상이다.

내가 생각할 수있는 유일한 것은 이것들을 "프로그래밍 툴"로 볼 때, 당신이 그것들을 같은 것으로 생각해서는 안된다는 데 동의합니다. 그것들은 "수학적으로"또는 "계산적으로"동등하다 ( 일반적으로 반복이 아니라 스택 으로 반복). 그러나 이것이 어느 쪽이든 할 것이라는 생각에 문제에 접근해야한다는 것을 의미하지는 않는다. 구현 / 문제 해결 관점에서 일부 문제는 어떤 방식 으로든 더 잘 작동 할 수 있으며 프로그래머로서의 업무는 어느 것이 더 적합한 지 올바르게 결정하는 것입니다.

명확히하기 위해 질문에 대한 대답 은 while 루프가 본질적으로 재귀입니까? 한정된 no 또는 적어도 "스택이 없으면"입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.