2009-12-08 2 views
2

목록으로 주어진 첫 번째 목록에 두 번 이상 나타나는 항목 만있는 새 목록을 반환하려고합니다. 나는 다음과 같은 실행하면숙제 :리스트에 두 번 이상 나타나는 리스프 항목

(defun myf (lista) 
    (if (endp lista) 
     nil 
     (if (member (first lista) (rest lista)) 
      (append (list (first lista)) (myf (rest lista))) 
      (myf (rest lista))))) 

는 : (myf '(A A B A B C))를, 그것은 (A A B)을 반환

나는 다음과 같은 했어요. 항목을 한 번만 반환하도록하려면 어떻게해야합니까 (즉, 두 번 "A"가 없는지)?

+2

연결된 IF 형태가 COND로 더 잘 변형됩니다. – Svante

답변

2

Vincent, Gishu 및 Jerry는 아이템이 추가되기 전에 결과 목록에 이미 있는지 확인해야한다고 암시했지만 Derek는 아이템이 반복되는 것을 보았을 때 원래 목록을 수정할 수 있음을 암시했습니다.

이 기능 adjoin 여기 remove의 문서 읽기 :

http://www.lispworks.com/documentation/HyperSpec/Body/f_adjoin.htm

http://www.lispworks.com/documentation/HyperSpec/Body/f_rm_rm.htm

HyperSpec는 매우 유용한 참조입니다, 그것을 북마크를 추가 할 수 있습니다.

두 함수는 인수를 수정하지 않고 결과를 새 목록으로 반환합니다. 인수를 수정하여 더 효율적일 수있는 다른 함수가 있지만,이 시점에서는 아마도 걱정할 필요가 없을 것입니다.

오케이,이 글을 쓰고 나면 그 사실을 알았 으면 좋겠습니다. 그렇지 않다면 계속 노력하십시오. 이것이 당신이 정말로 배울 수있는 유일한 방법입니다.

이제는 또 다른 접근법에 대해 이야기하고 싶습니다. 재귀 호출을 통해 결과를 전달하는 것입니다.

우리의 기능 repeated 그것에 초기 빈 결과 '() 통과, 실제 작업을 수행하는 도우미 함수 repeatedr을 아무것도하지 않고 있지만 호출합니다 :

(defun repeated (lst) 
    (repeatedr lst '())) 
이제

의 헬퍼 함수를 ​​정의 할 수 있습니다를, 두 개의 매개 변수를 수신 : 중복을 검색 할 목록 및 중복 항목을 누적 할 결과 목록

(defun repeatedr (lst result) 
    (if (null lst) 
    result 
    (if (member (first lst) (rest lst)) 
     (repeatedr (rest lst) (adjoin (first lst) result)) 
     (repeatedr (rest lst) result)))) 

조건 (member (first lst) (rest lst)) 우리는 result 내지 제 항목과 두 번째 매개 변수로 재귀 호출로 전달 될 것임을 인접 결과 인접한다 성립

; 그렇지 않으면 재귀 호출에 그대로 result을 전달합니다.

목록이 마침내 비어있는 경우 (if (null lst)result을 반환합니다. 결과가 (B A)이며, 어쩌면 당신이 (A B)가 될 것으로 생각하는

> (repeated '(a a b a b c)) 
(B A) 

알 수 있습니다. 재귀 호출의 실행과 각 호출시 매개 변수의 값을 펜 및 용지로 따라하면 좋은 연습이 될 것이므로 해당 동작을 이해하려면 adjoin을 사용해야합니다. 이의 결과 재귀 완료 역

(defun repeatedr (lst result) 
    (if (null lst) 
    (reverse result) 
    (if (member (first lst) (rest lst)) 
     (repeatedr (rest lst) (adjoin (first lst) result)) 
     (repeatedr (rest lst) result)))) 

: 당신이 결과를 얻고 싶은 경우에 당신이 함수를 다음과 같이 수정할 수 있습니다 반전.

이제 앞으로 진행하기 전에 목록에서 중복 된 요소를 삭제할 것을 제안합니까? 우리는 다음과 같이 우리의 기능을 쓸 수 :

(defun repeatedr (lst result) 
    (if (null lst) 
    result 
    (if (member (first lst) (rest lst)) 
     (repeatedr (remove (first lst) lst) (cons (first lst) result)) 
     (repeatedr (rest lst) result)))) 

시도가 REPL에서 remove 노는 : 우리가 더 이상 인접있는 결과를 보내고

> (remove 'a '(a b c d a e f b a d)) 
(B C D E F B D) 

공지 사항, 대신에 우리가 만들 새로운 "cons 셀"(cons (first lst) result). consadjoin은 목록에 이미 존재하지 않는 경우에만 인접 항목이 값을 추가한다는 점을 제외하고는 동일한 작업을 수행합니다.

희망 사항은 다음과 관련이 있습니다. 순서가 중요하지 않은 경우

+0

훌륭한 설명을 해 주셔서 감사합니다. – daniels

3

문제는 목록의 첫 번째 요소가 목록의 끝에 있는지 확인한 다음 결과 목록에 추가하는 것입니다. 이 문제는 A의 두 번째 인스턴스에 올 때 발생합니다.이 옵션을 선택하면 A에 세 번째 인스턴스가 있기 때문에 테일에있는 것입니다. 입력 목록에 4 A가 있으면 3을 반환합니다.

그래서 A가 이미 결과 목록의 구성원인지 확인해야합니다. 즉, 최초의 lista가 list의 멤버가 아닌 경우, 최초의 lista를리스트에 추가합니다.

2

요소가 추가되기 전에 출력 목록에 요소가 있는지 확인하지 않는 것 같습니다.

+0

예, 그게 내가 알아낼 수 없어 어떻게 varible (예 : 사용하지 않고 확인하는 것입니다 : 설정,하자, 등 .. – daniels

+0

""의 나머지 인스턴스에서 모든 인스턴스를 제거하는 방법에 대해 한 번 ' A를 중복으로 선택 했습니까? – Gishu

3

편지가 목록에 두 번 이상 나왔다면 다시 확인하지 않아도되므로 나머지 목록에는 편지가 필요하지 않습니다. 그래서 나머지 목록을 수정할 수 있습니다 ...

참고 :이 답변은 의도적으로 다소 모호합니다. 숙제와 모든 것 때문입니다. :)

1

지금은 목록의 각 요소에 대해 목록의 나머지 부분에 포함되지 않은 요소를 결과에 추가하고 있습니다.

분명히 원하는 결과를 얻으려면 결과에 아직 포함되지 않은 목록의 각 요소를 결과에 추가하십시오.

1

당신은 할 수 있습니다 : 여기

(defun my-fun-no-order (lst) 
    (loop for (item . rest) on lst 
    when (= (count item rest) 0) 
    collect item)) 

우리는 이상 반복되는 현재 항목으로 침 목록을 반복하고 나머지 항목은 아직 검사 할 수 있습니다. when 절은 우리가 항목을 수집하는 상황을 결정합니다.