When working on a personal project, I found what seems to be an inconsistent behaviour of closures.
The idea in the codes below is to find the maximum within several numbers (1, 3, 2, 5, 4) by inspecting them one after the other. The maximum is obviously 5. The codes below seem sophisticated, since they are minimal examples derived from a more complicated case.
Code 1, with plain numbers --> it works
(defun look-for-maximum-1 ()
(labels ((test-successive-values-with-function (f)
(let ((z 0))
(setf z 1)
(funcall f z)
(setf z 3)
(funcall f z)
(setf z 2)
(funcall f z)
(setf z 5)
(funcall f z)
(setf z 4)
(funcall f z))))
(let ((maximum -1))
(test-successive-values-with-function
;; function to check if a given number is the new maximum:
(lambda (x)
(format t "~&Inspecting ~S whereas maximum is ~S" x maximum)
(when (> x maximum)
(format t "~&Maximum was: ~S" maximum)
(setf maximum x)
(format t "~&Maximum is now: ~S" maximum))))
(format t "~&Final maximum: ~S" maximum))))
The call to (look-for-maximum-1)
brings the following output:
Inspecting 1 whereas maximum is -1
Maximum was: -1
Maximum is now: 1
Inspecting 3 whereas maximum is 1
Maximum was: 1
Maximum is now: 3
Inspecting 2 whereas maximum is 3
Inspecting 5 whereas maximum is 3
Maximum was: 3
Maximum is now: 5
Inspecting 4 whereas maximum is 5
Final maximum: 5
This output is correct: the program progressively finds the maximum (which is 5).
Code 2 = the same with numbers encapsulated in one-element lists --> it does not work
(defun look-for-maximum-2 ()
(labels ((compare-car (list1 list2)
"Returns true if and only if the car of list1 is > than car of list2"
(> (car list1) (car list2)))
(test-successive-values-with-function (f)
(let ((z '(0)))
(setf (car z) 1)
(funcall f z)
(setf (car z) 3)
(funcall f z)
(setf (car z) 2)
(funcall f z)
(setf (car z) 5)
(funcall f z)
(setf (car z) 4)
(funcall f z))))
(let ((maximum '(-1)))
(test-successive-values-with-function
;; function to check if a given number is the new maximum:
(lambda (x)
(format t "~&Inspecting ~S whereas maximum is ~S" x maximum)
(when (compare-car x maximum)
(format t "~&Maximum was: ~S" maximum)
(setf maximum x)
(format t "~&Maximum is now: ~S" maximum))))
(format t "~&Final maximum: ~S" maximum))))
The call to (look-for-maximum-2)
brings the following output:
Inspecting (1) whereas maximum is (-1)
Maximum was: (-1)
Maximum is now: (1)
Inspecting (3) whereas maximum is (3)
Inspecting (2) whereas maximum is (2)
Inspecting (5) whereas maximum is (5)
Inspecting (4) whereas maximum is (4)
Final maximum: (4)
This is incorrect. The program seems to lose track of the maximum, whereas it is within the closure. The maximum seems systematically set to the value of the inspected number, whereas it should not be this way.
Any idea for the above-described strange behaviour?
The codes were tested with: - CLISP through Emacs+Slime - SBCL through Emacs+Slime - Common Lisp on line: https://www.tutorialspoint.com/execute_lisp_online.php