考虑来自OpenCV Tutorial 8 - Chapter 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)
为!first
,我认为这是正确的。
在C ++中我做:
static int first = 1;
if( first ){
cout << "reached this part of code " << endl << " " << first << endl << endl;
}
但是由于代码设计,似乎永远不会产生任何输出。为什么教程代码的设计者会这样?他正在复制学习OpenCV 。
答案 0 :(得分:5)
C代码中的变量first
为static
,这意味着它只有一个实例,并且在函数的所有调用之间共享。 (有关{C}中static
的更多信息,请参阅What does "static" mean?的已接受答案。)这有点像拥有一个全局变量,除了其他函数无法访问它(因为它不在他们的范围)。你可以使用用defvar
或defparameter
定义的全局变量在Common Lisp中模拟这个,但是我认为更直接的翻译会通过将整个defun
包装在一起来使其保持在翻译函数的本地let
。
首先,让我们看一下类似结构的东西。此代码执行第一次时间,但不执行后续调用:
(let ((firstp t))
(defun frob (bar)
(when firstp
(print 'initialized)
(setf firstp nil))
(print (list 'frobbed bar)))
nil)
现在,当我们运行第一次时,firstp
为真,所以我们会在输出中看到initialized
,但在后续运行中,我们赢了T:
CL-USER> (frob 'bar3)
INITIALIZED ; printed output
(FROBBED BAR3) ; printed output
;=> NIL
CL-USER> (frob 'bar5)
(FROBBED BAR5) ; printed output
;=> NIL
你得到的C代码实际上是在每次调用时做的事情,除了第一个。你也可以这样说,“除非这是第一次,做一些事情。”“除非”意味着提示unless
,你可以使用它来清理你的代码。除了let
- 包装 - 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)))
答案 1 :(得分:1)
我不知道您的Lisp发生了什么,甚至不知道您的问题是什么,但原始代码将在第一次调用该函数时跳过! first
块中的代码并且。
我认为你缺少的是static int first = 1;
因为变量first是静态的(即没有存储在堆栈中),它将在函数调用之间保留它的值。因此,对于第一个之后的所有呼叫,首先将为0(即,不是第一个)。
答案 2 :(得分: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) ...))
最后,如果由于某种原因既没有实现,也可以通过使用结构或类来保持状态,在这里写一个完整的例子太过分了,但你可以阅读它here。 / p>
最后,有时你确实会发现需要一个包级特殊变量,尽管很多人会认为最好将这些代码包装在一个隐藏用户变量的宏中,如下所示:
(defparameter *firstp* t)
(defmacro with-first (&body body)
`(let (*firstp*) ,@body))