【问题标题】:Common Lisp: Efficiently handling supplied-p-parameter efficiently without using &rest argumentsCommon Lisp:在不使用 &rest 参数的情况下有效地处理提供的 p 参数
【发布时间】:2021-04-04 17:21:12
【问题描述】:

假设我有这个功能

(defun bar (a &optional (b nil bp))
  (declare (ignore a b bp)) ; <-- replace it with (list a b bp)
                            ;     to check for correctness
  )
;; I want to preserve the value of bp here inside bar, and inside foo below

我希望围绕bar写一个包装器:

(defun foo (a &optional (b nil bp))
  (declare (optimize speed))
  (apply #'bar a (nconc (when bp (list b)))))

同时在从foobar 的调用中保留bp 的值,同时保持参数b 在emacs 的迷你缓冲区/eldoc 模式中可见。我想知道是否有一种可能不可移植的非 consing 方法来在此调用中保留 bp 的值。

例如,

CL-USER> (time (loop repeat 1000000 do (foo 4 2)))
Evaluation took:
  0.040 seconds of real time
  0.043656 seconds of total run time (0.043656 user, 0.000000 system)
  110.00% CPU
  96,380,086 processor cycles
  15,990,784 bytes consed <--- plenty of consing
  
NIL

如果我忽略 b-visibility-in-eldoc,我可能会使用 &amp;rest 参数,但我确实希望参数可见。

虽然在这种特殊情况下还有其他方法可以实现这一点,但我确实想考虑存在多个 &optional(或 &keyword)参数的情况。

【问题讨论】:

  • 你为什么使用nconc?只需写(apply #'bar a (when bp (list b))))
  • @adabsurdum 在这个特定的例子中, nconc 没有相关性,是的;当我写它时,我打算将它用于可能有多个可选参数的情况,比如另一个(c nil cp)

标签: performance common-lisp


【解决方案1】:

使用cond 表单来决定如何调用bar

(defun bar (a &optional (b nil bp) (c nil cp))
  (declare (ignore a b bp c cp)))

(defun foo (a &optional (b nil bp) (c nil cp))
  (declare (optimize speed))
  (cond (cp (funcall #'bar a b c))
        (bp (funcall #'bar a b))
        (t (funcall #'bar a))))
CL-USER> (time (loop repeat 1000000 do (foo 1 2 3)))
Evaluation took:
  0.015 seconds of real time
  0.017203 seconds of total run time (0.017148 user, 0.000055 system)
  113.33% CPU
  41,186,554 processor cycles
  0 bytes consed

检查传递给bar的参数:

(defun bar (a &optional (b nil bp) (c nil cp))
  (list a b bp c cp))
CL-USER> (foo 1 2 3)
(1 2 T 3 T)
CL-USER> (foo 1 2)
(1 2 T NIL NIL)
CL-USER> (foo 1)
(1 NIL NIL NIL NIL)

【讨论】:

  • 想想看,使用cond 似乎是处理&optional 参数的最佳方式。任何其他方法(包括使用nconclist)似乎至少需要线性数量的步骤来进行任何“设置”。我会等一会儿再接受。
  • &key 参数是否有等效版本?提供 &rest 参数可以帮助避免 consing,但会更改 slime/eldoc 显示的参数列表。 OTOH,使用cond 会导致许多分支。
  • 另一种选择是使用编译器宏,这是我一直在做的,但我想知道是否有其他方法可以实现这一点。
  • &amp;optional&amp;key 不能很好地结合在一起,但是对于 &amp;key 参数,您需要对每个组合进行测试;我没有看到任何其他方式。对于少量的关键字参数来说,这没什么大不了的。您可能可以编写一些宏来帮助您处理更多的参数。
  • @digikar (... &amp;rest args &amp;key a b c) 结合合适的 ignore 声明和 (apply ... args) 是关键字的规范方法。如果任何工具都不够智能,无法意识到该函数完全采用一组固定的关键字,那么在这种情况下,它需要变得更智能:将程序变形到无法识别以帮助工具跛行是没有意义的。
【解决方案2】:

我不确定 barfoo 实际上应该做什么,但是:

(defun foo (a &optional (b nil bp))
  (declare (optimize speed))
  (funcall #'bar a (when bp b))) 

CL-USER> (time (loop repeat 1000000 do (foo 4 2)))
Evaluation took:
  0.005 seconds of real time
  0.005810 seconds of total run time (0.005804 user, 0.000006 system)
  120.00% CPU
  8,099,624 processor cycles
  0 bytes consed

【讨论】:

  • 这不会在bar 中保留bp 的值。 funcall 版本将bp 的值生成为t,而apply 版本在未提供b 时生成nil
  • 嗯,我明白了。我只是专注于在你的包装函数中使用list,这显然会导致consing。
猜你喜欢
  • 1970-01-01
  • 2011-02-13
  • 1970-01-01
  • 2014-06-11
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多