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


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.