Prev: Tracing recursive functions in slime REPL (Allegro CL)
Next: help save comp.lang.lisp from spam
From: Alberto Riva on 23 Apr 2010 09:06 Johan Ur Riise wrote: > Alberto Riva <ariva(a)nospam.ufl.edu> writes: > >> Johan Ur Riise wrote: >>> Another reason to hate loop: >>> >>> (defun closures-from-loop (list) >>> (loop for el in list collect (lambda () (format t "~s~%" el)))) >>> >>> (loop for lambda in (closures-from-loop (list 1 2 3 'a 'b 'c)) >>> do (funcall lambda)) >>> >>> prints, since heavy setq-ing is going on behind the scene curtain: >>> >>> C >>> C >>> C >>> C >>> C >>> C >> I don't see why you were expecting anything different. There's a >> single 'el' variable, and all closures are capturing it. > I didn't notice that fact, that's why I didn't expect it >> Try doing >> this: >> >> (let ((a 1)) >> (list (lambda () (print a)) >> (incf a) >> (lambda () (print a)))) >> >> and then funcall the first and third elements of the resulting list: >> it will print 2 in both cases. It doesn't have to do with LOOP. >> >>> (defun closures-from-mapcar (list) >>> (mapcar (lambda (el) (lambda () (format t "~s~%" el))) list)) >>> >>> (mapcar #'funcall (closures-from-mapcar (list 1 2 3 'a 'b 'c))) >> In this case, the "(lambda (el) ..." makes it clear that you're >> introducing a new local variable every time. >> >>> prints: >>> >>> 1 >>> 2 >>> 3 >>> A >>> B >>> C >>> (NIL NIL NIL NIL NIL NIL) >>> >>> >>> Of course, when you find the bug >> Where did you see a bug? >> >>> you can also say: >>> >>> (defun closures-from-loop (list) >>> (let (result) >>> (loop for el in list do (let ((el el)) (push >>> (lambda () (format t "~s~%" el)) result))) >>> (reverse result))) >> Which is exactly what your MAPCAR example does: create a new local >> variable every time. You could have obtained the same result much more >> simply this way: >> >> (defun closures-from-loop (list) >> (loop >> for el in list >> collect (let ((el2 el)) (lambda () (print el2))))) > Better than the result/let/push/reverse solution, thanks, but also > collect (let ((el el)) (lambda () (print el))) > is nice For what definition of 'nice'? I think it is just unnecessarily confusing, it makes it even harder to understand when you're referring to the loop variable and when to the captured one. Actually, I would make the distinction even more explicit: (let ((saved-el el)) (lambda () (print saved-el))) Alberto
From: Captain Obvious on 23 Apr 2010 09:17 AR> For what definition of 'nice'? I think it is just unnecessarily AR> confusing, I think it is not. (let ((x x)) ...) creates new binding with same name and value, and this makes intention clear -- you just want a binding. I think it is an idiom, and people who know it will instantly recognize it, but it's not that hard to understand if you don't know what is going on. Btw, there is a macro in arnesi library called rebind: (macroexpand-1 '(arnesi:rebind (x) x)) (LET ((X X)) X) AR> it makes it even harder to understand when you're referring to the loop AR> variable and when to the captured one. Apparently, inside the let you refer to captured one. It isn't hard. AR> Actually, I would make the distinction even more explicit: AR> (let ((saved-el el)) AR> (lambda () (print saved-el))) This is unnecessarily verbose and it might be more confusing. People who are not aware about problems with loop bindings can think that it is unnecessary code and throw additional binding away. While (let ((x x)) ...) suggests that it is there for reason. (rebind (x) ....) is even more so.
From: o.jasper on 23 Apr 2010 11:33 Looks like an abstraction leak to me.. Abstraction leaks are bad.
From: Captain Obvious on 23 Apr 2010 11:46 oj> Looks like an abstraction leak to me.. Abstraction leaks are bad. Yep, it sucks, but it isn't that hard to deal with it.
From: Barry Margolin on 23 Apr 2010 12:31 In article <9a1dc309-b1c7-40f9-b0c5-9e23ceca0955(a)i40g2000yqd.googlegroups.com>, "o.jasper(a)gmail.com" <o.jasper(a)gmail.com> wrote: > Looks like an abstraction leak to me.. Abstraction leaks are bad. It's an abstraction leak either way. Either we require new bindings on each iteration or require a single binding with assignment on each iteration. Or we could leave it unspecified. This no longer leaks the abstraction, but you'd still have to use the idiom because you have to assume the worst. -- Barry Margolin, barmar(a)alum.mit.edu Arlington, MA *** PLEASE post questions in newsgroups, not directly to me *** *** PLEASE don't copy me on replies, I'll read them in the group ***
|
Pages: 1 Prev: Tracing recursive functions in slime REPL (Allegro CL) Next: help save comp.lang.lisp from spam |