2012-09-30 2 views
3

나는 혀짤배기 숙제가있다. 힘들어한다.Lisp 함수 : 공용어

공용 작업을 수행하는 함수를 작성해야합니다. 이 함수는 두 개의 입력을 원자 또는 목록의 형태로 사용하고 모든 요소를 ​​결합하여 순서를 유지하고 모든 수준의 괄호를 제거합니다.

함수의 출력은 :

(my-union 'a 'b)       ;; (a b) 
(my-union 'a '(b))      ;; (a b) 
(my-union '(a b) '(b c))     ;; (a b c) 
(my-union '(((a))) '(b(c((d e))a)))  ;; (a b c d e) 

나는 LISP 상당히 새로운입니다. 여기 는 내가 지금까지 쓴 무엇이며, 그것은 단지 세 번째 예를 들어 작동합니다

(defun new-union (a b) 
(if (not b) 
     a 
     (if (member (car b) a) 
     (new-union a (cdr b)) 
     (new-union (append a (list (car b))) (cdr b))))) 

어떤 도움을 주시면 감사하겠습니다!

답변

5

, 여기에없는 성능에 대한 걱정, 그리고 CL이 제공하는 도구를 잘 사용하고, 아주 간단한 하향식 (top-down) 접근 방식 중복을 제거하는 함수가 이미 있습니다 : remove-duplicates. :from-end 키워드 인수와 함께 사용하면 "주문 보존"됩니다. 이제 임의의 중첩 목록을 평평하게하는 함수 flatten이 있다고 상상해보십시오. 그런 다음 질문에 대한 해결책은 다음과 같습니다

(defun new-union (list1 list2) 
    (remove-duplicates (flatten (list list1 list2)) :from-end t)) 

이 더 이상 제한이 주어지지 때이 문제를 접근 할 방법이며, 성능에 대해 많이 걱정하는 진짜 이유가 없다. 가능한 한 많은 도구 상자를 사용하고 필요하지 않으면 바퀴를 다시 열지 마십시오.

이렇게 문제가 발생하면 flatten 함수를 작성하는 데 익숙해 져 있습니다.이 연습은 제가 연습 문제로 남겨 둘 것입니다. 너무 어렵지 않습니다. 쉬운 옵션 중 하나는 재귀 함수를 작성하여 다음과 같은 문제에 접근하는 것입니다.

병합 할 목록의 첫 번째 요소 자체가 목록 인 경우 병합 된 첫 번째 요소를 병합 된 나머지 부분에 추가하십시오 . 첫 번째 요소가 목록이 아닌 경우 목록의 병합 된 나머지 부분 앞에 추가하십시오. 입력이 전혀 목록이 아니면 그냥 반환하십시오.

좋은 연습이되어야하며 몇 줄의 코드만으로 수행 할 수 있습니다.

(매우 올바른 경우 도우미 함수를 사용하여 작업을 수행하고 인수가 실제로 목록인지 여부를 확인하십시오. 그렇지 않으면 원자에 대해서도 flatten이 작동합니다. 당신을 위해 문제)

지금, 당신은 flatten을 쓴 가정 :.

> (defun new-union (list1 list2) 
    (remove-duplicates (flatten (list list1 list2)) :from-end t)) 
NEW-UNION 
> (new-union 'a 'b) 
(A B) 
> (new-union 'a '(b)) 
(A B) 
> (new-union '(a b) '(b c)) 
(A B C) 
> (new-union '(((a))) '(b (c ((d e)) a))) 
(A B C D E) 
+1

나는 처음에는 바퀴를 재발 명하고 언어에 대한 느낌을 얻으려고 생각한다. 진정한 이해 없이는 이름과 인터페이스를 열심히 배워 가려고 노력하는 대신 표준 기능을 인식합니다. 표준 기능 사용은 나중에 제공됩니다. 모든 조각이 나에게 검은 상자와 같고 검은 마법으로 작업을하면 정확한 지그 소 퍼즐을 형성하기가 어렵습니다.:) –

+1

윌, reinventing 바퀴 좋은 운동을하고 그 자리가 있습니다. 그러나 Lisp 또는 유사한 언어로 시작하여 중요한 재사용은 재사용 가능하고 구성 가능하며 일반적인 기능이 있다는 것을 배우는 것과 같은 상위 수준의 사고입니다. 내 답변의 요점은 '중복 제거'뿐 아니라 문제에 대한 해결책을 간단한 구성 요소의 조합으로 표현할 수 있다는 것입니다. 물론,'remove-duplicates'를 다시 구현하거나보다 성능있는 해결책을 찾으러 갈 수도 있지만, 재 구현이 항상 사용되어야한다고 생각하지는 않습니다. – danlei

+0

고마워요! 정말 좋은 책을 추천 해 주실 래요? – user1561949

3

이 방법에 접근하는 한 가지 방법은 우려를 분리하는 것입니다. 하나는 평평하다. 다른 것은 중복 제거입니다. 또 다른 결과는 결과입니다.

결과로 빈 목록으로 시작하여 이미 결과에있는 요소를 건너 뛰고 첫 번째 목록의 요소를 추가합니다.

그런 다음 두 번째 목록 요소를 동일한 결과 목록에 추가하여 동일하게 수행하십시오.

(defun my-union (a b &aux (res (list 1)) (p res)) 
    (nadd-elts p a) 
    (nadd-elts p b) 
    (cdr res)) 

nadd-elts

는 파괴적 예를 들어, 사용 ( p가 가리키는)의 마지막 셀을 업데이트 목록의 마지막에 추가합니다 rplacd. 예를 들어 here입니다.

