2016-02-05

So, I'm finally looking for software jobs again. As part of this I've been studying Skiena's algorithms book, figuring fundamentals were more important than details of some language. OTOH, between intensive study, the stress of a phone coding interview, and lack of sleep, the last few days I've decided to do something that doesn't take thought, like learn Python.

Not totally from scratch, I'd dabbled in it a bit years before, but never used it much. Work at the time called for C++ or Perl, which I'd learned some years before Python 2.0 came out. (Python's early roots go back to the 1980s, wow.) I knew it had some nice bits and annoying bits -- my first program had a 20 minute bug which either Perl or C would have caught in compilation. And in grad school I investigated a whole bunch of languages, concluding they all sucked in some way. I never did go down the path of making my own, though.

Anyway, I've been realizing that Python seems to have largely eclipsed Perl these days, and if I want a modern scripting language, which I do, I should learn it more. Also I'd been doing my algorithms coding practice in C and Perl, and Perl's quirks were starting to annoy me. (Like having to shift function arguments, or use references in a list of objects, or the $@% of variables.

And Python does have neat stuff! I'm amazed that after all this time Perl still doesn't come with a nice interactive shell; that goes back to *Lisp*, not to mention BASIC. "Writable pseudocode" syntax is pretty sweet. In the libraries, I'm really attached to memoization (@functools.lru_cache), and the unittesting modules look cool too, especially doctest, letting you make tests out of your own comments in a sort of literate programming. List comprehensions are a neat thing to steal from Haskell, and generators look cool too.

But as expected, there are annoyances too. One of them I can see but am not running into, due to my doing pure coding. If you were doing lots of little 'shell' or text munging programs, I think you'd find Perl was a lot nicer, due to it having OS interaction and regular expressions right in the language. On the flip side, if you were doing functional stuff for some reason, I'm pretty sure perl has reduce as a builtin, while Python has map and filter, but reduce was exiled to a library.

More substantial to me, though also relevant to the first use case: string interpolation is a mess. And Python can't even invoke it's Zen of "there's one way to do it", because there are many ways to do it, they just suck in concision and locality compared to "$name". I suspect a lot of "we can't be like Perl!" brand identity. I get the impression Python has surrendered as of 3.6, but it's not really out yet.

Another big one is the lack of "use strict vars" equivalent. Python *does* prevent you from referencing or modifying a variable out of the blue, you can't say "a += 4" as your first use of 'a', but you can assign to any name, with nothing like "use strict" and "my $a" to prevent typos. The advice seems to be "use pylint or other code checkers", to which I'd say "tools like that should be in the frigging compiler".

More minor ones include the lack of named break and continue, or of complex lambdas. Not that I've needed them yet. Also, my idea of a good educational language includes 'goto'; yeah, you don't want to use it much, but it's good prep for understanding JMP at a lower level. But I first learned programming on GW-BASIC, where between line numbers and GOTO 10, it really was like a high level assembly language in a way, and assembly itself looked quite familiar in concept.

Also, the tutorial seems to have buried the input() function in the section on 'if'. Seems poor pedagogy.

I can't help comparing dynamic scripting languages to Lisp and Scheme, so the clunkiness of complex or fractional numbers has me grumble a bit too, though I grant there's a good reason it's easier to have '3/4' as an atom in Lisp (because actual division would be (/ 3 4), causing a different set of grumpiness.)

I sort of miss the "statement if guard" syntax of Perl, but not enough to grouse much. OTOH, to my surprise Python allows a sort of ternary operator: "small = x if x < y else y".

***

Bouncing back to the algorithms level, Skiena's chapter on dynamic programming took me a few passes to grok, I think largely because the example problems of minimum edit distance and such were so new to me. DP itself became clearer when I linked it to memoization from my functional programming days, and searching revealed that yep, they're closely related. A sensible sequence would be:

1: naive recursive form
2: memoized version of the above for speed
3: transformation to a dynamic iterative tabulation if you really need it

But I had to search because despite being praised in "Toward A Better way to Teach Dynamic Programming" (PDF) as the best least bad text on DP, he doesn't mention it all, not even as something to research on your own. Skiena jumps directly from 1 to 3.

To be fair, when I've practiced memoizing C functions, I've found it can be kind of a pain. Making a resizable cache that's accessible by recursive calls of a function is tricky, with static/local variable guards. Making it re-entrant for a library function, I haven't even thought about... So the code complexity savings are less dramatic, if not lost entirely. But still, it should be worth at least a mention in chapter notes.

By contrast, in Scheme or Python you can make a function that memoizes any other function with hashable arguments. You write your recursive function f, then you say 'memoize f', and voila, huge speedup. SO COOL. I suspect you could do it in C++ too, though I fear it'd be ugly.

***

Also, holy fuck, have one extra < in your post and Dreamwidth goes berserk.

See the DW comments at http://mindstalk.dreamwidth.org/438324.html#comments

Show more