속성 목록에 함수를 매핑 하시겠습니까?


17

Q : 속성 목록에서 함수를 매핑하는 관용적 방법은 무엇입니까?

다양한 매핑 함수 ( mapcar및 패밀리)는 목록과 같은 시퀀스에 함수를 매핑합니다. 속성 목록을 다룰 때 , 즉 목록에 포함 된 각 속성 (첫 번째 속성에서 시작하는 다른 모든 요소)을 매핑하려고 할 때 이러한 기능을 어떻게 사용 합니까? 매핑 기능은 개별 요소가 아닌 요소 쌍으로 목록에 액세스해야 할 것 같습니다.

장난감의 예로, 어떻게 속성 목록을 가져 와서 모든 속성 값을 수집합니까? 연결 목록 인 경우에는 매우 간단합니다.

(mapcar #'cadr '((:prop1 a) (:prop2 b) (:prop3 c))) ;=> (a b c)

나는 이것이 루프로 이루어질 수 있다고 확신하지만 조금 힘들어 보이고 그것을 수행하는 더 관용적 인 방법이 있는지 궁금합니다.


속성 값만 표시할지 (어떻게 들리는 지, mapcaralist 예제가 수행하는 것) 만 매핑할지 또는 속성 기호와 속성 값 쌍 을 매핑 할지를 명시하십시오 . 후자는 더 일반적이며 (보다 일반적으로 유용합니다) 추측합니다.
Drew

@Drew : 일반적인 경우에 더 관심이 있습니다. 그 예는 내가 생각할 수있는 가장 간단한 것이었다. 속성 / 값 을 매핑하는 방법을 알고 싶습니다 . 대답이 "루프"라면 그럴 것입니다. 그러나 더 우아한 해결책이 있는지 궁금합니다.
Dan

귀하의 예에있는 것은 속성 목록이 아닙니다. 속성 목록에는 짝수 개의 요소가 있고 홀수 요소는 속성 이름이며 요소도 속성 값입니다. 아마도 당신은 나무 (목록의 목록)라고 불렀을 것입니다.
wvxvw

답변:


8

다양한 loop반복 답변을 얻을 수 있습니다. AFAIK, 이것을 수행하는 관용적 방법은 없습니다. 심지어 속성 값만 누적하고 속성과 연결하지 않는 것이 일반적이라고 생각하지 않습니다.

간단한 방법은 다음과 같습니다.

(defun prop-values (plist)
  "..."
  (let ((pl    (cdr plist))
        (vals  ()))
    (while pl
      (push (car pl) vals)
      (setq pl  (cddr pl)))
    (nreverse vals)))

plist에 이진 함수를 매핑하려는보다 일반적인 경우 :

(defun map-plist (fn plist)
  "..."
  (let ((pl    plist)
        (vals  ()))
    (while pl
      (push (funcall fn (car pl) (cadr pl)) vals)
      (setq pl (cddr pl)))
    (nreverse vals)))

(setq foo '(a 1 b 2 c 3 d 4 e 5))

(map-plist #'cons foo) ; => ((a . 1) (b . 2) (c . 3) (d . 4) (e . 5))

13

상황에 따라 다를 수 있습니다. 일반적으로 여러 이름으로 여러 값을 묶어야하는 경우 해시 테이블을 사용하지만 속성 목록을 사용해야하는 경우을 사용 cl-loop합니다. 다음은 몇 가지 예입니다.

(cl-loop for (key value) on '(:prop1 a :prop2 b :prop3 c) by 'cddr
         collect value)
;;; (a b c)

그리고 데이터 구조가 있다면 예제에 표시됩니다.

(cl-loop for (key value) in '((:prop1 a) (:prop2 b) (:prop3 c))
         collect value)
;;; (a b c)

5

나에게 답은 plist를 alist로 바꾸는 것인데, 이것은 동등한 데이터 구조 인 절반의 깊고 조작하기 쉬운 데이터 구조입니다.


3
글쎄, 그것은 일종의 답변 (그리고 좋은 조언)이지만 더 많은 의견입니다.
Drew

4

Emacs 25가 될 현재 Git HEAD의 Emacs를 사용하면 다음과 같은 작업을 수행 할 수 있습니다. 즉, 새로운 seq-partition기능 으로 plist를 쉽게 alist로 바꾸고 standard를 사용하여 alist 항목을 처리 할 수 ​​있습니다 mapcar.

(let* ((my-plist (list :a 1 :b 2 :c 3 :more (list 4 5 6)))
       (my-alist (seq-partition my-plist 2))
       (my-reverse-alist (mapcar (lambda (entry)
                                   (let ((prop (car entry))
                                         (val  (cadr entry)))
                                     (list val prop)))
                                 my-alist)))
  (message "my-plist: %s\nmy-alist: %s\nmy-reverse-alist: %s"
           my-plist my-alist my-reverse-alist))
;; my-plist: (:a 1 :b 2 :c 3 :more (4 5 6))
;; my-alist: ((:a 1) (:b 2) (:c 3) (:more (4 5 6)))
;; my-reverse-alist: ((1 :a) (2 :b) (3 :c) ((4 5 6) :more))

seq-partition사용 하여 문서를 살펴보십시오 C-h f seq-partition RET.


1

하나 개의 아이디어가 사용하는 것입니다 -map-indexed에서 dash단지리스트의 홀수 값으로 변환을 적용 :

(-non-nil (-map-indexed (lambda (index item) (when (oddp index) item))
  '(a x b y c z))) ; => (x y z)
(-non-nil (--map-indexed (when (oddp it-index) it) '(a x b y c z))) ; => (x y z)

또 다른 아이디어는 ht<-plistfrom을 사용하여 plist를 해시 테이블로 변환하는 것 입니다 ht.

(ht-values (ht<-plist '(a x b y c z) 'equal)) ; (z y x)

해시 테이블은 항목의 순서를 유지하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.