상호재귀

Mutual recursion

수학과 컴퓨터 과학에서 상호 재귀는 함수나 데이터 유형과 같은 두 개의 수학적 또는 계산적 객체가 서로에 대한 용어로 정의되는 재귀의 한 형태다.[1]상호 재귀는 기능 프로그래밍데이터 유형이 자연적으로 상호 재귀적인 재귀적인 재귀 강하 파서와 같은 일부 문제 영역에서 매우 흔하다.

데이터티페스

상호 재귀에 의해 정의될 수 있는 데이터 유형의 가장 중요한 기본 예는 나무로, 숲(나무 목록)의 관점에서 상호 재귀적으로 정의할 수 있다.상징적으로:

f: [t[1], ..., t[k] t: v f

f는 나무의 목록으로 구성되며, 나무 t는 v와 f(그들의 자식)의 쌍으로 구성된다.이 정의는 나무를 단순한 용어로 표현하기 때문에(예: 나무의 성질에 대한 이론을 증명할 때) 우아하고 추상적으로 작업하기 쉽다(한 종류의 리스트와 두 종류의 리스트).게다가, 그것은 나무의 많은 알고리즘을 일치시킨다. 나무의 알고리즘은 가치로 하는 것과 아이들과 하는 것으로 구성된다.

이 상호 재귀적 정의는 숲의 정의를 요약하여 단일 재귀적 정의로 변환할 수 있다.

t: v [t[1], ..., t[k]

트리 t는 값 v와 트리 목록(자녀)의 쌍으로 구성된다.이 정의는 더 작지만 다소 더 메스껍다: 나무는 한 종류의 한 쌍과 다른 종류의 목록으로 구성된다. 이것은 결과를 증명하기 위해 분리되어야 한다.

표준 ML에서 트리와 숲 데이터 유형을 다음과 같이 상호 반복적으로 정의할 수 있으며, 이에 따라 빈 트리가 허용된다.[2]

데이터타입 'a 나무 = 비어 있음   노드  'a * 'a 숲의 , 그리고      'a 숲의 =    단점  'a 나무 * 'a 숲의 

컴퓨터 기능

재귀 데이터 유형의 알고리즘이 재귀 함수에 의해 자연스럽게 부여될 수 있듯이, 상호재귀 데이터 구조에 대한 알고리즘도 상호재귀 함수에 의해 자연스럽게 부여될 수 있다.일반적인 예로는 나무의 알고리즘과 재귀 강하 파서가 있다.직접 재귀와 마찬가지로 재귀 깊이가 크거나 무한할 경우 멀티태스킹에 상호 재귀 사용과 같이 테일콜 최적화가 필요하다.일반적으로 테일 콜 최적화(호출된 함수가 테일-리커버리 호출에서와 같이 원래 기능과 같지 않은 경우)는 테일-리커버리 호출 최적화의 특별한 경우보다 구현이 더 어려울 수 있으며, 따라서 상호 테일-리커버전의 효율적인 구현이 테일-리커버를 최적화하는 언어에서만 부재할 수 있다는 점에 유의한다.성가신호사용 전 선언이 필요한 파스칼과 같은 언어에서는 상호 재귀 함수를 정의할 때 전진 참조를 피할 수 없기 때문에 상호 재귀 함수를 전진 선언을 필요로 한다.

직접 재귀 함수와 마찬가지로, 포장 함수도 유용할 수 있으며, 상호 재귀 함수가 지원될 경우 범위 내에서 내포 함수로 정의된다.이것은 특히 기능들 간에 매개 변수를 통과하지 않고도 여러 기능들에 걸쳐 상태를 공유하는 데 유용하다.

기본 예시

상호 재귀의 표준 예로서, 인정된 인위적인 경우, 서로 부르는 두 개의 별개의 기능을 정의하여 비 음수가 짝수인지 홀수인지를 매번 1씩 감소시켜 결정한다.[3]C:

바가지 긁다 is_even(서명이 없는 인트로 n) {     만일 (n == 0)         돌아오다 진실의;     다른         돌아오다 is_message(n - 1); }  바가지 긁다 is_message(서명이 없는 인트로 n) {     만일 (n == 0)         돌아오다 거짓의;     다른         돌아오다 is_even(n - 1); } 

이 함수들은 질문이 4 짝수라는 관찰에 근거한 것이다.3 홀수와 같으며, 2 짝수 2와 같으며, 0으로 낮아진다.이 예는 상호 단일 반복이며 반복으로 쉽게 대체될 수 있다.이 예에서 상호 재귀 호출은 테일 호출이며, 테일 호출 최적화는 일정한 스택 공간에서 실행하기 위해 필요할 것이다.C에서는, 호출 대신 점프를 사용하도록 다시 쓰지 않는 한, O(n) 스택 공간이 필요할 것이다.[4]이것은 단일 재귀 함수로 축소될 수 있다.is_even. 그런 경우에는is_odd인라인으로 할 수 있는 것은, 전화할 것이다.is_even그렇지만is_even자기 자신을 부를 뿐이지

보다 일반적인 예시 등급으로서 나무 위의 알고리즘은 가치에 대한 행동과 어린이에 대한 행동으로 분해될 수 있으며, 나무 위의 행동을 명시하는 것과 어린이의 숲을 위한 숲 기능을 부르는 것, 그리고 숲에서의 행동을 명시하는 것, 트롤을 부르는 것 등 두 가지 상호 재귀적 기능으로 나눌 수 있다.ee는 숲속의 나무의 기능을 한다.Python의 경우:

반항하다 f_트리(나무) -> 없음:     f_value(나무.가치를 매기다)     f_숲(나무.아이들.)  반항하다 f_숲(숲의) -> 없음:     을 위해 나무  숲의:         f_트리(나무) 

이 경우 트리 함수는 단일 재귀에 의해 포리스트 함수를 호출하지만 포리스트 함수는 다중 재귀에 의해 트리 함수를 호출한다.

위의 표준 ML 데이터 유형을 사용하여 트리의 크기(노드 수)를 다음과 같은 상호 재귀 함수를 통해 계산할 수 있다.[5]

재미있다 size_tree 비어 있음 = 0     size_tree (노드 (_, f)) = 1 + size_forest f , 그리고 size_forest  = 0     size_forest (단점 (t, f')) = size_tree t + size_forest f' 

트리의 잎을 세는 체계에서 더 자세한 예:[6]

(정의를 내리다(카운트다운을 하다 나무)   (만일(잎사귀? 나무)       1       (삼림지대의 수림. (아이들. 나무))))  (정의를 내리다(삼림지대의 수림. 숲의)   (만일(무효가 되십니까?숲의)       0       (+(카운트다운을 하다 (자동차숲의))          (삼림지대의 수림. (cdr숲의))))) 

이러한 예들은 흔히 행해지는 트리함수에 숲 기능을 줄임으로써 하나의 재귀함수로 쉽게 축소된다. 즉, 나무에서 작용하는 재귀함수는 이를 두 개의 개별함수로 나누지 않고 한 기능 내에서 노드의 가치를 순차적으로 처리하고 어린이에게 재귀함수다.

고급 예제

더 복잡한 예는 재귀적 강하 파서들에 의해 주어지는데, 이것은 문법의 각 생산 규칙에 대해 하나의 기능을 갖는 것으로 자연스럽게 구현될 수 있고, 이것은 상호적으로 반복된다; 이것은 일반적으로 생산 규칙이 여러 부분을 결합하기 때문에 일반적으로 다중 재귀가 될 것이다.이것은 또한 상호 재귀 없이 이루어질 수 있는데, 예를 들어 각 생산 규칙에 대해 여전히 별개의 기능을 가지고 있지만, 단일 제어 기능으로 호출하게 하거나, 모든 문법을 단일 함수에 넣음으로써 가능하다.

상호 재귀는 또한 각 상태에 대해 하나의 기능, 그리고 변화하는 상태의 단일 재귀로 유한 상태 기계를 구현할 수 있다. 이것은 상태 변화 수가 크거나 무한할 경우 테일 콜 최적화가 필요하다.이것은 협동 멀티태스킹의 단순한 형태로 이용될 수 있다.멀티태스킹과 유사한 접근법은 다른 루틴을 호출하여 종료하는 것이 아니라 다른 루틴을 호출하여 종료하는 것이 아니라 한 코루틴이 다른 루틴을 생성하지만 종료되지 않고 다시 실행될 때 실행을 재개하는 것이다.이것은 매개변수로 전달되거나 공유 변수에 저장될 필요 없이 개별 코루틴이 상태를 유지할 수 있도록 한다.

또한 자연적으로 2상(최소 및 최대)을 갖는 알고리즘도 있는데, 각 상이 상호 재귀와 함께 별도의 함수에 있게 함으로써 구현될 수 있지만, 직접적인 재귀와 함께 단일 함수로 결합할 수도 있다.

수학적 함수

수학에서 호프스태터 암수 순서는 상호 재귀적인 방식으로 정의된 정수 순서의 한 쌍의 예다.

프랙탈은 재귀 함수에 의해 (주어진 분해능까지) 계산될 수 있다.이것은 때때로 상호 재귀적 기능을 통해 더 우아하게 이루어질 수 있다; 시에르피에스키 곡선이 좋은 예다.

유병률

상호 재귀는 기능 프로그래밍에서 매우 흔하며 LISP, Scheme, ML 및 유사한 프로그래밍 언어로 작성된 프로그램에 자주 사용된다.예를 들어, Abelson과 Sussman은 메타 순환 평가자를 사용하여 평가 적용 주기를 갖는 LISP를 구현하는 방법을 설명한다.[7]프롤로그와 같은 언어에서 상호 재귀는 거의 피할 수 없다.

일부 프로그래밍 스타일은 답을 만들어내지 않고 코드가 영원히 실행될 수 있는 조건과 답변을 반환할 조건을 구분하는 것이 혼동될 수 있다고 주장하면서 상호 재귀성을 저해한다.Peter Norvig는 다음과 같이 말하면서, 완전히 사용을 억제하는 디자인 패턴을 지적한다.[8]

개체 상태를 모두 변경하는 상호 복구 기능이 두 개 있는 경우 거의 모든 기능을 하나의 기능으로 이동하십시오.그렇지 않으면 당신은 아마도 코드를 복제하게 될 것이다.

용어.

상호 재귀는 간접 재귀라고도 하며, 단일 함수가 직접 호출하는 직접 재귀와는 대조적으로, 간접 재귀라고도 한다.이것은 단순히 강조의 차이일 뿐, 다른 개념이 아니다: "간접적 재귀"는 개별적인 기능을 강조하는 반면, "상호 재귀"는 기능 집합을 강조하며, 개별적인 기능을 배제하지 않는다.예를 들어 f가 스스로 전화를 걸면 그것은 직접적인 재귀가 된다.대신 fg부르고 그 다음에 g를 호출하면 f의 관점에서 다시 g를 호출하면 f는 간접적으로 재귀하는 반면 g의 관점에서만 보면 g는 간접적으로 재귀하는 반면, f와 g는 둘 다의 관점에서 상호 재귀하는 것이다.마찬가지로 서로 호출하는 3개 이상의 함수의 집합을 상호 재귀함수의 집합이라고 할 수 있다.

직접 재귀로 변환

수학적으로 상호 재귀함수의 집합은 원시 재귀함수로, 이는 가치의 과정 재귀에 의해 증명될 수 있으며,[9] 개별 재귀함수의 값을 순서대로 나열하는 단일 함수 F를 구축한다.= ( 0), 2( ), ( ), f 2( 1),… ,}(}() , 그리고 원시적 재귀로서 상호재귀 를 다시 쓴다.

두 절차 사이의 상호 재귀는 한 절차의 코드를 다른 절차로 요약하여 직접 재귀로 변환할 수 있다.[10]하나의 절차가 다른 하나의 사이트를 호출하는 사이트만 있는 경우 이는 간단하지만, 여러 개의 절차가 있는 경우 코드 복제가 수반될 수 있다.콜 스택의 관점에서, 두 개의 상호 재귀적 절차는 스택 ABAB...를 생성하며, B를 A로 줄이면 직접 재귀(AB)(AB)가 생성된다.

또는, 절차의 선택과 그 주장을 나타내는 변종 기록(또는 대수 데이터 유형)을 인수로서 취하는 단일 절차로 병합할 수 있다. 그런 다음 병합 절차는 해당 코드를 실행하기 위해 자신의 주장을 파견하고, 적절하다면 자기 자신을 호출하기 위해 직접 재귀성을 사용한다.이는 제한된 기능 상실 적용으로 볼 수 있다.[11]이 번역은 상호 재귀적 절차를 외부 코드로 호출할 수 있을 때 유용할 수 있으므로 한 절차를 다른 절차로 요약하는 명백한 사례가 없다.그런 다음 그러한 코드를 수정하여 기술된 변종 레코드에 인수를 결합하여 절차 호출을 수행하도록 해야 한다. 또는 이 작업에 래퍼 절차를 사용할 수도 있다.

참고 항목

참조

  1. ^ 마누엘 루비오 산체스, 제이미 우르키자 푸엔테스, 크리스토발 파레자 플로레스(2002), '상호 재귀에 대한 온화한 소개', 제13차 컴퓨터 과학 교육 혁신 및 기술에 관한 연례 회의의 진행, 2008년 6월 30일~7월 2일 스페인 마드리드.
  2. ^ 하퍼 2000, "날짜 유형"
  3. ^ Hutton 2007, 6.5 상호 재귀, 페이지 53–55.
  4. ^ "상호 꼬리-회귀 기능"과 "꼬리-회귀 기능", 훙웨이 Xi, 2010년 ATS의 프로그래밍 기능에 관한 자습서
  5. ^ 하퍼 2000, "다타입스"
  6. ^ Harvey & Wright 1999, V. 추상화: 18.나무: 상호 재귀, 페이지 310~313.
  7. ^ Abelson, Harold; Sussman, Gerald Jay; Sussman, Julie (1996). Structure and Interpretation of Computer Programs (PDF). London, England: The MIT Press. p. 492. ISBN 978-0262510875.
  8. ^ 모든 스도쿠 퍼즐 해결
  9. ^ "상호 반복", PlanetMath
  10. ^ 오웬 케이저, C. R. 라마크리쉬난, 쇼낙 파와기가 뉴욕 주립대학의 간접 재귀 전환에 관하여(1993)
  11. ^ Reynolds, John (August 1972). "Definitional Interpreters for Higher-Order Programming Languages" (PDF). Proceedings of the ACM Annual Conference. Boston, Massachusetts. pp. 717–740.

외부 링크