|
|
|
|
|
by ohyes
2498 days ago
|
|
Well, you have to make the go statement in lexical scope in order for the tagbody to work... that seems reasonable enough to me. You specifically stated: > Implement an O(1) jump table in Common Lisp that respects the lexical environment. You don't know the lexical environment until there's a lexical environment to know, you can't have your cake and also eat it (or not have your cake and also know it). Interestingly, try/catch also solves this problem fairly elegantly without distastefully consing up a list or array at run-time. (defun jump2 (i)
(let ((location nil))
(tagbody
(catch 'a
(catch 'b
(catch 'c
(catch 'd
(setf location (elt #(a b c d) i))
(throw location nil))
(when location
(print "D!")
(go end)))
(when location
(print "C!")
(go end)))
(when location
(print "B!")
(go end)))
(when location
(print "A!")
(go end))
end)))
I'll leave the relevant macro to the reader, it shouldn't be that difficult... (I'm hoping I didn't just do someone's homework).edit: removed some cruft from experiments and fixed formatting |
|
But that’s beside the point because in practice this is probably O(n) not O(1). I think most CL compilers are going to need to push things to the control stack for each catch. Even if the compiler is clever enough to eliminate the catches, it still mightn’t implement the computation of which catch catches as O(n). So even in the best case of a very smart compiler, your code is basically equivalent to:
Which isn’t guaranteed to be O(1).The point the GP is making is that the only solution which looks like it might O(1) (and indeed is O(1) if there is no (or just one) lexical scope to capture) is the array-of-lambdas-doing-go, but that this isn’t guaranteed by the language to be O(1) when the lexical scope changes each time because in a naive evaluation, the array of lambdas must be consed before jumping and this is O(n).
The reason that it is reasonable to complain about this is that what one needs for a jump table is weaker than this. The lambdas do capture the lexical scope but they only have dynamic extent and don’t extend the scope at all so a lambda of the form (lambda () (go foo)) only has to be consed each time you hit the jump table because you can only reference foo once you are inside the tagbody. However the code to actually jump is likely to be static for most compilers.
For guaranteed O(1) performance (which doesn’t rely on the cleverness of the compiler) you’d need to introduce a new special form, e.g. (jump n tag1 tag2 ... tagn) which evaluates n and jumps to the nth tag in its arguments in O(1) time.