Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何复制使用Lisp闭包生成的计数器?_Lisp_Common Lisp - Fatal编程技术网

如何复制使用Lisp闭包生成的计数器?

如何复制使用Lisp闭包生成的计数器?,lisp,common-lisp,Lisp,Common Lisp,Lisp闭包的经典示例是以下返回计数器的函数: (defun make-up-counter () (let ((n 0)) #'(lambda () (incf n)))) 调用时,它递增计数并返回结果: CL-USER > (setq up1 (make-up-counter)) #<Closure 1 subfunction of MAKE-UP-COUNTER 20099D9A> CL-USER > (funcall up1) 1 CL-USER

Lisp闭包的经典示例是以下返回计数器的函数:

(defun make-up-counter ()
  (let ((n 0))
    #'(lambda () (incf n))))
调用时,它递增计数并返回结果:

CL-USER > (setq up1 (make-up-counter))
#<Closure 1 subfunction of MAKE-UP-COUNTER 20099D9A>

CL-USER > (funcall up1)
1

CL-USER > (funcall up1)
2
以下是我最好的尝试:

(defun make-up-counter ()
  (let ((n 0))
    #'(lambda (&optional copy)
        (if (null copy)
            (incf n)
          (let ((n 0))
            #'(lambda () (incf n)))))))
要返回计数器的副本,请使用参数t调用它:

(defun copy-counter (counter) (funcall counter t))
它适用于第一代副本:

CL-USER > (setq up2 (copy-counter up1))
#<Closure 1 subfunction of MAKE-UP-COUNTER 200DB722>

CL-USER > (funcall up2)
1
CL-USER>(设置up2(复制计数器up1))
#
CL-USER>(funcall up2)
1.
但如果你试图复制up2,显然是行不通的。我不知道如何让它正常工作,因为化妆柜的定义需要在自己的定义中有一个自身的副本


有什么建议吗?

要解决这个问题,您需要使用递归函数,使用
标签

(defun make-up-counter ()
  (labels ((new ()
             (let ((n 0))
               (lambda (&optional copy)
                 (if copy
                     (new)
                     (incf n))))))
    (new)))
copy
为true时,您甚至可以使其复制当前计数器值:

(defun make-up-counter ()
  (labels ((new (n)
             (lambda (&optional copy)
               (if copy
                   (new n)
                   (incf n)))))
    (new 0)))
为了两全其美,如果
copy
是数字,您可以让它创建一个具有指定值的计数器,否则只需复制计数器值,如果truthy,否则递增:

(defun make-up-counter ()
  (labels ((new (n)
             (lambda (&optional copy)
               (cond ((numberp copy) (new copy))
                     (copy (new n))
                     (t (incf n))))))
    (new 0)))

下面是另一种解决方案,
copy counter
获取一个计数器作为参数,并从参数的当前值开始返回一个新计数器,但使用另一个变量:

(defun new-counter(&optional (n 0))
  (lambda (&optional noincrement)
    (if noincrement n (incf n))))

(defun copy-counter(c)
  (new-counter (funcall c t)))
这是一个测试:

CL-USER> (let* ((up1 (new-counter))
                (up2 (progn (funcall up1) (funcall up1) (copy-counter up1))))
           (print (funcall up2))
           (print (funcall up2))
           (print (funcall up2))
           (print (funcall up1))
           "end test")

3 
4 
5 
3 
"end test"

解决此问题的最简单方法是
补偿计数器
从中提取一个数字进行计数

(defun make-up-counter (&optional (initial-count 0))
  (let ((count initial-count))
    #'(lambda () (incf count))))
但是,这并不能解决问题,因为我们无法检查计数器中的值,因此,如果我们尝试使用计数器为值设定种子,我们将修改当前计数器

(defun copy-counter (counter)
  (make-up-counter (funcall counter)))
为了提供一种获取值的方法,我们让闭包接受一个“operation”参数,因此我们可以检查或增加值

(defun make-up-counter (&optional (initial-count 0))
  (let ((count initial-count))
    #'(lambda (&optional (operation :increment))
        (ecase operation
          (:inspect count)
          (:increment (incf count))))))

(defun copy-counter (counter)
  (make-up-counter (funcall counter :inspect)))
现在,当我们运行以下代码时

(let ((1st-counter (make-up-counter)))
  (loop :repeat 3 :do (funcall 1st-counter))
  (let ((2nd-counter (copy-counter 1st-counter)))
    (loop :repeat 3 :do (funcall 1st-counter))
    (format t "1st counter: ~A~%2nd counter: ~A~%"
            (funcall 1st-counter :inspect)
            (funcall 2nd-counter :inspect))))
它打印

第一柜台:6

第二柜台:3


没有真正回答这个问题。但是这样的话,拷贝会更容易

(defun make-up-counter ()
  (let ((n 0))
    #'(lambda () (incf n))))
一般来说,我会避免在可维护软件中生产使用此类代码的更复杂版本。调试和反省会更困难。这是过去的基本FP知识(使用闭包隐藏可变状态,例如,请参见早期的方案文件),但对于任何更复杂的内容来说,这都是一种痛苦。它隐藏了有用的值,但同时也使调试变得困难。Minimum是一个能够查看闭包绑定的调试器/检查器。它很方便,因为它很容易写,但代价是以后支付的

问题:

CL-USER 36 > (make-up-counter)
#<anonymous interpreted function 40600015BC>
然后:

CL-USER 44>(let((c(make-instance'counter:start 10)))
(列表(下一个值c)
(下一个值c)
(下一个值c)
c) )
(10 11 12 #)
CL-USER 45>(描述(第四*)
#这是一个柜台
价值13
正确的做法是避免使用闭包作为对象。如图所示,您可以使用
defclass
,但对于一个简单的计数器,您只需编写:

(defstruct counter (value 0))
这定义了所有您需要的:
(生成计数器)
(生成计数器:值x)
(复制计数器c)
所有工作都按预期进行。您的对象也可以以可读的方式打印

(let* ((c (make-counter))
       (d (copy-counter c)))
  (incf (counter-value c))
  (values c d))

#S(counter :value 1)
#S(counter :value 0)

您仍然应该导出更高级别的函数,如reset和next,这样计数器的用户就不需要知道它是如何实现的。

我们可以定义一个用于构造计数器的API,以便:

(make-counter <integer>) --> yields new counter starting at <integer>
(make-counter <counter>) --> yields a clone of counter
试运行:

[1]> (defvar x (make-counter 3)) X [2]> (funcall x) 3 [3]> (funcall x) 4 [4]> (defvar y (make-counter x)) Y [5]> (funcall x) 5 [6]> (funcall x) 6 [7]> (funcall x) 7 [8]> (funcall y) 5 [9]> (funcall y) 6 [10]> (funcall x) 8 [11]> (funcall y) 7
复制计数器
只不过是对象上基于低级操作码的分派API的包装器。

对于这样的简单对象是合适的。
(defstruct counter (value 0))
(let* ((c (make-counter))
       (d (copy-counter c)))
  (incf (counter-value c))
  (values c d))

#S(counter :value 1)
#S(counter :value 0)
(make-counter <integer>) --> yields new counter starting at <integer>
(make-counter <counter>) --> yields a clone of counter
(defun make-counter (&optional (constructor-arg 0))
  (etypecase constructor-arg
    (integer 
      (lambda (&optional opcode) ;; opcode selects method
        (ecase opcode ;; method dispatch
          ((nil) (prog1 constructor-arg (incf constructor-arg)))
          (copy-self (make-counter constructor-arg)))))
    (function
      (funcall constructor-arg 'copy-self))))
[1]> (defvar x (make-counter 3)) X [2]> (funcall x) 3 [3]> (funcall x) 4 [4]> (defvar y (make-counter x)) Y [5]> (funcall x) 5 [6]> (funcall x) 6 [7]> (funcall x) 7 [8]> (funcall y) 5 [9]> (funcall y) 6 [10]> (funcall x) 8 [11]> (funcall y) 7
(defun make-counter (&optional (constructor-arg 0))
  (lambda (&optional opcode)
    (ecase opcode
      ((nil) (prog1 constructor-arg (incf constructor-arg)))
      (copy-self (make-counter constructor-arg)))))

(defun copy-counter (counter)
  (funcall constructor-arg 'copy-self))