使用remove-if-not和& key参数

时间:2015-01-10 22:05:42

标签: lisp common-lisp

我有以下代码,它应该是一个高阶函数,它根据输入的 &key 参数过滤元素(在本例中为:year:month和{{ 1}}。

:type

问题在于,除非使用所有关键字参数,否则它将始终返回 (defun filter-by (&key year month type) "Remove members of the list not matching the given year and/or month and/or type, returns a function that takes the list" (lambda (lst) (remove-if-not #'(lambda (element) (when year (equalp (local-time:timestamp-year (get-record-date element)) year))) (when month (equalp (local-time:timestamp-month (get-record-date element)) month))) (when type (equalp (get-type element) type)))) lst))) ,我猜是因为nil形式在when内的行为1}}。

无论如何不使用多个remove-if-not语句来完成这项工作? cond的问题在于我必须专门写下所有可能的参数组合,这对于3个参数是可以的,但是如果将来它很难扩展我想使用其他关键字进行过滤。

1 个答案:

答案 0 :(得分:2)

Common Lisp的关键字参数有一个特殊的语法,可以告诉你 是否提供参数。我认为你应该可以使用 这可以实现你想要的。

这是一个工作示例,尽管数据表示略有不同 因为我没有local-timeget-record-date的定义。您 应该能够轻松地将其适应您的代码。

(defun my-filter-by (lst &key
                         (year  nil year-p)   ;; nil is the default
                         (month nil month-p)  ;; year-p/month-p/day-p say whether
                         (day   nil day-p))   ;; the argument was supplied
  (remove-if-not
   (lambda (element)
     (let* ((year-okp (or (not year-p)
                          (equal year (cdr (assoc :year element)))))
            (month-okp (or (not month-p)
                           (equal month (cdr (assoc :month element)))))
            (day-okp (or (not day-p)
                         (equal day (cdr (assoc :day element)))))
            (all-okp (and year-okp month-okp day-okp)))
       all-okp))
   lst))

还有一些例子:

(defparameter *lst* '(((:year . 2000) (:month . :may) (:day . 17))
                      ((:year . 2000) (:month . :may) (:day . 18))
                      ((:year . 2001) (:month . :aug) (:day . 2))
                      ((:year . 2002) (:month . :jan) (:day . 5))))


(my-filter-by *lst*) ;; keeps everything
(my-filter-by *lst* :year 2000) ;; everything from 2000
(my-filter-by *lst* :year 2000 :day 17) ;; only 2000 / may 17