2

考虑一下OpenCV 教程 8 - 第 9 章中的这个 C 代码

// Learn the background statistics for one more frame
void accumulateBackground( IplImage *I ){
    static int first = 1;
    cvCvtScale( I, Iscratch, 1, 0 );
    if( !first ){
        cvAcc( Iscratch, IavgF );
        cvAbsDiff( Iscratch, IprevF, Iscratch2 );
        cvAcc( Iscratch2, IdiffF );
        Icount += 1.0;
    }
    first = 0;
    cvCopy( Iscratch, IprevF );
}

似乎代码的设计方式是因为

if( !first )

程序永远不会执行:

cvAcc( Iscratch, IavgF );
cvAbsDiff( Iscratch, IprevF, Iscratch2 );
cvAcc( Iscratch2, IdiffF );
Icount += 1.0;

在 Lisp 中,我试图将其翻译为:

(defun accumulate-background (i)
  (setf 1st 1)
  (cvt-scale i i-scratch-1 1 0) ;; To float
  (if (not 1st) 
      (progn (acc i-scratch-1 i-avg-f)
             (abs-diff i-scratch-1 i-prev-f i-scratch-2)
             (acc i-scratch-2 i-diff-f)
             (setf i-count (+ i-count 1.0))))
  (setf 1st 0)
  (copy i-scratch-1 i-prev-f))

对于等效功能,使用(not 1st)for !first,我认为这是正确的。在 C++ 中,我这样做:

static int first = 1;

if( first ){
  cout << "reached this part of code " << endl << " " << first << endl << endl;
} 

但由于代码设计,似乎永远不会产生任何输出。为什么教程的设计者会这样编码?他正在从Learning OpenCV复制。

4

3 回答 3

5

C 代码中的变量firststatic这意味着它只有一个实例,并且它在对函数的所有调用之间共享。(有关 C 语言的更多信息,请参阅What does "static" mean?的已接受答案static。)这有点像拥有一个全局变量,除了其他函数无权访问它(因为它不在它们的范围内) . 您可以使用用defvaror定义的全局变量在 Common Lisp 中模拟这一点defparameter,但我认为更直接的翻译会通过将整个defunin包装在翻译后的函数中来使其保持本地化let

首先,让我们看一下具有类似结构的东西。此代码一次执行某些操作,但在后续调用中不执行:

(let ((firstp t))
  (defun frob (bar)
    (when firstp
      (print 'initialized)
      (setf firstp nil))
    (print (list 'frobbed bar)))
    nil)

现在,当我们第一次运行firstp它是真的,所以我们会initialized在输出中看到,但在随后的运行中,我们不会:

CL-USER> (frob 'bar3)
INITIALIZED            ; printed output
(FROBBED BAR3)         ; printed output
;=> NIL

CL-USER> (frob 'bar5)
(FROBBED BAR5)         ; printed output
;=> NIL

您所拥有的 C 代码实际上是在除了第一次调用之外的每个调用中都做一些事情。您也可以将其表述为“除非是第一次,否则做某事”。那里的“除非”是暗示性的unless,你可以用它来清理你的代码。除了let-wrapping-defun和适当的缩进,我们还有:

(let ((first t))
  (defun accumulate-background (i)
    (cvt-scale i i-scratch-1 1 0) ;; To float
    (unless first
      (acc i-scratch-1 i-avg-f)
      (abs-diff i-scratch-1 i-prev-f i-scratch-2)
      (acc i-scratch-2 i-diff-f)
      (setf i-count (+ i-count 1.0)))
    (setf first nil)
    (copy i-scratch-1 i-prev-f)))
于 2013-09-28T11:49:36.850 回答
1

我不知道你的 Lisp 发生了什么,甚至不知道你的问题是什么,但原始代码将在! first第一次调用函数时跳过块中的代码,并且每次调用函数时都会执行.

我认为您缺少的是static int first = 1; 因为变量首先是静态的(即不存储在堆栈中),它将在函数调用中保留它的值。因此,对于第一个之后的所有调用,first 将为 0(即,不是第一个)。

于 2013-09-28T02:43:26.450 回答
0

这可能是一个见仁见智的问题,但我不喜欢 C 代码的原始方法。如果一个函数的状态取决于它收到的参数,那么我会更喜欢一些全局修改的状态。

所以,IMO,重写这个函数(不管是什么语言)的好方法是这样的:

(defun accumulate-background (impl-image &key firstp) ...)

或者,如果可能的话:

(defun accumulate-background (impl-image)
  (labels ((%accumulate-background (firstp) ...))
    (%accumulate-background t)))

还有另一种避免全局变量的方法,如下所示:

(let (firstp)
  (defun accumulate-background (impl-image) ...))

最后,如果由于某种原因两者都不实用,可以通过使用结构或类来持久化状态,在这里写一个完整的例子就太多了,但你可以在这里阅读。

最后,有时您确实会发现需要一个包级别的特殊变量,尽管许多人会认为最好将此类代码包装在一个对用户隐藏变量的宏中,如下所示:

(defparameter *firstp* t)
(defmacro with-first (&body body)
  `(let (*firstp*) ,@body))
于 2013-09-28T07:11:28.183 回答