Lisp - Tears of Joy Part 6
Lisp - Tears of Joy Part 6
Submit Tips
Search
HOME
REVIEWS
HOW-TOS
CODING
INTERVIEWS
FEATURES
OVERVIEW
BLOGS
SERIES
IT ADMIN
Search for:
Search
Get Connected
Lisp has been hailed as the worlds most powerful programming language but only the top percentile of programmers use it, because of its cryptic syntax and academic reputation. This is rather unfortunate, since Lisp isnt that hard to grasp. If you want to be among the crme de la crme, this series is for you. This is the sixth article in the series than began in the June 2011.
RSS Feed
Closures
Suppose you want to write a function that saves some value it can examine the next time it runs. For example, functions that generate random numbers often use the previously generated random number to help generate the next. To do this, you need to lay aside a value such that a function can get at it next time. Its a risky proposition if you use a global variable or a property list to store such a value. If you get careless, and write another function that uses the same global variable or property, the two functions may interact in a way you never intended. It is even more problematic if you wish to have several incarnations of the same function around, each remembering what it computed previously, but not interfering with one another. Using a single, global object does not accommodate this. For example, suppose you want a function that will give you the next even number each time it is called. You could write this as a function that uses the global variable *evenum* to remember the last value it computed:
>( d e f u ng e n e r a t e e v e n ( ) ( s e t q* e v e n u m *( +* e v e n u m *2 ) ) ) G E N E R A T E E V E N >( s e t q* e v e n u m *0 ) 0 >( g e n e r a t e e v e n ) 2 >( g e n e r a t e e v e n ) 4 >( g e n e r a t e e v e n ) 6
Follow
+2,497
This works fine, but some other function could come along and clobber e v e n u min between calls. And we could not use g e n e r a t e e v e nto generate two sequences of even numbers simultaneously, independent of the other. The solution to this problem is to make a version of a function that has variables only it can
access. This is done by taking a function that contains some free symbols, and producing a new function in which all those free symbols are given their own, unique variables (free symbols are symbols used to designate variables within a function, but which are not formal parameters of that function). This is as if we replaced each free symbol with a new symbol, one we were certain no other function could ever use. When this new function is run, then, its free symbols will reference variables that no other function can reference. If the values of some of these variables are changed, their new values will be retained until the next time the function is run. However, changes to these variables will not have any effect outside of this function; moreover, the values of these variables cannot be accessed or altered outside of this function. In this example, we would like to produce a version of g e n e r a t e e v e nthat has its own private copy of the free variable * e v e n u m * . When we create this version of g e n e r a t e e v e n , we would like its version of * e v e n u m *to be initialised at whatever value * e v e n u m *currently has. No matter what happens subsequently to the original version, this will not affect the new version. When we run this new version, it would update its private copy of * e v e n u m * . This would not affect the version of * e v e n u m *known to the original function, but the new, updated copy of * e v e n u m *would be available to the new function the next time it is run. In other words, we take a sort of snapshot of a function, with respect to the current status of its free symbols. We can then manipulate this picture, rather than the function itself. The picture has about the same logical structure as the original, but if we change something in the picture, the original does not change. In fact, we should be able to take any number of such snapshots, and manipulate each one a bit differently. The alterations to each snapshot would serve to record its current state of affairs. Each snapshot could be looked at and altered quite independently of the others. When we take such a snapshot of a function, it is called a closure of that function. The name is derived from the idea that variables denoted by the free symbols of that function, normally open to the world outside that function, are now closed to the outside world. In Common Lisp, closures of functions are created by supplying the function function with a lambda expression as argument. If the free symbols of that lambda expression happen to be parameters of the function within which the lambda expression is embedded, function will return a snapshot of the function denoted by the lambda expression. This snapshot will contain its own variables corresponding to each of the lambda expressions free symbols. For example, we can use function to write a version of g e n e r a t e e v e nthat will produce a closure that includes the free symbol * e v e n u m *in the picture:
>( d e f u ng e n e r a t e e v e n( * e v e n u m * ) ( f u n c t i o n ( l a m b d a ( ) ( s e t q* e v e n u m *( +* e v e n u m *2 ) ) ) ) ) G E N E R A T E E V E N >( s e t qg e n e v e n 1( g e n e r a t e e v e n0 ) ) # < F U N C T I O N: L A M B D AN I L( S E T Q* E V E N U M *( +* E V E N U M *2 ) ) > >( f u n c a l lg e n e v e n 1 ) 2 >( f u n c a l lg e n e v e n 1 ) 4 >( f u n c a l lg e n e v e n 1 ) 6
Popular
Comments
Tag cloud
When g e n e r a t e e v e nis called, Lisp creates a new variable corresponding to * e v e n u m * , as Lisp always produces new variables corresponding to the formal parameters of a function. Then the f u n c t i o nfunction returns a closure of the specified lambda expression. Since a new variable corresponding to * e v e n u m *exists at this time, the closure gets this version of * e v e n u m * as its own. When we exit this call to g e n e r a t e e v e n , no code can reference this variable that is closed off to the outside world. We save this closure by assigning it to the variable g e n e v e n 1 . Next, let us use f u n c a l lto invoke this function (f u n c a l lis like a p p l y , but expects the arguments right after the function name. In this case, there are none, as the lambda expression of g e n e r a t e e v e n , and hence, the closure produced from it, is a function of no arguments). Lisp prints out the closure as # < F U N C T I O N: L A M B D AN I L( S E T Q* E V E N U M *( +* E V E N U M * 2 ) ) > . Remember that the notation used to print closures is not a part of the Common Lisp standard. This is because it is not really meaningful to talk about printing a closure. Therefore, other
implementations of Common Lisp may use a different notation. We run this closure a couple of times, and each time it produces a new value. We can create as many independent closures of the same function as we like. For example, if we make another closure of g e n e r a t e e v e nright now
>( s e t qg e n e v e n 2( g e n e r a t e e v e n0 ) ) # < F U N C T I O N: L A M B D AN I L( S E T Q* E V E N U M *( +* E V E N U M *2 ) ) > >( f u n c a l lg e n e v e n 2 ) 2 >( f u n c a l lg e n e v e n 2 ) 4 >( f u n c a l lg e n e v e n 1 ) 8 >( f u n c a l lg e n e v e n 1 ) 1 0 >( f u n c a l lg e n e v e n 2 ) 6
This closure starts off with its version of * e v e n u m *at the value 0. Each closure has its own independent variable corresponding to the symbol * e v e n u m * . Therefore, a call to one function has no effect on the value of * e v e n u m *in the other.
However, if we want to make closures of these functions, we are in trouble. If we use closure to produce a closure of each function, each closure would get its own version of * s e e d * . The closure of g e n e r a t e e v e ncould not influence the closure of g e n e r a t e o d d , and the converse would also be true. But this is not what we want. The solution to this problem is to create closures of a number of functions in the same context. The functions closed together would share their variables with one another, but not with anyone else. For example, here is a function that creates a list of two closures, one of which generates even numbers and the other, odd:
>( d e f u ng e n e r a t e e v e n o d d( * s e e d * ) ( l i s t ( f u n c t i o n ( l a m b d a ( ) ( s e t q* s e e d *( c o n d( ( e v e n p* s e e d * )( +* s e e d *2 ) ) ( t( 1 +* s e e d * ) ) ) ) ) ) ( f u n c t i o n ( l a m b d a ( ) ( s e t q* s e e d *( c o n d( ( o d d p* s e e d * )( +* s e e d *2 ) ) ( t( 1 +* s e e d * ) ) ) ) ) ) ) ) G E N E R A T E E V E N O D D >( s e t qf n s( g e n e r a t e e v e n o d d0 ) ) ( # F U N C T I O N: L A M B D AN I L( S E T Q* S E E D *( C O N D( ( E V E N P* S E E D * )( +* S E E D *2 ) )( T( 1 +* S E E D * ) ) ) ) > ( # F U N C T I O N: L A M B D AN I L( S E T Q* S E E D *( C O N D( ( O D D P* S E E D * )( +* S E E D *2 ) )( T( 1 +* S E E D * ) ) ) ) > ) >( f u n c a l l( c a rf n s ) ) 2 >( f u n c a l l( c a rf n s ) ) 4 >( f u n c a l l( c a d rf n s ) )
5 >( f u n c a l l( c a d rf n s ) ) 7 >( f u n c a l l( c a rf n s ) ) 8
Some readers who arent actively using Lisp may have forgotten some of what we covered earlier in this series, so here is a brief explanation of the keywords used in the code above.
( c o n d( ( t e s te x p r e s s i o n * ) * ) ) Evaluates test expressions until one returns true. If that
test has no corresponding expressions, returns the value of the test. Otherwise, evaluates the expressions in order, returning the value(s) of the last. If no test returns true, returns nil.
c a r The primitive functions for extracting the elements of lists are c a rand c d r . The c a rof
a list is the first element, and the c d ris everything after the first element:
>( c a r` ( abc ) ) A >( c d r` ( abc ) ) ( BC )
of c d r . All the functions of the form c _ x _ r , where _ x _is a string of up to four a sor d s , are defined in Common Lisp. With the possible exception of c a d r , which refers to the second element, it is not a good idea to use them in code that anyone else is going to read.
( e v e n pi ) Returns true if iis even; (o d d pi ) returns true if i is odd.
The call to g e n e r a t e e v e n o d dproduces a pair of closures, one for each call to function. This pair shares access to an otherwise private copy of the variable * s e e d * . Subsequent calls to
g e n e r a t e e v e n o d dwould create additional pairs of such closures, each pair sharing a
Related Posts:
Lisp: Tears of Joy, Part 5 Lisp: Tears of Joy, Part 3 Joy of Programming: Scope, Lifetime and Visibility in C Lisp: Tears of Joy, Part 7 Loading Library Files in C++
Tags: Common Lisp, even numbers, free symbols, lambda expression, LFY November 2011, Lisp, Lisp: Tears of Joy series, Programming Language, random numbers, sequences, syntax, variables
Previous Post
Next Post
Reviews
How-Tos
Coding
Interviews
Features
Overview
Blogs
Search
Popular tags
Linux , ubuntu, Java, MySQL, Google, python, Fedora, Android, PHP, C, html, w eb applications , India, Microsoft, unix , Window s , Red Hat, Oracle, Security , Apache, xml, LFY April 2012, FOSS, GNOME, http, JavaScript, LFY June 2011, open source, RAM, operating systems
All published articles are released under Creative Commons Attribution-NonCommercial 3.0 Unported License, unless otherw ise noted.
LINUX For You is pow ered by WordPress, w hich gladly sits on top of a CentOS-based LEMP stack. .