이 재귀 코드를 이해하는 방법?


12

입력 된 행 수를 기준으로 자갈 수를 찾는 함수 An Introduction to Programming in Emacs Lisp의 도움으로 재귀를 보여주는 수동 으로이 코드 cond를 찾았습니다. 즉, rows = 2 인 경우 자갈은 3이어야합니다. 그곳에.

(defun triangle-using-cond (number)
  (cond ((<= number 0) 0)
        ((= number 1) 1)
        ((> number 1)
         (+ number (triangle-using-cond (1- number))))))

인수 4를 전달한 후 10으로 평가

(triangle-using-cond 4)

매뉴얼은이 예제 코드의 각 단계에서 어떤 일이 발생하는지 명확하게 설명하지 않았으며 여기서 재귀가 어떻게 작동하는지 알 수 없었습니다. 각 인스턴스에서 발생하는 과정을 단계별로 이해하는 데 도움을 줄 수 있습니까 ?


다른 사람이 "재귀"라는 단어를 사용하도록 돕거나 (이 문맥에서 다른 것으로 생각하기 때문에) 내가 쓸 내용을 더 잘 설명 할 것입니다 : (a) 숫자가 작거나 같은 경우 0부터 0까지; (b) 숫자가 1이면 1; (c) 숫자가 triangle-using-cond1보다 크면 숫자가 무엇이든 1보다 작은 인수로 함수 가 반환 한 값에 숫자를 추가하십시오 . 조건은 a, b 및 c의 순서로 진행됩니다. 먼저 일치하는 것은 벅이 멈추는 곳입니다.
lawlist

평소 elisp 인터프리터는 가장 안쪽에서 바깥쪽으로 평가합니다. 따라서 1-4 = 3. 이제 재귀 호출은 (triangle-using-cond 3)이지만 조건부 1에 도달 할 때까지 반복적으로 동일한 재귀 호출로 끝날 것입니다. 다음에 무슨 일이 일어 날까?
박사 학위

오, 나는 기능이 3 단계에서 재사용한다는 것을 알았습니다.
lawlist

어떤 결과가 나올지 궁금합니다 (triangle-using-cond 3).
박사 학위

2
nb이 함수 1-는 특히 잘못된 표기법 인 것처럼 전화를 읽는 경우 잘못된 이름을 갖습니다. 인수에서 1을 뺀 값을 반환합니다. 인수를 뺀 것이 아닙니다.
phils

답변:


14

"printf 디버깅"사용

함수 정의를 수정하여 Emacs가 이해하도록 도울 수 있습니다.

(defun triangle-using-cond (number)
  (message (format "called with %d" number))
  (cond ((<= number 0) 0)
        ((= number 1) 1)
        ((> number 1)
         (+ number (triangle-using-cond (1- number))))))

버퍼에 (message ...)트레일을 인쇄하려면 어딘가에 추가하십시오 *Messages*.

Edebug 사용

함수 정의 내부의 아무 곳에 나 포인트를 놓고 C-u C-M-x"악기"를 누르 십시오. 그런 후 지점을 배치하여 기능, 예를 평가 (triangle-using-cond 3)타격 C-x C-e.

이제 Edebug 모드입니다. 스페이스 바를 눌러 기능을 단계별로 실행하십시오. 각 표현식의 중간 값이 에코 영역에 표시됩니다. Edebug 모드를 종료하려면를 누르십시오 q. 계측을 제거하려면 정의 내부 아무 곳에 나 포인트를두고 정의 C-M-x를 다시 평가하십시오.

표준 Emacs 디버거 사용

M-x debug-on-entry triangle-using-cond그런 다음 triangle-using-cond호출되면 Emacs 디버거 (버퍼 *Backtrace*)에 배치됩니다 .

를 사용하여 평가를 단계적으로 수행하십시오 d(또는 c관심없는 평가는 건너 뛰십시오).

중간 상태 (변수 값 등)를 보려면 e언제든지 사용할 수 있습니다. 평가할 sexp를 입력하라는 메시지가 표시되고 평가 결과가 인쇄됩니다.

디버거를 사용하는 동안 소스 코드의 사본을 다른 프레임에서 볼 수 있도록하여 진행중인 작업을 수행 할 수 있습니다.

소스 코드의 임의의 위치에서 디버거 (더 많거나 적은 중단 점)에 들어가기 위해 명시적인 호출을 삽입 할 수도 있습니다. 당신은 삽입 (debug)또는 (debug nil SOME-SEXP-TO-EVALUATE). 후자의 경우 디버거가 입력 될 때 SOME-SEXP-TO-EVALUATE평가되고 결과가 인쇄됩니다. (이러한 코드를 소스 코드에 삽입 C-M-x하고 평가하는 데 사용할 수 있으며 편집 한 파일을 저장할 필요가 없습니다.)

자세한 내용은 Elisp 매뉴얼 노드 Using Debugger를 참조하십시오.

루프로 재귀

어쨌든 재귀는 루프로 생각하십시오. 두 가지 종료 사례가 정의되어 있습니다 : (<= number 0)(= number 1). 이 경우 함수는 간단한 숫자를 반환합니다.

재귀 적 경우 함수는 해당 숫자와 함수 결과의 합을로 반환합니다 number - 1. 결국 함수는 10보다 작거나 같은 숫자 로 호출 됩니다.

재귀 사례 결과는 다음과 같습니다.

