Post #108,654
7/7/03 11:28:36 PM
|
Thought you had missed those :-)
As for when you will think like that, perhaps after reading On Lisp, and working your way into The Structure and Interpretation of Computer Programming. And then some good conversations with people who already program like that...
At least that was my strategy. :-)
Cheers, Ben
"good ideas and bad code build communities, the other three combinations do not" - [link|http://archives.real-time.com/pipermail/cocoon-devel/2000-October/003023.html|Stefano Mazzocchi]
|
Post #108,705
7/8/03 1:12:10 PM
|
Re: Thought you had missed those :-)
I've ordered The Structure and Interpretation of Computer Programming yesterday :) BTW, the first example cam probably be done with straight recursion (but not with eval). If I get it right, the advantage of closures in that case is that you "prebuild" recursion stack, so the actual execution of recursion goes faster (you don't have to parse parameters for every call). \n\nproc multilevel_for {varname all_ranges code} {\n multilevel_for_implementor $varname $code {} $all_ranges\n}\n\nproc multilevel_for_implementor {varname code proc_args all_ranges} {\n if {[llength $all_ranges] == 0} { \n set $varname $proc_args\n unset varname proc_args all_ranges\n eval $code\n return\n }\n set range [lindex $all_ranges 0]\n set from [lindex $range 0]\n set to [lindex $range 1]\n for {set x $from} {$x <= $to} {incr x} {\n multilevel_for_implementor $varname $code [concat $proc_args $x] [lrange $all_ranges 1 end]\n }\n}\n\nmultilevel_for combination { {1 10} {3 4} {10 20} } { puts $combination }\n\n\n
--
Less Is More. In my book, About Face, I introduce over 50 powerful design axioms. This is one of them.
--Alan Cooper. The Inmates Are Running the Asylum
|
Post #108,715
7/8/03 2:03:05 PM
|
That mostly works
The one big mistake is that you assume that the ranges passed in are always ranges. I only did that in my example because it is easy to generate ranges, but the combination code doesn't care what kinds of arrays you want.
But the idea does accomplish the job.
However an advantage of closures is that they don't run into problems if, say, the function that you are calling happens to name a variable using one of your internal variable names (eg varname, proc_args, or all_ranges). Another advantage is that you don't have the same number of parsing/encoding steps to confuse you. Furthermore the code passed into eval can be a closure itself - and may have behaviour which has already been parametrized in other ways.
Still these technical advantages of closures notwithstanding, the basic notion depends on code manipulating programmable behaviour. And any method of doing that, including eval, can make it work.
Incidentally I suspect that Ruby's version of eval can replace (inefficiently) any possible use of closures. The reason being that you can capture a context, and then by passing that context to eval with the code, you can eval the code in the other context. In other words the one thing that closures fundamentally buy you (ability to access encapsulated lexical values in some other part of the code) can be done within eval. I haven't seen this capability in other languages. (Not that I have looked very hard.)
Cheers, Ben
PS It took me a bit to figure out which language you were using... (Well, OK. It was my first guess, it just took me a bit to find my TCL interpreter and verify the guess. And a few others in gaim were just flailing.)
"good ideas and bad code build communities, the other three combinations do not" - [link|http://archives.real-time.com/pipermail/cocoon-devel/2000-October/003023.html|Stefano Mazzocchi]
|
Post #108,720
7/8/03 3:40:28 PM
|
Re: That mostly works
>>>>>>>>>> The one big mistake is that you assume that the ranges passed in are always ranges. I only did that in my example because it is easy to generate ranges, but the combination code doesn't care what kinds of arrays you want. <<<<<<<<<<<
I thought about it, but TCL has no construct equivalent to Perl's range array. And, as you know already, I hate Perl :)
I could do some special case processig to accomodate ranges and lists, something like
multilevel_for combination { {1-10} {3-4} {10 20 30} } { puts $combination }
and have a regexp to notice '-' in the middle of single-element list's only element. Very TCLish way to do things, and also very ugly, because now you need an escape for the lists that have one element ... and so on.
>>>>>>>>>>>>>>>> However an advantage of closures is that they don't run into problems if, say, the function that you are calling happens to name a variable using one of your internal variable names (eg varname, proc_args, or all_ranges). Another advantage is that you don't have the same number of parsing/encoding steps to confuse you. Furthermore the code passed into eval can be a closure itself - and may have behaviour which has already been parametrized in other ways. <<<<<<<<<<<<<<<<<
I know, I know. The only variable that is left to pollute the namespace is $code. I could work around it by using TCL's namespaces, but that's to much for a proof of concept.
As to parameterizing the closure, it can be acomplished to some small extent via exceedingly ugly use of concat command. I don't want to write it, and you don't want to see it :) .
Hey, I've never said it will be pretty :)
--
Less Is More. In my book, About Face, I introduce over 50 powerful design axioms. This is one of them.
--Alan Cooper. The Inmates Are Running the Asylum
|
Post #108,722
7/8/03 3:59:45 PM
|
Think we are even then
I thought about it, but TCL has no construct equivalent to Perl's range array. And, as you know already, I hate Perl :)
And my summary of TCL is that yes, you can base a language entirely around quoting and parsing strings, and it is a bad idea.
My solution wouldn't be very TCLish though. I would just write a function to produce ranges, and then I would pre-process arguments rather than post-process them in each function that wants to support such a basic notion. Of course the fact that this approach is not very TCLish speaks volumes for why I don't like the language...
Cheers, Ben
"good ideas and bad code build communities, the other three combinations do not" - [link|http://archives.real-time.com/pipermail/cocoon-devel/2000-October/003023.html|Stefano Mazzocchi]
|
Post #108,820
7/9/03 10:08:08 AM
|
A function to produce list from range
would not work very well for huge ranges. No, you need real iterators for that. Too bad for TCL.
--
Less Is More. In my book, About Face, I introduce over 50 powerful design axioms. This is one of them.
--Alan Cooper. The Inmates Are Running the Asylum
|
Post #108,847
7/9/03 12:51:26 PM
|
So?
In the kinds of problem spaces that scripting languages are usually used for, wasting memory like that is just fine. When you get to a problem where the simple wasteful approach doesn't work, then you start to worry about it.
Though I admit that the better scripting languages have been moving for a while towards making it possible to replace basic data structures with real iterators. (With varying levels of ease.)
Cheers, Ben
"good ideas and bad code build communities, the other three combinations do not" - [link|http://archives.real-time.com/pipermail/cocoon-devel/2000-October/003023.html|Stefano Mazzocchi]
|
Post #109,006
7/10/03 11:34:37 AM
|
Fake Closures
Ben: Incidentally I suspect that Ruby's version of eval can replace (inefficiently) any possible use of closures. The reason being that you can capture a context, and then by passing that context to eval with the code, you can eval the code in the other context. [...]Since a closure is just code + context, you can do as you describe. Its a little tricky passing parameters since the parameter you wish is pass is generally not in the scope where the string is evaluated. I used a global stack ... global to handle the cross scope parameter passing and a stack to handle nested closure calls. Here's a Ruby class implementing fake Closures ... $args = Array.new\nclass FakeClosure\n def initialize(str, bind)\n @str = str\n @bind = bind\n end\n def call(*args)\n $args.push(args)\n eval @str, @bind\n ensure\n $args.pop\n end\nend Ben: [...] I haven't seen this capability in other languages. [...]TCL has it with its uplevel command. Old (pre-closure) versions of Lisp had something called "funargs" that represented scope and could be passed to eval (I don't know if modern Lisps still support funargs).
-- -- Jim Weirich jweirich@one.net [link|http://onestepback.org|http://onestepback.org] --------------------------------------------------------------------- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)
|
Post #109,018
7/10/03 12:35:26 PM
|
Wow
first geniunely new TCL command in a long while
I missed it completely. Thank you.
Together with "info level" it's almost perfect.
--
Less Is More. In my book, About Face, I introduce over 50 powerful design axioms. This is one of them.
--Alan Cooper. The Inmates Are Running the Asylum
|