Prolog 재귀 순서가 중요합니까?

lefunction

문제에 대한 두 가지 해결책의 차이점에 대해 질문이 있습니다. 문제는 다음과 같이 목록을 잘린 목록으로 변환하도록 요청합니다.

?- reduce([a,a,a,b,b,c,c,b,b,d,d],Z).
Z = [a,b,c,b,d].

이 첫 번째 솔루션에는 목록을 반대로하는 추가 단계가 필요합니다.

reduce([X|Xs],Z) :-
   reduce(X,Xs,Y,[X]),
   reverse(Y,Z).

reduce(X,[L|Ls],Y,List) :-
    (  X=L
    -> reduce(X,Ls,Y,List)
    ;  reduce(L,Ls,Y,[L|List])
    ).
reduce(_,[],Y,Y).

두 번째 솔루션에는 다음이 필요하지 않습니다 reverse/2.

reduced([X|Xs],Result) :- 
    reduced(Xs,List),
    List=[A|_],
    (  A=X
    -> Result=List
    ;  Result=[X|List]
    ),
    !.
reduced(Result,Result).

일련의 명령문 전후에 재귀를 수행 할 때 최적화 고려 사항은 무엇입니까? 조건의 순서가 중요합니까? 내 성향은 모든 재귀를 선행하는 것이 갈 길이라고 생각하는 것입니다. 특히 여기서 목록을 거꾸로 작성해야하기 때문입니다.

user1812457

무엇이든 최적화 할 때 먼저 측정해야합니다! (우리 대부분은 이것을 잊는 경향이 있습니다 ....)

Prolog를 최적화 할 때 다음 사항을 확인하십시오.

  • 꼬리 재귀는 더 나은 경향이 있습니다 (그러므로 "일련의 명령문 전후"질문이 있습니다).
  • 필요하지 않은 선택 지점을 만들지 마십시오 (Prolog 구현에 따라 다름).
  • 최적의 알고리즘을 사용하십시오 (예 : 필요하지 않은 경우 목록을 두 번 탐색하지 마십시오).

다소 표준 Prolog 구현에 대해 "최적화 된"솔루션은 약간 다를 수 있습니다. 이름을 지정하겠습니다 list_uniq(명령 줄 uniq도구 와 유사 ).

list_uniq ([], []). % 기본 케이스 
list_uniq ([H | T], U) : 
    -list_uniq_1 (T, H, U). % 도우미 술어
 
list_uniq_1 ([], X, [X]). 
list_uniq_1 ([H | T], X, U) 
    :-( 
    H == X- > list_uniq_1 (T, X, U) 
    ; [X | U1] = U, 
        list_uniq_1 (T, H, U1) 
    ).

그것은 다른 reduce0/2것이 사용하기 때문에 @CapelliC에 의해 지체 의 고유 비 결정론하지 않도록 [X|Xs]하고 [X,X|Xs]첫 번째 인수를.

이제 "최적화"되었다고 주장합니다.

  • 목록을 정확히 한 번 순회합니다 (반전 할 필요 없음).
  • 그것은 꼬리 재귀
  • 선택 포인트를 생성 및 폐기하지 않습니다.

@CapelliC와 동일한 12 가지 추론을 얻게되며, 더 긴 목록을 사용하면 차이점을 볼 수 있습니다.

?-길이 (As, 100000), 맵리스트 (= (a), As), 
   길이 (Bs, 100000), 맵리스트 (= (b), Bs), 
   길이 (Cs, 100000), 맵리스트 (= (c), Cs), 
   append ([As, Bs, Cs, As, Cs, Bs], L), 
   time (list_uniq (L, U)). 
% 600,006 추론, 0.057 초 동안 0.057 CPU (100 % CPU, 10499893 입술) 
As = [a, a, a, a, a, a, a, a, a | ...], 
Bs = [b, b, b, b, b, b, b, b, b | ...], 
Cs = [c, c, c, c, c, c, c, c, c | ...], 
L = [a, a, a, a, a, a, a, a, a | ...], 
U = [a, b, c, a, c, b].

