在Common Lisp函数中摆弄参数顺序

时间:2015-04-30 09:18:49

标签: coding-style common-lisp

阅读Python's range() analog in Common Lisp之后,我开始认为我并不喜欢答案中使用的功能界面。

出现了三个不同的lambda列表:

  1. (start end &optional (step 1))startend参数都是必需的。

  2. (end &key (start 0) (step 1)):恕我直言,使用关键字参数对于这样一个简单的函数来说似乎有点过头了,它们只是为了隐藏endstart没有出现在自然顺序(即第一个start,然后是end

  3. (n &key (start 0) (step 1))(来自alexandria:iota):这里,参数的可选性和顺序是正确的,但代价是使用不同的抽象。

  4. 问题是我想写(range 6)来生成(0 1 2 3 4 5),还要(range 3 6)来生成(3 4 5)。实际上,它很容易实现;例如:

    (defun range (start_or_end &optional end (step 1))
      (multiple-value-bind (start end)
          (if end
              (values start_or_end end)
              (values 0 start_or_end))
        (loop for n from start below end by step collect n)))
    

    但是,我还没有看到这种争论在其他代码中摆弄,而作为一个Lisp新手,我想知道这是否是一个可接受的成语。

    更新:我刚刚发现Racket提供的range功能类似于我提议的功能(也是in-range生成器)。

2 个答案:

答案 0 :(得分:4)

作为Alessio Stalla pointed out,这与此没有关系,但它不是你经常看到的东西。当语言允许可选和休息参数时,arity的重载变得更加复杂。

我认为通常会处理这种情况的方式是用指定符来定义事物。您可以声明范围由三个值确定:开始,结束和步骤。然后你可以说范围指示符是一个长度最多为3的列表,具有以下语义:

  • (n)指定(:start 0 :end n :step 1)
  • (m n)指定(:start m :end n :step 1)
  • (m n s)指定(:start m :end n :step s)

然后你可以做类似的事情:

(defun range (&rest range-designator)
  (destructuring-bind (a &optional (b nil bp) (c nil cp))
      range-designator
    (multiple-value-bind (start end step)
        (cond
          (cp (values a b c))
          (bp (values a b 1))
          (t  (values 0 a 1)))
      (loop for x from start to end by step
           collect x))))

CL-USER> (range 5)
(0 1 2 3 4 5)
CL-USER> (range 2 7)
(2 3 4 5 6 7)
CL-USER> (range 2 7 3)
(2 5)

如果你期望在其他地方使用范围指示符,你可以将内部的东西拉出来:

(defun to-range (designator)
  (destructuring-bind (a &optional (b nil bp) (c nil cp))
      designator
    (cond
      (cp (values a b c))
      (bp (values a b 1))
      (t  (values 0 a 1)))))

(defun range (&rest range-designator)
  (multiple-value-bind (start end step)
      (to-range range-designator)
    (loop for x from start to end by step collect x)))

答案 1 :(得分:2)

虽然你不经常遇到它,但这是可以接受的。我很确定标准中有这样的签名功能,但我现在记不起了。

我记得的一个例子是ABCL中的JFIELD原语:http://abcl.org/trac/wiki/JavaFfi#FunctionJFIELDJFIELD-RAWSETFJFIELD

如果你担心性能,因为“解析”lambda列表有成本,你可以使用编译器宏来避免支付它,特别是在像你这样的情况下,函数的行为只由数字驱动参数(与其类型相对)。