公共Lisp:非nil参数及其名称,如何命名?

公共Lisp:非nil参数及其名称,如何命名?,lisp,arguments,common-lisp,Lisp,Arguments,Common Lisp,我对常见的Lisp和编程非常陌生,我正在尝试编写一个函数,将所有非nil参数转换为一个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 (acon

我对常见的Lisp和编程非常陌生,我正在尝试编写一个函数,将所有非nil参数转换为一个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名称,那么使用loop或mapcar可以很容易地过滤掉nil,但是因为它没有,所以我看不出如何“自动”完成此操作。
如果人们认为这种方式是不自然的,我对所描述的解决方案以外的其他解决方案完全感兴趣

编辑:下面是我正在尝试做的一个示例:

创建了一个对象,其中包含非固定数量的数据对和一些标记,例如:

user = "someone"  
creation-time = (get-universal-time)  
color-of-sky = "blue"  
temperature-in-celsius = 32  
language = "Common Lisp"
...  
tags = '("one" "two" "three")
这些属性(即键/参数名称)每次都可能不同。然后将新对象添加到集合中;我认为该数组可能工作得很好,因为我需要恒定的访问时间,并且只需要一个数字ID。
集合将无限期地容纳越来越多的此类自定义对象。
我希望能够快速访问与这些对象中使用的任何标记的任何组合相匹配的所有对象。

由于数组应该在很长一段时间内存储越来越多的数据,所以我不希望每次需要搜索标记时都解析其中的每一项。因此,我还将具有给定标记的每个对象的索引存储在一个哈希表中的标记名下。我已经编写了这个函数,但我发现困难的是如何收集数据并将其转换为列表或任何我可以轻松解析、索引和存储的内容。

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

(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))
请注意,我们需要特殊声明,否则符号值不起作用(您需要符号值的全局绑定)。我找不到摆脱它的办法

再看看你的问题,看起来你实际上不想让那些没有通过的关键字参数通过。在这种情况下,您可以解析
&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))

此宏将定义一个函数,该函数在执行主体时将其非nil参数转换为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))

或者他可以使用&REST参数定义F,宏将解析关键字列表本身。这不起作用,因为&REST只包含实际指定的参数。所以用(f:a1)调用(defun f(&rest r&key abc)…)将r绑定到“(a1)。嗯,重读这个问题,看起来这可能是他想要的。我对我的答案做了一个编辑:希望这更有帮助。谢谢,这很有帮助。我已经盯着它看了一段时间,并尝试了它,它似乎就是我想要的——坦率地说,我还不能确定,因为我的大脑在尝试使用宏的想法后停止了工作P我认为这对我看这一阶段非常有用,但是有了这一点,我仍然需要提前知道关键点,不是吗?另外,您可以演示如何两次映射&rest列表吗?我认为您在概念上做了一些错误的事情。你能提供一些样本数据和它们应该发生什么吗?谢谢你,我想这是我现在要用的,因为它稍微短一点。如果我必须提前定义属性名,我想不出为什么我需要在另一个答案中使用关键字。是否有可能在编译时不知道这些的情况下创建列表?