와 같은 쿼리 reduce0, reduce1, reduce2CapelliC의 대답 @에서 :

% 감소 0 (L, U) 
% 600,001 추론, 0.125 CPU 0.125 초 (100 % CPU, 4813955 입술) 
% 감소 1 (L, U) 
% 1,200,012 추론, 0.393 CPU 0.394 초 (100 % CPU, 3050034 입술) 
% 감소 2 (L, U) 
% 2,400,004 추론, 0.859 CPU 0.861 초 (100 % CPU, 2792792 입술)

따라서 컷 ( !)을 사용하여 선택 포인트를 만들고 버리는 것도 대가가 있습니다.

그러나은 list_uniq/2첫 번째 인수가 근거가 아닌 쿼리에 대해 잘못 될 수 있습니다.

?-list_uniq ([a, B], [a, b]). 
B = b. % OK
 
?-list_uniq ([a, A], [a]). 그릇된. % 잘못되었습니다!

reduce0/2또한 reduce1/2잘못 될 수 있습니다.

?-reduce0 ([a, B], [a, b]).
그릇된. 

?-reduce1 ([a, B], [a, b]). 그릇된.

에 관해서는 이것에 대해 reduce2/2잘 모르겠습니다.

?-reduce2 ([a, A], [a, a]). 
A = a.

대신의 정의에 사용 if_/3에서 이 답변 :

list_uniq_d ([], []). % 기본 케이스 
list_uniq_d ([H | T], U) : 
    -list_uniq_d_1 (T, H, U). % Helper 술어 

list_uniq_d_1 ([], X, [X]). 
list_uniq_d_1 ([H | T], X, U) : 
        -if_ 
    (H = X, list_uniq_d_1 (T, H, U), 
        ([X | U1] = U, 
            list_uniq_d_1 (T, H, U1) 
        ) 
    ).

그것으로:

?-list_uniq_d ([a, a, a, b], U). 
U = [a, b]. 

?-list_uniq_d ([a, a, a, b, b], U). 
U = [a, b]. 

?-list_uniq_d ([a, A], U). 
A = a, 
U = [a]; 
U = [a, A], 
dif (A, a). 

?-list_uniq_d ([a, A], [a]). 
A = a; 
그릇된. % 
댕글 링 선택 점
 ?-list_uniq_d ([a, A], [a, a]). 
그릇된. 

?-list_uniq_d ([a, B], [a, b]). 
B = b. 

?-list_uniq_d ([a, A], [a, a]). 
그릇된.

시간이 더 걸리지 만 술어가 올바른 것 같습니다 . 다른 타이밍에 사용 된 것과 동일한 쿼리를 사용합니다.

% 3,000,007 inferences, 1.140 CPU in 1.141 seconds (100% CPU, 2631644 Lips)

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

Prolog에서 재귀 적 목표의 결과에 원자 결합

분류에서Dev

순서와 재귀 결합

분류에서Dev

순서가있는 요소를 재귀 적으로 추가

분류에서Dev

경로 찾기 재귀가 Prolog에서 메모리 부족

분류에서Dev

Prolog의 재귀 문제-재귀를 벗어날 때 True 대신 False가 됨

분류에서Dev

재귀에 이중 포인터가 필요합니다.

분류에서Dev

Prolog에서 예상치 못한 답변을받는 이유는 무엇입니까? 나열, 곱하기, 재귀

분류에서Dev

Prolog의 목록에서 요소를 제거하기 위해 재귀 함수에서 '잘라 내기'제어

분류에서Dev

회귀 모델에서 베타 추정치를 해석 할 때 정렬 순서가 중요합니까?

분류에서Dev

C ++-재귀 구조-가능합니까?

분류에서Dev

단순 재귀와 다중 재귀 구별

분류에서Dev

프롤로그 쿼리 : 재귀 쿼리에서 더하기는 어떻게 중요합니까?

분류에서Dev

주어진 길이를 가진 제곱합의 재귀 찾기 순서

분류에서Dev

명령 순서가 중요합니까?

