When using emacs or my android app I run
(defun big (num) (setf num2 5)(little num)))
(defun little (num)(+ num2 num))
Little
happily accepts num2 but when I run it in my SBCL repl (with sublimetext3) it does not.
Is this correct?
What is a workaround without creating a global variable for num2?
I could just pass a second argument (little num num2)
But this wont work when I am trying to mapcar little
over a list. Because I can only have one argument when mapcaring correct?
Please read §6. Variables from Practical Common Lisp.
Unlike Emacs Lisp, Common Lisp relies on lexical scope by default (Emacs Lisp is dynamic by default). Dynamic scope (i.e. indefinite scope and dynamic extent) is provided by declaring variables special, and by convention, they are written with asterisks around their names (named "earmuffs"), like *standard-output*
. You use defparameter
or defvar
to declare those variables. Since it has a global effect, you should never use them from inside functions; likewise, your usage of setf
is not defined in Common Lisp: no variable named num2
was declared previously in the scope; besides, even if it did, using a global/special variable for local variable is bad style.
With special variables, you can for example locally rebind the standard output: the new value is only visible while the code is inside the body of the let
binding:
(let ((*standard-output* *error-output*))
(print "Stream redirection"))
By default, print
writes to the stream bound to *standard-output*
; here, the stream is locally bound to the one given by *error-output*
. As soon as you escape the let
, *standard-output*
reverts to its previous value (imagine there is a stack).
With lexical scope, your code can only access the bindings that are visible in the text surrounding your code (and the global scope), and the extent is indefinite: it is possible to access a binding (sometimes indirectly) even after the code returns from the let
:
(let ((closure
(let ((count 0))
(lambda () (print (incf count))))))
(funcall closure)
(funcall closure))
;; prints:
;; 1
;; 2
The lambda
expression creates a closure, which captures the variable named count
. Every time you call it, it will increase the count
variable and print it. If you evaluate the same code another time, you define another closure and create another variable, with the same name.
Because I can only have one argument when mapcaring correct?
Not exactly; the function called by mapcar
should be able to accept at least as many elements as the number of lists that are given to it (and it should also not require more mandatory arguments):
(mapcar (lambda (x y) (* x y))
'(1 2 3)
'(0 3 6))
=> (0 6 18)
(mapcar #'list '(1 2) '(a b) '(+ /))
=> ((1 a +) (2 b /))
The function can also be a closure, and can use special variables.
(defun adder (x)
(lambda (y) (+ x y)))
(mapcar (adder 10) '(0 1 2))
=> (10 11 12)
The adder
functions takes a number x
and returns a closure which accepts a number y
and returns (+ x y)
.
If you prefer dynamic scope, use earmuffs and give it a meaningful name:
(defparameter *default-offset* 0)
... and define:
(defun offset (x)
(+ x *default-offset*))
You can then mapcar
too:
(let ((*default-offset* 20))
(mapcar #'offset '(1 2 3)))
=> (21 22 23)
As said by jkiiski in comments, you can also declare special variables with (declare (special ...))
where you usually put declarations (when entering a let
, a defun
, ...). You could also use the special operator progv
. This can be useful to have "invisible" variables that are only known by a set of functions to exchange information. You rarely need them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With