2010-02-10 35 views
31

Tôi đang cố gắng học lisp, sử dụng phương ngữ emacs và tôi có một câu hỏi. hãy để chúng tôi nói rằng danh sách có một số thành viên, trong đó vị từ đánh giá sai. làm cách nào để tạo danh sách mới mà không có các thành viên đó? một cái gì đó như { A in L: p(A) is true }. trong python có chức năng lọc, có cái gì đó tương đương trong lisp? nếu không, làm thế nào để làm điều đó?lisp lọc ra kết quả từ danh sách không phù hợp với vị ngữ

Cảm ơn

Trả lời

38

Những chức năng trong gói CL, bạn sẽ cần phải (require 'cl) để sử dụng chúng:

(remove-if-not #'evenp '(1 2 3 4 5)) 

này sẽ trả về một danh sách mới với tất cả các số chẵn từ cuộc tranh cãi.

Đồng thời tra cứu delete-if-not, thực hiện tương tự, nhưng sửa đổi danh sách đối số của nó.

+6

Tôi muốn chỉ ra rằng hàm '# 'remove-if-not' không được dùng trong Common Lisp [¹] (http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/ HyperSpec/Body/fun_removecm__elete-if-not.html) trong đó bộ lọc sẽ được viết '(remove-if (bổ sung # 'evenp)' (1 2 3 4 5))' hoặc đơn giản '(remove-if # 'oddp' (1 2 3 4 5)) '- chức năng' bổ sung' không tồn tại trong Emacs Lisp với kiến ​​thức của tôi. –

+1

Xin vui lòng sử dụng gói * cl-lib * và sử dụng chức năng ** cl-remove-if-not ** để thay thế. –

19

Tôi đang tìm kiếm đêm qua rất giống nhau và đã xem qua số Elisp Cookbook trên EmacsWiki. The section on Lists/Sequences chứa các bộ lọc teqniques và hiển thị cách thức này có thể được thực hiện với mapcardelq. Tôi đã phải mod mã để sử dụng nó cho mục đích riêng của tôi, nhưng đây là bản gốc:

;; Emacs Lisp doesn’t come with a ‘filter’ function to keep elements that satisfy 
;; a conditional and excise the elements that do not satisfy it. One can use ‘mapcar’ 
;; to iterate over a list with a conditional, and then use ‘delq’ to remove the ‘nil’ 
;; values. 

    (defun my-filter (condp lst) 
    (delq nil 
      (mapcar (lambda (x) (and (funcall condp x) x)) lst))) 

;; Therefore 

    (my-filter 'identity my-list) 

;; is equivalent to 

    (delq nil my-list) 

;; For example: 

    (let ((num-list '(1 'a 2 "nil" 3 nil 4))) 
    (my-filter 'numberp num-list)) ==> (1 2 3 4) 

;; Actually the package cl-seq contains the functions remove-if and remove-if-not. 
;; The latter can be used instead of my-filter. 
+1

Liên kết bị hỏng; vị trí mới là http://www.emacswiki.org/emacs/ElispCookbook#toc37 (nhưng stackoverflow sẽ không cho phép tôi chỉnh sửa ít hơn 6 ký tự ...) – robru

+0

liên kết mới là: https://www.emacswiki.org/ emacs/ElispCookbook # toc39 – Cheeso

18

Nếu bạn thao tác danh sách nặng nề trong mã của bạn, hãy sử dụng dash.el thư viện lập trình chức năng hiện đại, thay vì viết code soạn sẵn và tái phát minh bánh xe. Nó có mọi chức năng để làm việc với các danh sách, cây, ứng dụng chức năng và điều khiển luồng mà bạn có thể tưởng tượng. Để giữ tất cả các yếu tố phù hợp với một vị ngữ và loại bỏ những người khác bạn cần -filter:

chức năng hấp dẫn khác bao gồm -remove, -take-while, -drop-while:

(-remove (lambda (x) (> x 2)) '(1 2 3 4 5)) ; (1 2)  
(-take-while (lambda (x) (< x 3)) '(1 2 3 2 1)) ; (1 2) 
(-drop-while (lambda (x) (< x 3)) '(1 2 3 2 1)) ; (3 2 1) 

là gì tuyệt vời về dash.el được rằng nó hỗ trợ anaphoric macros . Anaphoric macro hoạt động giống như các hàm, nhưng chúng cho phép cú pháp đặc biệt để làm cho mã ngắn gọn hơn. Thay vì cung cấp một đối số anonymous function, chỉ cần viết s-expression và sử dụng it thay vì biến cục bộ, như x trong các ví dụ trước. Tương ứng macro anaphoric bắt đầu với 2 dấu gạch ngang thay vì một:

(--filter (> it 2) '(1 2 3 4 5)) ; (3 4 5) 
(--remove (> it 2) '(1 2 3 4 5)) ; (1 2) 
(--take-while (< it 3) '(1 2 3 2 1)) ; (1 2) 
(--drop-while (< it 3) '(1 2 3 2 1)) ; (3 2 1) 
+0

Thư viện tuyệt vời, cảm ơn vì đã chỉ cho tôi thư viện! – JustGage

0

Với lisp thông thường, bạn có thể thực hiện các chức năng như sau:

(defun my-filter (f args) 
    (cond ((null args) nil) 
     ((if (funcall f (car args)) 
      (cons (car args) (my-filter f (cdr args))) 
      (my-filter f (cdr args)))))) 

(print 
     (my-filter #'evenp '(1 2 3 4 5))) 
3

Emacs bây giờ đi kèm với thư viện seq.el, sử dụng seq-remove.

seq-remove (pred sequence) 
"Return a list of all the elements for which (PRED element) is nil in SEQUENCE." 
0

Thật ngạc nhiên khi không có phiên bản dựng sẵn của bộ lọc mà không cl hoặc (hoặc seq mà là rất mới).

Việc triển khai filter được đề cập ở đây (mà bạn thấy trong Sách hướng dẫn Elisp và các nơi khác) không chính xác. Nó sử dụng nil làm điểm đánh dấu cho các mục cần xóa, có nghĩa là nếu bạn có nil s trong danh sách của mình để bắt đầu, chúng sẽ bị xóa ngay cả khi chúng thỏa mãn vị từ.

Để sửa lỗi triển khai này, cần phải thay thế các điểm đánh dấu nil bằng biểu tượng không liên kết (ví dụ: gensym).

(defun my-filter (pred list) 
    (let ((DELMARKER (make-symbol "DEL"))) 
    (delq 
     DELMARKER 
     (mapcar (lambda (x) (if (funcall pred x) x DELMARKER)) 
       list)))) 
0

Có rất nhiều cách để lọc hoặc chọn nội dung từ danh sách bằng cách sử dụng công cụ dựng sẵn nhanh hơn nhiều so với vòng lặp. Có thể sử dụng remove-if tích hợp theo cách này. Ví dụ, giả sử tôi muốn thả các phần tử từ 3 đến 10 trong danh sách MyList. Thực thi đoạn mã sau làm ví dụ:

(let ((MyList (number-sequence 0 9)) 
     (Index -1) 
    ) 
    (remove-if #'(lambda (Elt) 
        (setq Index (1+ Index)) 
        (and (>= Index 3) (<= Index 5)) 
       ) 
       MyList 
      ) 
) 

Bạn sẽ nhận được '(0 1 2 6 7 8 9).

Giả sử bạn chỉ muốn giữ các phần tử từ 3 đến 5. Bạn về cơ bản, lật điều kiện mà tôi đã viết ở trên trong vị từ.

(let ((MyList (number-sequence 0 9)) 
     (Index -1) 
    ) 
    (remove-if #'(lambda (Elt) 
        (setq Index (1+ Index)) 
        (or (< Index 3) (> Index 5)) 
       ) 
       MyList 
      ) 
) 

Bạn sẽ nhận được '(3 4 5)

Bạn có thể sử dụng bất cứ điều gì bạn cần cho vị mà bạn phải cung cấp để loại bỏ-nếu. Giới hạn duy nhất là trí tưởng tượng của bạn về những gì để sử dụng. Bạn có thể sử dụng các chức năng lọc tuần tự, nhưng bạn không cần chúng. Ngoài ra, bạn cũng có thể sử dụng mapcar hoặc mapcar * để lặp qua danh sách bằng cách sử dụng một số chức năng để chuyển các mục cụ thể thành không và sử dụng (remove-if nil ...) để thả nils.

Các vấn đề liên quan