I defined a local recursive function using flet
. The recursive function calculates factorials:
(flet ((my-fac (n)
(cond ((= n 1) 1)
(t (* n (my-fac (1- n)))))))
Then I used mapcar
to apply my-fac
to each element of a list to produce a list of factorials:
(defun my-mapping (lst x)
(flet ((my-fac (n)
(cond ((= n 1) 1)
(t (* n (my-fac (1- n)))))))
(mapcar #'my-fac lst)))
Note the unused second parameter variable in the my-mapping
definition. Here are the results:
CL-USER> (my-mapping '(1 2 3 4) 1)
(1 2 6 24)
Next, I changed the my-mapping
function to use the second parameter variable:
(defun my-mapping (lst x)
(flet ((my-fac (n)
(cond ((= n 1) x) ; <==========CHANGE HERE
(t (* n (my-fac (1- n)))))))
(mapcar #'my-fac lst)))
As a result of the change, I expected when I called (my-mapping '(1 2 3 4) 2)
, the results would be factorials that were multiplied by 2. Therefore, instead of getting the previous result:
(1 2 6 24)
I expected to get:
(2 4 12 48)
Here are my actual results
CL-USER> (my-mapping '(1 2 3 4) 2)
(2 2 6 24)
It looks to me like my-fac
picked up the value for x
when calculating the factorial for 1 (the first element in the list), but for the rest of the elements in the list it looks like x
had a value of 1 somehow.
There are two ways to define local functions: FLET
and LABELS
.
See the CL Hyperspec for the specification of FLET and LABELS.
It says about FLET:
The scope of the name binding encompasses only the body. Within the body of
flet
, function-names matching those defined byflet
refer to the locally defined functions rather than to the global function definitions of the same name.
It says about LABELS
:
labels
is equivalent toflet
except that the scope of the defined function names forlabels
encompasses the function definitions themselves as well as the body.
Means:
(flet ((my-local-function ()
...)) ; <- my-local-function CAN'T be used here
; my-local-function can be used in the body:
(my-local-function))
(labels ((my-local-function ()
...)) ; <- my-local-function can be used here
; my-local-function can be used in the body :
(my-local-function))
LABELS
is used for recursive functions.
Here you are using FLET
, which won't work.
(defun my-mapping (lst x)
(flet ((my-fac (n)
(cond ((= n 1) x)
(t (* n (my-fac (1- n))))))) ; <- problem
(mapcar #'my-fac lst))) ; <- no problem
In above function the recursive call to MY-FAC
is not possible. FLET
does not allow self-recursive calls. You are calling a global function called MY-FAC
. You would need to have such a function, to make above code work. I would suspect that this is the case, defined in prior experiments.
To actually call the local recursive function replace FLET
with LABELS
:
CL-USER 16 > (defun my-mapping (lst x)
(labels ((my-fac (n)
(cond ((= n 1) x)
(t (* n (my-fac (1- n)))))))
(mapcar #'my-fac lst)))
MY-MAPPING
CL-USER 17 > (my-mapping '(1 2 3 4) 2)
(2 4 12 48)
Above shows that one gets the expected result.
The problematic code and the Lisp compiler
This is for example what the LispWorks compiler says:
CL-USER 18 > (defun my-mapping (lst x)
(flet ((my-fac (n)
(cond ((= n 1) 1)
(t (* n (my-fac (1- n)))))))
(mapcar #'my-fac lst)))
MY-MAPPING
CL-USER 19 > (compile *)
;;;*** Warning in MY-MAPPING: X is bound but not referenced
The following function is undefined:
MY-FAC which is referenced by MY-MAPPING
SBCL says:
; in: DEFUN MY-MAPPING
; (SB-INT:NAMED-LAMBDA MY-MAPPING
; (LST X)
; (BLOCK MY-MAPPING
; (FLET ((MY-FAC #
; #))
; (MAPCAR #'MY-FAC LST))))
;
; caught STYLE-WARNING:
; The variable X is defined but never used.
; in: DEFUN MY-MAPPING
; (MY-FAC (1- N))
;
; caught STYLE-WARNING:
; undefined function: COMMON-LISP-USER::MY-FAC
;
; compilation unit finished
; Undefined function:
; MY-FAC
; caught 2 STYLE-WARNING conditions