Programming Without Variables
drdobbs.com
drdobbs.com
collatz_vec :: Int -> [Int]
collatz_vec = reverse . go []
where
go acc 1 = acc
go acc n = go (n:acc) (if even n
then div n 2
else 3 * n + 1)
You simply push new values to the front of the list rather than the end, and then reverse the list all in one go at the end. Sure, you could use a fancier data type like an IntMap, but why bother when you already have linear complexity with a simple one?Edit: In fact, here's an even simpler solution that doesn't need to reverse the final result
collatz_vec :: Int -> [Int]
collatz_vec = takeWhile (/= 1) . iterate step
where
step 1 = 1
step n = if even n then div n 2 else 3 * n + 1
It's not tail recursive (because it uses `iterate`) but it's lazy -- so if the elements of the list are consumed as they are created, the whole computation can be done in constant space as well as being fast.Maybe his point was that you need to use a linked list rather than an array to get an efficient solution? Coming from a Scheme/ML/Haskell background I tend to view linked lists as much simpler than arrays!
> In effect, the whole idea of an array is built on being able to change part of it, and if we intend to avoid changing memory once we have put values into it, we need another kind of data structure entirely. Next week, we'll look at what such a data structure might be.
I'm assuming that a linked list is precisely the data structure he'll introduce next week. ;)
collatz :: Int -> [Int]
collatz x = go id x []
where
go :: ([Int] -> [Int]) -> Int -> [Int] -> [Int]
go f 1 = f . (1:)
go f n =
if even n
then go (f . (n:)) (n `div` 2)
else go (f . (n:)) (3 * n + 1)Linked lists have performance problems on computers. You need contiguous memory arrays for performance, since Direct Memory Access engines, caches, and pipelining all achieve peak performance only when memory is strictly accessed in contiguous chunks. How do functional language implementations deal with that problem?
Also, the garbage collector will often move lists into contiguous region(s) of memory.
More to the point, though, sometimes a list is the appropriate data structure for a problem (like returning all intermediate results from the Collatz call, where you don't know how many there will be). Sure, you could use an array and double its size every now and then, but you're doing an awful lot of violence to your program just to handle something the runtime could take care of for you :)
And yes, I know there will be a slight performance penalty under many circumstances.
also Land of Lisp and Realm of Racket (the sequel will be Solar System of Macros and Special forms
When it comes down to it, however, programming languages are neither for "simple expression of ideas" nor "execution by computers". They are for humans to write, read, and reason about the actions of computers. The biggest challenge most programmers face is not "how do I express this in an elegant, pure way that shows how smart I am?" but "how the heck does this giant existing code base work exactly?" and "how can I modify it in such a way that does not make it harder for other developers?"
A key goal of a language should be ease of reasoning. The conjecture that removing variable does this for most people is wholly unproven.
(8 . (4 . (2 . (1 . NIL))))
(There's a rage face to accompany this answer, the one for an overexcited noob who knew an answer, but rage faces don't belong on HN.)(I'm unclear whether the array version should include the 1, since it is not "counted").
That said, I really don't see the point of making programming look more like math... the deep distaste most people have for the abstract symbol manipulations therein, and the clear favoring of instances and examples, seems to pretty effectively point towards the opposite direction.
: collatz ( n -- length )
{ { [ dup 1 = ] [ drop 0 ] }
{ [ dup even? ] [ 2 / collatz 1 + ] }
[ 3 * 1 + collatz 1 + ] }
cond ;
Usage: 27 collatzI love when Gerald Jay Sussman explains the difference between recursion and iteration (http://ocw.mit.edu/courses/electrical-engineering-and-comput...), but I really feel this is history of computer science, not hot news.
The fact that he's doing this in a non-lisp makes it more accessible to some, and just interesting to others. I think that's OK to have on the HN front page, even if it's not something you personally find interesting.
Try considering that HN is a resource to more than just you or just me. Consider that some programmers here might be more or less experienced than us, and that it's possible this might be the window into "oh, hey, recursion!" that some new programmer needed in order to discover a new avenue of learning.
In that light, this is a pretty worthy article on HN. I especially like that he goes into implementation detail, rather than just saying, "Duh, use tail recursion", and thus ends up explaining how to actually do it well (in case a reader didn't know).
Well, that's fine. It was written for folks who had never thought about the relationship between iteration and recursion in this way and is clearly angling towards introducing something like a point-free style. Perhaps there are more people like than on HN than you thought.
Both your original comment and this one are prototypical examples of a middlebrow dismissal. You're not actually saying anything about the article, but you're telling us a lot about how good you think your taste is.
In addition, the dismissive tone of your comment, based on you seeing a similar feature years ago in an obscure language rubs people the wrong way. Are we never supposed to discuss coding techniques or algorithms simply because they were once discussed elsewhere years ago?
Enjoy HN and contribute to the dialog; try not to attack other people's contributions.