nadd-eltsflattening 절차를 emulate 것, 요소를 추가하고, 중복 res 확인 후 p에 각 잎 요소를 추가합니다.


파괴 갱신하지 않고, 기능적인 스타일에서 작업은 일반적인 접근 방식은 동일하게 유지 : 그것으로 첫 번째 목록을 추가, 빈 결과 목록에서 시작 - 중복없이 - 다음 두 번째.

(defun my-union (a b &aux res) 
    (setq res (add-into res a)) 
    (setq res (add-into res b)) 
    res) 

이제는 add-into 기능을 구현했습니다.

(defun add-into (res a &aux r1 r2) 
    (cond 
    ((atom a) ....) 
    (T (setq r1 (add-into res (car a))) 
     (setq r2 (............ (cdr a))) 
     r2))) 

은 상기 보조 변수없이 set 프리미티브 안에 재 작성 될 수있다. 확인을 여기에 내가 그 무슨 뜻인지 얼마나 ... 알아보십시오 :

당신이 해시 테이블을 사용하는 것이 허용되지 않는 한 (어떤 이유로 내가 전에 요구 사항으로이 발생했습니다)
(defun my-union (a b) (add-into NIL (cons a b))) 

(defun add-into (res a) 
    (cond 
    ((atom a) ....) 
    (T (add-into (add-into res (car a)) 
        (cdr a))))) 
+0

나는 2 주 전에 lisp.Started 꽤 새로운 오전. 숙제는 쉬웠을 뿐이에요. '& aux (res (list 1)) (p res))'는 무엇을 의미 하는가? – user1561949

+0

만약 당신이 그렇게 새로운 것이라면 파괴적인 코드는 당신이가는 길은 아닐 것입니다. 어떤 방향으로 나아갈 지에 대한 조언을 받았습니까? 아마도 이것을 "기능적"스타일로 코딩해야할까요? 그러나 일반적인 충고는 빈 결과부터 시작하여 첫 번째 목록을 중복되지 않고 두 번째로 추가합니다. –

+0

'& aux'는 내부 변수를 함수에 선언합니다. 하나는'res'이고, 초기 값은'(list 1)'입니다. 또 하나는'res'와 같은'p'입니다. –

2

, 당신이 올 수 결과 집합을 만드는 데 도움이되는 순서 지정 함수를 사용하면 검색을 반복해서 반복 할 필요가 없습니다.

또한 중첩 목록이 허용되므로 트리에서 중복 된 항목 만 제거하도록 문제가 확장됩니다. 처리하기 전에 원하는만큼 많은 목록을 추가 할 수 있습니다.

지금, 나는 당신이 그것을 할 수있는 방법의 몇 가지 예를 보여주기 위해 노력할 것이다 : 나는 요소를 주문하는 데 사용했다 함수가 보편적 아니라고

;; Large difference between best and worst case. 
;; Lists containing all the same items will be processed 
;; in square time 
(defun union-naive (list &rest lists) 
    (when lists (setf list (append list lists))) 
    (let (result) 
    (labels ((%union-naive (tree) 
       (if (consp tree) 
        (progn 
        (%union-naive (car tree)) 
        (when (cdr tree) (%union-naive (cdr tree)))) 
        (unless (member tree result) 
        (setq result (cons tree result)))))) 
     (%union-naive list) result))) 

;; Perhaps the best solution, it is practically linear time 
(defun union-hash (list &rest lists) 
    (when lists (setf list (append list lists))) 
    (let ((hash (make-hash-table)) result) 
    (labels ((%union-hash (tree) 
       (if (consp tree) 
        (progn 
        (%union-hash (car tree)) 
        (when (cdr tree) (%union-hash (cdr tree)))) 
        (setf (gethash tree hash) t)))) 
     (%union-hash list)) 
    (maphash 
    #'(lambda (a b) 
     (declare (ignore b)) 
     (push a result)) hash) 
    result)) 

;; This will do the job in more time, then the 
;; solution with the hash-map, but it requires 
;; significantly less memory. Memory is, in fact 
;; a more precious resource some times, but you 
;; have to decide what algo to use based on the 
;; data size 
(defun union-flatten (list &rest lists) 
    (when lists (setf list (append list lists))) 
    (labels ((%flatten (tree) 
      (if (consp tree) 
       (if (cdr tree) 
        (nconc (%flatten (car tree)) 
          (%flatten (cdr tree))) 
        (%flatten (car tree))) 
       (list tree)))) 
    ;; the code below is trying to do something 
    ;; that you could've done using 
    ;; (remove-duplicates (%flatten list)) 
    ;; however sorting and then removing duplicates 
    ;; may prove to be more efficient 
    (reduce 
    #'(lambda (a b) 
     (cond 
      ((atom a) (list a)) 
      ((eql (car a) b) b) 
      (t (cons b a)))) 
    (sort (%flatten list) 
      #'(lambda (a b) 
       (string< (symbol-name a) 
         (symbol-name b))))))) 



(union-naive '(((a))) '(b(c((d e))a))) 
(union-hash '(((a))) '(b(c((d e))a))) 
(union-flatten '(((a))) '(b(c((d e))a))) 

공지 사항,하지만 당신은 아마 할 수있을 것입니다 모든 종류의 데이터에 대한 대체 기능을 제안하십시오. 일반적으로 빠른 해싱 함수를 사용하면 간단하게 해왔습니다. 커먼 리스프에서

:이 첫 번째 숙제이며, 당신이 리스프에 새로운 때문에

+0

"중첩 목록이 허용됩니다 ..."입력 *에서 *를 의미합니까? 혼란 스러울 수 있습니다. –

관련 문제