I'd suggest learning C as soon as a basic grasp of programming (variables, condition, iteration, basic data structures and abstract data types) is there. C is the lingua-franca of programming: advanced algorithms are often implemented in C (or in C like languages), other language concepts are often expressed in terms of C language concepts. "Gold standards" of systems programming are the C POSIX API and BSD sockets (best books on network programming use C for all of their examples). Learning UNIX and the GNU tool chain is also important.
Learning a non-imperative languages is also vital: most importantly Lisp such as Scheme and a strictly and statically typed functional language with type inference like Haskell or ML.
As for Ruby vs. Python, it's an implementation detail. The languages are close enough as if you know one, you can learn the other when you need to (at a new job, or when there's an open source project you'd like to contribute to). Make sure to be fluent in one, however, as "scripting" is a vital skill (especially if you have to deal with a production environment or build/release tooling).
Same principle as Ruby vs. Python also goes for Java vs. C#. I would, however, suggest learning C++ and/or (one of) Java/C#, as they're rather widely used in industry (and not just for legacy work).
Important thing is understanding the fundamentals and seeing the overall patterns.
Important thing is understanding the fundamentals
and seeing the overall patterns.
And the most damning for C and Java in my opinion. C and Java's syntax (which does matter to those who are learning), is so verbose as to hide a lot of what the fundamentals and patterns are.
This is why I chose Ruby and Python. The languages syntaxes are clean and are never in the way, allowing new programmers an easier time understanding those fundamentals.
By "fundamentals," strlen means "memory management," "pointers," the gritty stuff. You can't escape memory management by using Ruby and Python, and a rudimentary understanding of pointers are also essential.
a = ["Hello", "World"]
b = a
b[0] = "Goodbye"
print a
The first time I tried this, I was baffled why ["Goodbye", "World"] showed up. The answer lied in the way this scripting language used pointers, and it wasn't until I learned C that I really understood that.
As for memory management, I often accidentally write scripts that use gobs of memory thanks to silly mistakes; e.g. not using weak references when I should (oh look, more pointers!), creating new objects instead of reusing old ones , forgetting to unregister event handlers in my nodejs apps, etc. C's "every malloc() must be free()d" policy teaches these things to you in a very explicit way. Sometimes you have to clean things up when your scripting runtime doesn't know it should.
Even after using python for many years, I still occasionally make this mistake. It is a tough "bug" to track down. Can anyone comment as to why deep-copy is not the norm?
def f(b):
b[0] = 'Goodbye'
a = ['Hello', 'world']
f(a)
It's more efficient to pass function arguments by reference, and it would be fairly baffling if function argument passing did not work like assignment (c.f. C++ copy construction being similar to, but slightly distinct from, assignment).
Less philosophically, everything in Python has pass-by-reference semantics, even ints. The things you might think are passed by value are immutable, so it doesn't really matter whether they are passed by value or by reference. For example:
a = 1
a = a + 1
conceptually creates a new integer object and binds the name a to it, and so does
Simply because it is slow. In the GP's example if a was a much bigger array, copying it over to b would be expensive. On a related note, in C++ for many containers (if not all) in the standard library, copying is the norm; in "The C++ Programming Language" Bjarne Stroustrup warns that it may be slow.
C's syntax is different from Python's for a reason: what each operator does is very much transparent from the syntax. The point of learning C is to understand what the computer does (as you program it in a higher level language).
In Python, I can say:
...
if "foo" in bar:
do_something()
That's excellent, that's what I expect from a high level language. However, I am not forced to think about whether bar is a hash table (dictionary) an array, or something else. That, again, is excellent for the kind programming you do with Python: you're programming to an interface and not an implementation. Java's standard library also encourages that.
Collection<String> bar = getBar();
...
if(bar.contains("foo")) {
However, you also need to understand how the various containers implement "__contains__" or ".contains()". That is, you need to understand algorithms, pointers (in Python and Java, every value is a pointer and memory leaks are still possible!), memory management. C is a better language for learning these fundamentals than Python or Java.
Of course, Python and Java (and also, Smalltalk and Lisp/CLOS) are much better languages for learning object oriented design and "design patterns" in the classical sense. However, I've found that I never quite "got" OO until I had already had a job as a software engineer. I would have never gotten a job as a software engineer without knowing the fundamentals. I also feel that having grown up on C has been an advantage in terms of being able to reason about systems aspects of my work, even though professionally I mostly worked in C++ (with STL), Perl/Python and Java.
C's syntax as being applied to high level languages is a different matter. I agree it doesn't make much sense for Java: for example, lack of any type inference is really annoying. Perhaps that's why I find programming in Scala to be more pleasant, even when I am writing imperative code. I agree, it can be annoying to use a syntax meant for a low-level language to write in a strongly typed, memory-safe high level language. However, the syntax is not the most important part of a language, especially for pedagogic purposes.
At what academic level are these students? Every university^ I know of teaches incoming students in a language with C style syntax and they handle it just fine.
Carnegie Mellon's intro CS course has been in python for a couple of years, and we're switching to ruby for the coming year.
Of course, that's followed by (Our own version of! =/ ) C, and then SML.
Question for those who know more than I: what are the tradeoffs of first learning one language in depth, then branching out versus learning several languages(/styles) early on?
Using python for a completely introductory course for students that had no prior exposure to programming is one thing. However, it must almost _immediately_ be followed by C (and later by Lisp, ML/Haskell) rather than be used as primary language of instruction.
No offense, but I actually picked Ruby because everyone just seemed so damned excited all the time, gushing over the latest Rails release or coming up with weird names for gems, and when you're knee-deep in bug-infested legacy code or banging your head against the keyboard wondering why you're the only hacker in the world that doesn't get it, that's actually very important.
Learning a non-imperative languages is also vital: most importantly Lisp such as Scheme and a strictly and statically typed functional language with type inference like Haskell or ML.
As for Ruby vs. Python, it's an implementation detail. The languages are close enough as if you know one, you can learn the other when you need to (at a new job, or when there's an open source project you'd like to contribute to). Make sure to be fluent in one, however, as "scripting" is a vital skill (especially if you have to deal with a production environment or build/release tooling).
Same principle as Ruby vs. Python also goes for Java vs. C#. I would, however, suggest learning C++ and/or (one of) Java/C#, as they're rather widely used in industry (and not just for legacy work).
Important thing is understanding the fundamentals and seeing the overall patterns.