분류에서Dev

변수를 통한 재귀는 어떻게 증가 순서를 반환합니까?

분류에서Dev

파이썬에서 단순화 된 지뢰 찾기 재귀 : 왜이 코드가 성공하지 못합니까?

분류에서Dev

C에서 안전한 재귀가 가능합니까?

분류에서Dev

이 재귀 함수의 작업 순서는 무엇입니까?

분류에서Dev

JavaScript 재귀 XML 요소 순회

분류에서Dev

이 코드에서 재귀가 어떻게 작동합니까?

분류에서Dev

파이썬 재귀 다중 처리가 선형으로 작동합니까?

분류에서Dev

Clojure 재귀의 평가 순서

분류에서Dev

재귀 함수가 비 재귀 함수보다 느립니다.

분류에서Dev

Prolog에서 증분기를 사용하여 각 재귀 호출을 추적

분류에서Dev

Prolog-반복 알고리즘을 재귀로 변환 (함수 파서)

분류에서Dev

재귀 구성 요소 및 재귀 중첩에서 Vue 드래그 앤 드롭

분류에서Dev

typescript : 재귀 keyof가 있습니까?

분류에서Dev

내 재귀 메서드가 true를 반환하면 왜 재귀 호출을 입력합니까?

분류에서Dev

순서를 준수하면서 가능한 모든 조합의 JavaScript 배열을 재귀 적으로 구성

Related 관련 기사

  1. 1

    Prolog에서 재귀 적 목표의 결과에 원자 결합

  2. 2

    순서와 재귀 결합

  3. 3

    순서가있는 요소를 재귀 적으로 추가

  4. 4

    경로 찾기 재귀가 Prolog에서 메모리 부족

  5. 5

    Prolog의 재귀 문제-재귀를 벗어날 때 True 대신 False가 됨

  6. 6

    재귀에 이중 포인터가 필요합니다.

  7. 7

    Prolog에서 예상치 못한 답변을받는 이유는 무엇입니까? 나열, 곱하기, 재귀

  8. 8

    Prolog의 목록에서 요소를 제거하기 위해 재귀 함수에서 '잘라 내기'제어

  9. 9

    회귀 모델에서 베타 추정치를 해석 할 때 정렬 순서가 중요합니까?

  10. 10

    C ++-재귀 구조-가능합니까?

  11. 11

    단순 재귀와 다중 재귀 구별

  12. 12

    프롤로그 쿼리 : 재귀 쿼리에서 더하기는 어떻게 중요합니까?

  13. 13

    주어진 길이를 가진 제곱합의 재귀 찾기 순서

  14. 14

    명령 순서가 중요합니까?

  15. 15

    변수를 통한 재귀는 어떻게 증가 순서를 반환합니까?

  16. 16

    파이썬에서 단순화 된 지뢰 찾기 재귀 : 왜이 코드가 성공하지 못합니까?

  17. 17

    C에서 안전한 재귀가 가능합니까?

  18. 18

    이 재귀 함수의 작업 순서는 무엇입니까?

  19. 19

    JavaScript 재귀 XML 요소 순회

  20. 20

    이 코드에서 재귀가 어떻게 작동합니까?

  21. 21

    파이썬 재귀 다중 처리가 선형으로 작동합니까?

  22. 22

    Clojure 재귀의 평가 순서

  23. 23

    재귀 함수가 비 재귀 함수보다 느립니다.

  24. 24

    Prolog에서 증분기를 사용하여 각 재귀 호출을 추적

  25. 25

    Prolog-반복 알고리즘을 재귀로 변환 (함수 파서)

  26. 26

    재귀 구성 요소 및 재귀 중첩에서 Vue 드래그 앤 드롭

  27. 27

    typescript : 재귀 keyof가 있습니까?

  28. 28

    내 재귀 메서드가 true를 반환하면 왜 재귀 호출을 입력합니까?

  29. 29

    순서를 준수하면서 가능한 모든 조합의 JavaScript 배열을 재귀 적으로 구성

뜨겁다태그

보관