The Little Schemer, by Daniel Friedman & Mathias Felleisen
Friedman and Felleisen guide us through a pleasant (and probably Socratic) presentation of the Scheme programming language, recursion and thinking recursively, continuations, the Y combinator (which is built step by step in a mind bending way). You end up building a simple Scheme interpreter, too. Regardless of its utility, the book is invaluable by the bare fact that it makes us think differently.
A Philosophy of Software Design, by John Ousterhout
I have never read beautifully written code. Never. At university, my professor who lectured about Software Engineering probably has never designed any complex system in his life. Most of the code I have faced in my life was written by practical programming (as defined by the author). John advocates the tactical programming as a way to create better software design. He spends a whole chapter to discuss how to name variables; then, several other chapters about how to properly comment code. The reading is really pleasant and presents the experience of the author who has some great background such as the creation of the Tcl programming language which is widely used in EDA software.