Common lisp CommonLisp:如果关键字参数已传递给我,如何传递它

Common lisp CommonLisp:如果关键字参数已传递给我,如何传递它,common-lisp,keyword-argument,optional-arguments,Common Lisp,Keyword Argument,Optional Arguments,我一次又一次地发现,函数a需要调用函数B,不管是否有关键字参数,这取决于这些关键字参数是否已被赋予函数a 也许愚蠢的MWE更容易理解:让我们问问一个叫汤米的男孩他在学校的日子过得怎么样: (定义答案和键(数学“无聊”数学测试?) (物理“重”物理测试?) (如果数学考试? (如果物理测试? (格式t“我数学得了~A,物理得了~A!”数学物理) (格式t“我数学得了~ A.”数学) (如果物理测试? (格式t“我从物理学中得了~ A.”物理学) (格式t“数学是~A,物理是~A.“数学物理”))

我一次又一次地发现,函数a需要调用函数B,不管是否有关键字参数,这取决于这些关键字参数是否已被赋予函数a

也许愚蠢的MWE更容易理解:让我们问问一个叫汤米的男孩他在学校的日子过得怎么样:

(定义答案和键(数学“无聊”数学测试?)
(物理“重”物理测试?)
(如果数学考试?
(如果物理测试?
(格式t“我数学得了~A,物理得了~A!”数学物理)
(格式t“我数学得了~ A.”数学)
(如果物理测试?
(格式t“我从物理学中得了~ A.”物理学)
(格式t“数学是~A,物理是~A.“数学物理”))
(defun父子对话和关键(数学无数学测试?)
(物理无物理测试?)
(格式t“你好,汤米,今天学校怎么样?~%”)
(条件((数学测试?物理测试?)
(回答:数学:物理)
(数学考试?
(答:数学)
(物理测试?
(答:物理)
(答)
这是预期的结果,但有几个原因让人感到厌烦:

  • answer
    的调用基本上是重复的。如果
    answer
    除了关键字参数之外还有几个正常参数,我必须格外小心,在所有四种情况下都要这样做。更糟糕的是,在维护这样的东西时

  • 复杂性随着关键字参数的数量呈指数增长。如果父亲也会问英语、地质学和汤米的午餐呢

  • 如果父亲和儿子有不同的默认参数,则很容易产生进一步的混淆(人们可能会尝试编写
    (答案:数学:物理)

  • 问题:假设
    答案
    是我必须遵守的界面的一部分,我如何简化
    父子对话
    ?理想的情况下,我想要

    (定义父子对话和键(数学无数学测试?)
    (物理无物理测试?)
    (格式t“你好,汤米,今天学校怎么样?~%”)
    (回答:数学(数学测试?):物理(物理测试?)
    
    解决这一问题的常见方法是应用
    apply

    (defun father-son-talk (&rest all-options &key math physics)
      (declare (ignore math physics))
      (format t "Hello Tommy, how was school today?~%")
      (apply #'answer all-options))
    
    如果父子对话本身根本不需要关键字参数,您可以进一步简化它

    (defun father-son-talk (&rest all-options)
      (format t "Hello Tommy, how was school today?~%")
      (apply #'answer all-options))
    
    对于这样的函数,一个很好的技巧是说您确实需要关键字参数,并且您接受任何关键字参数,然后将它们传递给实现函数:

    (defun father-son-talk (&rest all-options &key &allow-other-keys)
      (format t "Hello Tommy, how was school today?~%")
      (apply #'answer all-options))
    
    这与以前的版本类似,只是例如
    (父子对话1)
    现在是一个错误:它的参数必须都是关键字参数。您想要哪个取决于
    answer
    是否需要非关键字参数,因此这两个参数都很有趣

    还有一个很好的例子,比如函数本身需要关键字参数,但希望集合是可扩展的:函数得到的单个
    &rest
    参数实际上是一个属性列表,因此可以执行以下操作:

    (defun foo (&rest plist &key &allow-other-keys)
      ...
      (getf ':x plist 1) ...)
    

    我在你的回答中添加了几个其他案例,我认为值得一提:我希望没问题。如果您不喜欢这些更改,请拒绝编辑。您好@tfb,我对您的更改很满意。谢谢你们的“合作”:-)谢谢你们两位。这正是我所希望的那种口齿不清的解决方案:乍一看不是很合理,但由于一切都在列表中,所以第二眼就非常聪明。