Discussion in 'Technologics' started by Charles, Jan 5, 2012.
This is sadly not the case at many schools. It's very depressing.
Every school is different, yeah. At my school, the focus of the curriculum was producing programmers who could get jobs. Every class had programming assignments / group projects. Sometimes you were expected to figure out everything on your own, and other times the teachers would spend a lot of time on the coding aspects. Either way you had to produce working code, often.
Re: OS classes for learning threading -- that's horrible. Because they don't teach you threading, per se, but how to implement the low level primitives directly (as in, you're writing an OS). NO ONE will do that in the real world. You don't take some new grad and be all "Hey, can you write us a semaphore primitive for our production code?"
What's lacking is learning how to use threading to solve problems, but yeah, academia tends to trend about 10 years out of date. We've had multithreading desktop operating systems for 20 years, so why the rush?
Re: algorithm analysis -- I'm going to sound snooty here, sorry, but that's not the kind of thing you relearn. It's basic comprehension of how you write data structures and code. If you don't have an intuitive sense of N^2 vs. logN -- you're going to fuck up some code, in a big way, and not realize why.
For example, if you can't identify why this works but will eventually come to an effective halt when num_strings exceeds some threshold...you're in trouble:
void catstrings( char *buffer, size_t s, char const *strings, size_t num_strings )
buffer[ 0 ] = 0;
for ( size_t i = 0; i < num_strings; i++ )
strcat_s( buffer, s, strings[ i ] );
The value of a college education is that it teaches you things that you would almost never learn on your own because it's abstract and unintuitive until it's been taught, then it's vital. Sort of like math. Matrices are total bullshit until you have to write code that transforms vectors -- and I've known many programmers who did write that code doing it algebraically and not realizing they were doing a set of dot products that could be formalized into some that could be concatenated cleanly.
Point being that for most programmers, if they don't get exposed to a data structures or algorithms class, every sort ends up looking like bubble sort and ever search is linear and they don't know that it's even bad.
I think I've got the intuition to know when a solution seems brutish or janky. My issue is more one of needing to research better ways to do things when I start to get the "something isn't right" feeling, as opposed to just doing it right the first time. I hope that's just an experience thing.
Although if you asked me for two examples of O(log n) algorithms right now I'd say "binary search" and then stare blankly at you. It's that kind of thing I worry about.
Can you tell me what's wrong with this function?
string catstrings(List<string> strings)
string result = "";
for (var str in strings)
result += str;
It's making a new string (and tearing down the old) every time you concatenate like that. Which is a huge waste of resources -- O(n!) I think?
Well, sure, but that's how everything works. You don't implement hash tables in the real world -- but having done so in class means that you're more likely to understand how they work and how a bad hash key can break them. Implementing things you'll just consume later is pretty much a staple of programming education.
Yes, there are obvious cases, and there's some stuff that's bone-deep. But there are also cases that really do take some analysis that I at least would have to relearn. In production code, you can just use a few rules of thumb (sorting is probably nlgn, binary search is lgn, etc.), count the loops, and call it a day, and that's plenty good enough. In even a basic CS class, you're trying to prove shit mathematically. I think you can look at something like this and think "oh man, I barely remember that, I'd have to read up on that" while still writing perfectly sound production code.
I am dead certain that right now, today, without doing any additional reading, I could not mathematically prove why quicksort has a worst-case of n^2. I could read the quicksort code, think about how it would work on various inputs, and explain to you why a particular pathological input could give you n^2 -- but I couldn't prove rigorously and mathematically that this is the worst case.
Sure, I don't dispute that, my point is that learning how something works isn't the same as learning how to use something properly.
I'm not talking about deep formal analysis, so I think we're in agreement here. Intuitively understanding the difference between N, N^2, and logN gets you 98% of the way there, and that's all I'm talking about.
Worst case analysis isn't that big a deal either. I'm not talking about mathematically proving worst case, but intuitively it should be obvious (after some education) that inserting a sorted list into a binary tree will give you a worst case search of O(N) vs. the ideal of O(logN).
Yeah, don't do that. I mean, it won't eventually fail like BTGs (at least not until you run out of memory), but C# strings are not really meant to be used for manipulation, use StringBuilder.
Note: to be precise about my earlier example, it won't actually crash (at least, if I typed in the code right), my point was that even with a large enough buffer, that it will eventually be so slow as to be useless.
Oh you're right, for some reason I thought there was an index involved, and that the index was going to quickly overflow. I think I was confusing it with something else I was working on at the time.
Oh, stumbled across an interesting programmer blog about physics and networking by one of the God of War Ascension guys:
The physics stuff was interesting for me.
Unity drives me nuts when it magically decides to not update my scripts after I make a change to them.
So I've been reading a bevy of programmer blogs, going to share this guy's two snafu's that caused him heap issues:
if (!strcmp("impactTextureName", LE_getCurrentToken()))
explosionTexture.path = calloc(strlen(LE_getCurrentToken()+1), sizeof(char));
event_t *event ;
event = calloc(1, sizeof(event));
event->type = EV_REQUEST_SCENE;
event->time = simulationTime + 5000;
Both are just simple typos, but its stuff that like that can cause crazy problems. I'm a little surprised that there were no warning/errors on the first one.
He also had a nasty one involving a char being considered as unsigned on the Android platform.
Both cases have the root cause going back to using an untyped memory allocation API. A stronger typed language addresses that, as would wrapping all those callocs with type appropriate allocators.
#define ALLOC_OBJECT(t) calloc( 1, sizeof(t) )
#define STRDUP(s) strcpy(calloc(strlen(s)+1),sizeof(char))
explosionTexture.path = STRDUP(LE_getCurrentToken());
event = ALLOC_OBJECT(event_t); // See note below 
You can't warn reliably since both use cases are totally valid.
 This has the downside that if you change the type of 'event', then you'll have a similar issue. This is fairly inescapable in a weakly typed language.
stdint.h is everyone's friend.
These lines are the problem-causers, right?
explosionTexture.path = calloc(strlen(LE_getCurrentToken()+1), sizeof(char));
// should be:
explosionTexture.path = calloc(strlen(LE_getCurrentToken())+1, sizeof(char));
event = calloc(1, sizeof(event));
// should be:
event = calloc(1, sizeof(event_t));
yeah, simple typo stuff, but caused some nasty corruption. He's got a fairly nifty blog with descriptions on how he tracked down the issue, and what exact tools he used. It's all pretty good stuff, he also has code analysis on most of the Id code that was freely released.
BTW, my earlier string concatenation performance issue is covered in some depth here:
It's just so amazing looking, they must be doing something edgy and deep in there somewhere that we just don't understand!
*goes back to unraveling a collection of shell scripts that in reality do and undo their own work fifteen times because nobody understood the script they were chaining off of*
Never use "sizeof(char)". sizeof(char) == 1, by definition.
James Johnson, what is more important is did the person actually pass the interview / get the job? A good resume and cover letter only go so far, and they may not have even looked at the code sample.
Is it on all platforms forever?
Personal opinion is that even if that's true, sizeof(char) is more readable than a hardcoded 1, and if someone goes in and decides to make all their strings wide_chars or whatever, they're more likely to spot sizeof(char) and change it.
#define PREFERRED_STRING_LENGTH_FUNCTION strlen
#define PREFERRED_MEMORY_ALLOCATION malloc
#define PREFERRED_CHARACTER_TYPE char
I liked BTG's better, it was a bit easier on the eyes. =)
Effectively, yes: http://stackoverflow.com/questions/2215445/are-there-machines-where-sizeofchar-1
calloc just blows, I don't know anyone that uses it. If you're going to be old school, just straight up call malloc with sizeof( object ) * num_objects, that's pretty obvious what it's doing at all times.
It may not be kosher, but on a TI F2812 DSP, sizeof(char) = sizeof(int) = 2. So many assumptions start falling apart when you hit embedded space, you can't even be assured the compilers work the way they should. It's gotten better over the years as the processors got better, more complex, and shifted away from crappy home-grown toolsets, but you've still got to be careful.
Hence, stdint.h is your friend, along with static_asserts to make sure that your assumptions check out.
For example, I preempted a bug because a few static_asserts told me that sizeof(bool) when running ARMv7 compilation was 1 byte, but was 4 bytes on ARMv5.
What's wrong with calloc? It initializes all the memory to 0 for you as well.
Objectively, nothing, except for whatever reason more people mess up its parameters than just doing the multiply explicitly.
Crazy town. Then again, probably not doing too much string processing on a DSP...
I was talking to a coworker about this (well, run time asserts, not static ones). asserts() are both the most widely abused construct out there, and also the most woefully underutilized. Carpet bombing code with assertions() on preconditions, postconditions, and invariants is always a good thing, but asserts are often used to validate data or exceptional situations that should be handled smoothly at run-time.
It's a pet programmer rage of mine. assert on actual bugs, smoothly handle (i.e. don't assert) understood but unexpected conditions, and never, ever try to have safe fallback code for situations you explicitly do not expect (e.g. null pointers passed to functions that do not expect null pointers).
And yeah, compile time asserts are always handy as well, albeit often implemented in goofy ways with cryptic error messages.
You can never count on that (or signedeness of char, or sizeof( enum ), etc.). I always set "treat bools as ints" to maintain uniformity.
She didn't get the job. I didn't mean to genuinely badmouth her -- she's working on my senior project with me and does a fine job. In fact I'd been rooting for her.
I guess I just used her as a generic example of freshly graduated CS students I know: they have a mentality of "if it works, who cares what it looks like?" That's a very foreign attitude to me.
Goddamnit why can't I find a tutorial to do this specific thing I want to do?
Maybe I just need to make the migraine go away, then try to get work done.
"exec yoursprocnamehere param1 param2"
The situation is a little more involved than that, and I probably shouldn't be posting about it anyway since it's related to work, rather than something I'm doing for fun. Hooray for second thoughts and migraine-fueled posting! Almost as good as ambien. Not really.
Yeah, if you're writing C, sizeof(char) == 1 always and forever. A char is one byte, by definition. Mind you, that byte might be some weird size--there's no rule against char being 36 bits or something. If the compiler for XPav's DSP really does make sizeof(char) == 2, then it's weird and broken and not C. sizeof(char) == sizeof(int) == 1 == 16 bits would be fine, though, which I bet is what it actually does.
Oh, I totally agree. I'll take a throw, caught by a top-level handler rather than a stupid assert(). I also use a macro to throw a set of standard exceptions that put __FILE__ and __LINE__ into the exception. Sure, I lose the callstack, but I also only really use these exceptions on things that should not happen, like "the serial port you told me to use isn't there".
I'm very happy that the "new" C++ compile time static_assert works properly with my gcc 4.4.2.
XNA is toast, not that this is really all that surprising, but I'm saddened by it nonetheless, as I liked XNA and thought it was pretty nifty.
Don't worry, some other badly-branded tech will be along shortly to get your hopes up for a couple years that Microsoft has any sort of coherent long term strategy.
FUUUUUUUCK YOUUUUUUU RVM
So uh, I have this problem. I need to use "bundler", which is an RVM gem or whatever the fuck.
When I try to execute it, it says it's not installed and I need to "gem install bundler". When I do, it installs it, but it doesn't actually download anything. Uninstalling it makes it download the files, but the problem persists. That's right, the problem persists through my reinstalling the gem!
There's some thread on stack overflow with a problem that appears similar, but the root cause, ferreted out on this git thread, doesn't appear to be the case in my VM. Specifically:
Does not include the doomful redirect in the linked threads.
However, uh, "echo $GEM_HOME" and "echo $GEM_PATH" both get me a newline. "echo $PATH" gets me "/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/home/aaron/.rvm/bin" and "which bundle" gets me "/home/aaron/.rvm/bin/bundle".
Is this the kind of problem I should just take to their support thing on git? Because I can't find fuckall ways to solve it and it's fucking infuriating.
I also ran the sequence of commands instructed by the github page and it fixed it... last time. But not this time, oh no! This time the problem fucking persists! And the time before that, the problem was solved by cding out of the directory and then back in!
Is this related to the massive security hack of the rubygems repository, possibly?
*runs off to google*
 Hmmm. I don't see why it would? It's not that I'm failing to retrieve the files. Unless the bundler gem was actually compromised and changed, that is.
Separate names with a comma.