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

StackOverflow https://stackoverflow.com/questions/2234860

Вопрос

Я пытаюсь выучить LISP, используя диалект EMACS, и у меня есть вопрос. Допустим, в списке есть некоторые участники, для которых предикат оценивается в False. Как создать новый список без этих участников? что-то типа { A in L: p(A) is true }. Анкет В Python есть функция фильтра, есть ли что -то эквивалентное в LISP? Если нет, как мне это сделать?

Спасибо

Это было полезно?

Решение

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

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

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

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

Другие советы

Если вы манипулируете списками в своем коде, пожалуйста, используйте 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-экспрессия и использовать it вместо локальной переменной, например x В предыдущих примерах. Соответствующие анафорические макросы начинаются с 2 черт вместо одного:

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

Я искал то же самое вчера вечером и наткнулся на Кулинарная книга Elisp на Emacswiki. Раздел на списках/последовательностях содержит фильтрацию teqniques и покажите, как это можно сделать с 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.

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

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

С помощью Common 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)))

Существует множество способов фильтрации или выбора материала из списка, используя встроенные встроенные данные, которые намного быстрее, чем петли. Встроенный удаление может быть использован таким образом. Например, предположим, что я хочу сбросить элементы с 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 ...), чтобы сбросить NIL.

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

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

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

(defun my-filter (pred list)
  (let ((DELMARKER (make-symbol "DEL")))
    (delq
      DELMARKER
      (mapcar (lambda (x) (if (funcall pred x) x DELMARKER))
              list))))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top