(+ number (+ (1- number) (+ (1- (1- number)) ... 1)

예를 들어 보자 (triangle-using-cond 4). 최종 표현을 축적합시다 :

  • 첫번째 반복에서 number4소위, (> number 1)분기가 이어진다. 식 작성을 시작 (+ 4 ...하고 (1- 4), 즉을 사용 하여 함수를 호출합니다 (triangle-using-cond 3).

  • 이제는 number이고 3결과는 (+ 3 (triangle-using-cond 2))입니다. 총 결과 표현식은 (+ 4 (+ 3 (triangle-using-cond 2)))입니다.

  • number2표현은 지금(+ 4 (+ 3 (+ 2 (triangle-using-cond 1))))

  • number1지금, 우리는 걸릴 (= number 1)지루한 결과, 지점 1. 전체 표현은 (+ 4 (+ 3 (+ 2 1)))입니다. 내부에서 그것을 평가하면 다음 (+ 4 (+ 3 3))과 같은 결과 (+ 4 6)가 나타납니다 10.


3
Edebug가 더 나을 것입니다. =)
Malabarba 2011

을 사용하여 트레일을 인쇄하는 방법 message (...), 타격은 C-x C-e단지 최종 결과 (10)를 보여줍니다. 뭔가 빠졌습니까?
박사 학위

@ 말라 바바, Edebug행동하는 법?
박사 학위

1
@doctorate C-u C-M-x는 함수 내부의 포인트로 edebug합니다. 그런 다음 정상적으로 기능을 실행하십시오.
Malabarba

@ 닥터는 (message ...)인쇄물을 *Message*버퍼에 인쇄합니다 .
rekado

6

SICP의 절차 적용위한 대체 모델은 이와 같은 코드를 이해하는 알고리즘을 설명 할 수 있습니다.

나는 이것을 쉽게하기 위해 몇 가지 코드를 작성했다. lispy-flatten에서 Lisp 다운 패키지이 작업을 수행합니다. 여기에 적용한 결과이다 lispy-flatten로는 (triangle-using-cond 4):

(cond ((<= 4 0)
       0)
      ((= 4 1)
       1)
      ((> 4 1)
       (+ 4 (triangle-using-cond (1- 4)))))

위의 표현을 간단하게 단순화 할 수 있습니다.

(+ 4 (triangle-using-cond 3))

그런 다음 다시 한 번 평평하게하십시오.

(+ 4 (cond ((<= 3 0)
            0)
           ((= 3 1)
            1)
           ((> 3 1)
            (+ 3 (triangle-using-cond (1- 3))))))

최종 결과 :

(+ 4 (+ 3 (+ 2 1)))

3

이것은 Emacs / Elisp에만 국한되지는 않지만 수학 배경이있는 경우 재귀는 수학적 유도와 같습니다 . (또는 당신이하지 않으면 : 당신이 유도를 배울 때, 그것은 재귀와 같습니다!)

정의부터 시작하겠습니다.

(defun triangle-using-cond (number)
  (cond ((<= number 0) 0)
        ((= number 1) 1)
        ((> number 1)
         (+ number (triangle-using-cond (1- number))))))

경우 number이다 4그것이 제 3 조건에 따라 평가 그래서 처음 두 조건이 길게도들 :
(triangle-using-cond 4)로 평가
(+ number (triangle-using-cond (1- number)))즉 같이
(+ 4 (triangle-using-cond 3)).

마찬가지로
(triangle-using-cond 3)으로 평가됩니다
(+ 3 (triangle-using-cond 2)).

마찬가지로 (triangle-using-cond 2)으로 평가됩니다
(+ 2 (triangle-using-cond 1)).

그러나의 (triangle-using-cond 1)경우 두 번째 조건이 유지되며로 평가됩니다 1.

재귀를 배우는 사람을위한 조언 : 피하려고 노력하십시오

재귀 호출이 작동한다는 것을 신뢰하는 대신 재귀 호출 중에 발생하는 일에 대해 생각하려고 시도하는 일반적인 초보자 실수입니다 (때로는 재귀 적 믿음의 도약이라고 함).

(triangle-using-cond 4)정답을 반환 할지 여부 를 스스로 확인하려는 경우 정답 을 반환 한다고 가정 하고 (triangle-using-cond 3)그 경우에 맞는지 확인하십시오. 물론 기본 사례도 확인해야합니다.


2

예제의 계산 단계는 다음과 같습니다.

(4 +               ;; step 1
   (3 +            ;; step 2
      (2 +         ;; step 3
         (1))))    ;; step 4
=> 10

입력으로 1이 이미 재귀를 끝내기 때문에 0 조건은 실제로는 절대로 충족되지 않습니다.


(1)유효한 표현이 아닙니다.
rekado

1
로 잘 평가됩니다 M-x calc. :-) 진지하게, 나는 Lisp 평가가 아니라 계산을 보여 주려고했습니다.
파프리카

오, 나는 그것이 당신의 대답 (4 +대신에 있음을 알지 못했습니다 (+ 4... :)
rekado

0

나는 그것이 쉬운 일이라고 생각합니다. 이맥스 리스프가 필요하지 않습니다. 초등학교 수학 일뿐입니다.

f (0) = 0

f (1) = 1

n> 1 인 경우 f (n) = f (n-1) + n

따라서 f (5) = 5 + f (4) = 5 + 4 + f (3) = 5 + 4 + 3 + 2 + 1 + 0

지금은 분명하다.


그러나이 함수의 경우 f (0)이 호출되지 않습니다.
rekado
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.