Common Lisp:非零参数及其名称,如何?

时间:2011-04-12 06:51:24

标签: lisp arguments common-lisp

我对Common Lisp和编程很新,我正在尝试编写一个将所有非零args转换为alist的函数。到目前为止,我能想到的唯一方法是:

(let ((temp nil))
    (if arg1
        (setf temp (acons 'arg1 arg1 nil)))
    (if arg2
        (setf temp (acons 'arg2 arg2 temp)))
    ...
    (if arg20-ish
        (setf temp (acons 'arg20-ish arg20-ish temp)))

    (do-something-with temp))

这看起来不是很优雅,但是当需要改变这些参数时会有许多争论。 我正在寻找一种更聪明的方法,这既是为了编写这个特定的功能,也是为了学习如何在Lisp和/或函数式编程中思考。

对我来说,棘手的部分是弄清楚如何获取参数的名称或使用什么符号,而不用手动编码每个案例。如果& rest提供arg名称,很容易用循环或mapcar过滤掉NIL,但由于它没有,我无法看到如何“自动化”这个。
如果人们认为这种方式不自然,我对其他解决方案完全感兴趣。

修改:以下是我要做的一个示例:

创建一个对象,具有非固定数量的数据对和一些标记,例如:

user = "someone"  
creation-time = (get-universal-time)  
color-of-sky = "blue"  
temperature-in-celsius = 32  
language = "Common Lisp"
...  
tags = '("one" "two" "three")

这些属性(即键/ arg名称)每次都可能不同。然后将新对象添加到集合中;我认为数组可能运行良好,因为我想要不断的访问时间,只需要一个数字ID 该系列将无限期地容纳越来越多的此类自定义对象 我希望能够快速访问匹配这些对象中使​​用的任何标签的任何组合的所有对象 由于数组应该在很长一段时间内存储越来越多的数据,因此我不想在每次需要搜索标记时解析其中的每个项目。因此,我还将每个对象的索引与给定标记一起存储在标记名称下的哈希表中。我已经编写了这个函数,我发现很难找到如何收集数据并将其转换为alist或任何我可以轻松解析,索引和存储的内容。

2 个答案:

答案 0 :(得分:2)

这是一种使用宏的解决方案:

(defmacro named-args (fun-name alist-sym (&rest syms) &body body)
  `(defun ,fun-name (&key ,@syms)
     (declare (special ,@syms))
     (let ((,alist-sym
            (loop
               for s in ',syms
               collecting (cons s (symbol-value s)))))
       ,@body)))

然后您可以将其用于

之类的内容
(named-args f u (a b c)
  (format t "~A~%" u))

扩展为

(DEFUN F (&KEY A B C)
  (DECLARE (SPECIAL A B C))
  (LET ((U
         (LOOP FOR S IN '(A B C)
            COLLECTING (CONS S (SYMBOL-VALUE S)))))
    (FORMAT T "~A~%" U)))

最后,打电话会给出

(f :a 3) => ((A . 3) (B) (C))

请注意,我们需要特殊声明,否则symbol-value不起作用(您需要对symbol-value进行全局绑定)。我无法找到摆脱它的方法。

再次查看您的问题,看起来您实际上不希望未通过的关键字参数。在这种情况下,您可以解析&rest参数(虽然这是一个平面列表,因此您需要以两个为单位进行映射),或者您可以按如下方式修改宏:

(defmacro named-args (fun-name alist-sym (&rest syms) &body body)
  `(defun ,fun-name (&key ,@syms)
     (declare (special ,@syms))
     (let ((,alist-sym
            (loop
               for s in ',syms
               when (symbol-value s)
               collecting (cons s (symbol-value s)))))
       ,@body)))

然后你得到

(f :a 3) => ((A . 3))

答案 1 :(得分:2)

此宏将定义一个函数,该函数在执行正文时将其非零参数转换为alist 绑定:

(defmacro defnamed (fun-name alist-sym (&rest args) &body body)
  `(defun ,fun-name (,@args)
     (let ((,alist-sym))
      ,@(mapcar
         (lambda (s)
          `(when ,s
            (push (cons ',s ,s) ,alist-sym)))
         (reverse args))
      ,@body)))

演示:

(defnamed make-my alist (a b c) 
  alist)

(make-my 1 NIL 3)

=> ((A . 1) (C . 3))