Подтвердить что ты не робот

Lisp отфильтровать результаты из списка, не соответствующего предикату

Я пытаюсь изучить lisp, используя диалект emacs, и у меня есть вопрос. скажем, в списке есть некоторые члены, для которых предикат оценивается как false. как мне создать новый список без этих членов? что-то вроде { A in L: p(A) is true }. в python есть функция фильтра, есть ли что-то эквивалентное в lisp? если нет, как это сделать?

Спасибо

4b9b3361

Ответ 1

Эти функции находятся в пакете CL, вам нужно (require 'cl) использовать их:

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

Это вернет новый список со всеми четными числами из аргумента.

Также найдите delete-if-not, который делает то же самое, но изменяет список своих аргументов.

Ответ 2

Я искал ту же самую прошлую ночь и наткнулся на Elisp Cookbook на EmacsWiki. В разделе о списках/последовательностях содержатся теги фильтрации и показано, как это можно сделать с помощью mapcar и delq. Мне пришлось модифицировать код, чтобы использовать его для моих собственных целей, но вот оригинал:

;; 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.

Ответ 3

Если вы сильно манипулируете списками в своем коде, используйте dash.el современную библиотеку функционального программирования вместо того, чтобы писать шаблонный код и изобретать колесо. Он имеет все функции для работы со списками, деревьями, функцией приложения и управлением потоком, которые вы когда-либо могли себе представить. Чтобы сохранить все элементы, соответствующие предикату, и удалить другие, вам нужно -filter:

(-filter (lambda (x) (> x 2)) '(1 2 3 4 5)) ; (3 4 5)

Другие функции, представляющие интерес, включают -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)

Что хорошего в dash.el, так это то, что он поддерживает анафорические макросы. Анафорические макросы ведут себя как функции, но позволяют специальный синтаксис сделать код более кратким. Вместо предоставления анонимной функции в качестве аргумента просто напишите s-expression и используйте it вместо этого локальной переменной, например x в предыдущих примерах. Соответствующие анафорические макросы начинаются с двух тире вместо одного:

(--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)

Ответ 4

Теперь Emacs поставляется с библиотекой seq.el, используйте seq-remove.

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

Ответ 5

С общим lisp вы можете реализовать эту функцию следующим образом:

(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)))

Ответ 6

Удивительно, что нет встроенной версии фильтра без cl или (или seq, что является очень новым).

Реализация filter, упомянутая здесь (что вы видите в поваренной книге Elisp и в другом месте), неверна. Он использует nil как маркер для элементов, которые нужно удалить, а это означает, что если вы начинаете с nil в своем списке, они будут удалены, даже если они удовлетворяют предикату.

Чтобы исправить эту реализацию, маркеры nil должны быть заменены неинтерпретированным символом (т.е. gensym).

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

Ответ 7

Существует множество способов фильтрации или выбора материала из списка с использованием встроенных модулей, которые намного быстрее, чем циклы. Встроенный remove-if может использоваться таким образом. Например, предположим, что я хочу удалить элементы с 3 по 10 в список MyList. Выполните следующий код в качестве примера:

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

Вы получите '(0 1 2 6 7 8 9).

Предположим, что вы хотите сохранить только элементы между 3 и 5. В основном вы переворачиваете условие, указанное выше в предикате.

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

Вы получите '(3 4 5)

Вы можете использовать все, что вам нужно для предиката, который вы должны предоставить для удаления - если. Единственный предел - ваше воображение о том, что использовать. Вы можете использовать функции фильтрации последовательностей, но они вам не нужны.

В качестве альтернативы вы также можете использовать mapcar или mapcar * для циклического перехода по списку, используя некоторую функцию, которая превращает определенные записи в nil, а использование (remove-if nil...) для удаления nils.