|
ShoulderDaemon posted:<snip> If you have complex resource building that can fail at multiple points and requires multiple cleanup, it's probably not a good idea to try and house the whole drat thing in one scope. Here's that same logic broken into functions. It has some early returns but if that's an issue we all know ways to solve it, I'm just being lazy. code:
|
# ? Jul 27, 2009 20:07 |
|
|
# ? May 14, 2024 18:59 |
|
Bhaal posted:If you have complex resource building that can fail at multiple points and requires multiple cleanup, it's probably not a good idea to try and house the whole drat thing in one scope. Here's that same logic broken into functions. It has some early returns but if that's an issue we all know ways to solve it, I'm just being lazy. If stage 3 construction fails, how does stage 2 cleanup happen? I suppose code:
|
# ? Jul 27, 2009 22:52 |
|
Bhaal posted:If you have complex resource building that can fail at multiple points and requires multiple cleanup, it's probably not a good idea to try and house the whole drat thing in one scope. Here's that same logic broken into functions. It has some early returns but if that's an issue we all know ways to solve it, I'm just being lazy. How very fitting that this was posted in the Coding Horrors thread! (I still can't decide whether you're trolling or not.)
|
# ? Jul 27, 2009 23:07 |
|
floWenoL posted:(I still can't decide whether you're trolling or not.)
|
# ? Jul 28, 2009 05:18 |
|
Breaking monster functions into component parts is a horror? Obviously I wouldn't chop things into nonsense enumerated functions like in the example, and it's ridiculous to think this applies to every function. But the horror is having a 1000 line member function that (through various points) recurses and/or calls the same function on different instantiations. Oh, and it can bail out along the way, causing no significant changes, or partial, or full. I came across that one about a year ago, and found myself having dreams at night about the nested, branching mayhem that I'd spend all day trying to step through. Look at the way "break;" can be viewed as a limited and not-very-descriptive form of goto. Most of the time that doesn't matter, and using a goto instead would be useless. But there are times where it isn't sufficient enough to describe the where, what, and why that something like "goto cleanup;" can give us. Similar to that, look at viewing a segment of conditionally-nested code and keeping the conditions in mind, and what needs to happen if a necessary condition fails and you need to back out. Usually isn't that big of a deal, and you just have this nice little segment of code that branches around a bit. But if the nesting is particularly complex, or the bail-out procedure differs frequently from one set of conditions to the next, that "nice little segment" might start earning a reputation among anyone who has to deal with it. To add more description to what's going on (and flexibility to what you can do), you break the logic it into functions that make sense. "Let's see, so right here foo is false and our container wasn't empty and X is null but Y isn't, and Y's depth is greater than l_depth, so if GetVar() fails here...." Turns into "I'm in fillContY(true), which means we have a non-empty container (because we were passed true) that we're first flushing then filling via Y only, and if GetVar() fails we abort and return false, which is handled by the caller based on foo and Y's depth versus l_depth, but i don't really care about that part right now"
|
# ? Jul 28, 2009 10:07 |
|
Bhaal posted:Breaking monster functions into component parts is a horror? When you nest them like you did in your example so the horror is only magnified, yes. If you had a single logic train function calling other functions, it would look good and be much more readable (disclaimer about function names applies, and I wouldn't separate the cleanup code, but this is still worlds better than your example): code:
|
# ? Jul 28, 2009 11:24 |
|
code:
|
# ? Jul 28, 2009 11:39 |
|
Jesus H Wept...code:
[*] There are exceptions, obviously, but if you don't know when an exception would be appropriate, then you're not good enough to be making an exception. Write clear code first and foremost.
|
# ? Jul 28, 2009 13:25 |
|
code:
|
# ? Jul 28, 2009 16:40 |
|
TSDK posted:You're essentially doing three things, so decompose the problem into three steps. Don't try to use your ultra leet indenting, break, goto if-while-else ninja skills to compress those three things into a single function*. Yea, man, it's totally unclear that the code does processing only if it acquires all three resources, but always releases any resources it gets. Edit: vvv yes, I think I do TheSleeper fucked around with this message at 21:50 on Jul 28, 2009 |
# ? Jul 28, 2009 21:13 |
|
Do you need a nap or something? That is his own example of doing what he's recommending.
|
# ? Jul 28, 2009 21:35 |
|
During a code review, we stumbled across something like this:code:
|
# ? Jul 28, 2009 23:11 |
|
CoasterMaster posted:Some people really take that 'no long functions' rule to heart. The variable names and functions names are changed, but the concept is still there. I had a 3000-line function once, though I later split it up into a pair of 1500-line functions and some support functions. It was a very long complicated algorithm with a lot of unique steps and a painful amount of state transmitted between all of them. Whenever I talk about it, people insist that I should have split it up into 50-line functions, which would have required either about 20 parameters for each function or a struct containing 20 members that were only related by virtue of being the entire state of the algorithm. "But 1500 lines is just too long! How can you ever understand it?" drat sight better than I can understand thirty 50-line functions strewn about my codebase.
|
# ? Jul 29, 2009 20:37 |
|
Obviously breaking up a large/complex function is more of a useful tooling (usually when refactoring) to keep in mind and less of a universal mandate that kicks in when a function reaches a certain line count. Monster functions usually have several "steps" to them and those steps can vary on their interdependence with eachother. If cleaning up the readability is the goal and the dependencies between areas of the function are simple enough, then you can often find logical ways to subdivide the labor that might not have been considered before. That makes it easier to follow along the process, as a function call (instead of a block of code) is easier to abstract out if you aren't concerned with it, etc. If changing scope or whatnot causes more problems than it solves then of course it's probably not the appropriate thing to do.
|
# ? Jul 29, 2009 22:33 |
code:
|
|
# ? Jul 30, 2009 07:30 |
|
ZorbaTHut posted:I had a 3000-line function once, though I later split it up into a pair of 1500-line functions and some support functions. It was a very long complicated algorithm with a lot of unique steps and a painful amount of state transmitted between all of them. You are a bad programmer and should feel bad.
|
# ? Jul 30, 2009 10:49 |
|
ZorbaTHut posted:a struct containing 20 members that were only related by virtue of being the entire state of the algorithm.
|
# ? Jul 30, 2009 13:08 |
|
Now, I'm no Perl guy, but why would someone writecode:
|
# ? Jul 30, 2009 13:25 |
|
CoasterMaster posted:
This kind of depends on the situation in my opinion, sometimes i like to break things into different functions if they're different concepts for simplicity and testing purposes.
|
# ? Jul 30, 2009 13:47 |
|
Maybe it was code in transition. Better than before, but still clearly not the best. Mini commits, mini gains...
|
# ? Jul 30, 2009 14:16 |
|
ZorbaTHut posted:"But 1500 lines is just too long! How can you ever understand it?" drat sight better than I can understand thirty 50-line functions strewn about my codebase. Is strewing the functions about your codebase a requirement for breaking a monolithic function down into smaller bite-sized functions? I mean, I've always kept related functions right next to each other as much as possible, maybe I was just doing it wrong though. Also I'm rather curious what this magical 1500-line algorithm that couldn't be subdivided further actually was.
|
# ? Jul 30, 2009 14:25 |
|
biznatchio posted:Is strewing the functions about your codebase a requirement for breaking a monolithic function down into smaller bite-sized functions? I mean, I've always kept related functions right next to each other as much as possible, maybe I was just doing it wrong though. As far as I know ZorbaTHut works at Google, so the magical algorithm was probably something like: code:
|
# ? Jul 30, 2009 14:46 |
|
Badly remembered kernighan quote: I have found the amount of local variables, and not the function length, to be a good indicator of if a function should be split up.
|
# ? Jul 30, 2009 14:59 |
|
biznatchio posted:Also I'm rather curious what this magical 1500-line algorithm that couldn't be subdivided further actually was. In his latest 1.0 branch he used a newer algorithm, but has to deal with more information. I haven't looked over the new code to see if it has slimmed down.
|
# ? Jul 30, 2009 15:12 |
|
ZorbaTHut posted:a struct containing 20 members that were only related by virtue of being the entire state of the algorithm. I don't see what's wrong with this, combined with a reasonable set of manipulation/iteration functions.
|
# ? Jul 30, 2009 16:33 |
|
dancavallaro posted:As far as I know ZorbaTHut works at Google, so the magical algorithm was probably something like: The nerd patrol knows him as the author of QuestHelper. I wouldn't be surprised at how many lines it'd take to do route-finding in Lua. edit: I had the browser open for 2 hours and hadn't yet replied
|
# ? Jul 30, 2009 17:09 |
|
I admit I can't even imagine why an implementation of TSP (or, even any of its variants - bitonic TSP, euclidean TSP, etc) would take anywhere near 3000 lines
|
# ? Jul 30, 2009 17:45 |
|
Dijkstracula posted:I admit I can't even imagine why an implementation of TSP (or, even any of its variants - bitonic TSP, euclidean TSP, etc) would take anywhere near 3000 lines ADD CURRENTEDGEWEIGHT TO TOTALEDGEWEIGHT GIVING TOTALEDGEWEIGHT,
|
# ? Jul 30, 2009 18:31 |
|
Dijkstracula posted:I admit I can't even imagine why an implementation of TSP (or, even any of its variants - bitonic TSP, euclidean TSP, etc) would take anywhere near 3000 lines Genetic TSP?
|
# ? Jul 30, 2009 20:15 |
|
Dijkstracula posted:I admit I can't even imagine why an implementation of TSP (or, even any of its variants - bitonic TSP, euclidean TSP, etc) would take anywhere near 3000 lines
|
# ? Jul 30, 2009 23:18 |
|
quadreb posted:Genetic TSP? From what he's said in the QuestHelper thread, that would seem to be the case (he talks about how it tends to get "stuck" on somewhat-good solutions when a large change would be better, which is exactly the issue a GA would face, for example) GAs are still magic to me. I want to make a game with GA AIs.
|
# ? Jul 31, 2009 05:20 |
|
Ryouga Inverse posted:From what he's said in the QuestHelper thread, that would seem to be the case (he talks about how it tends to get "stuck" on somewhat-good solutions when a large change would be better, which is exactly the issue a GA would face, for example) Check out an existing GA lib like NEAT (or its modified cousin rtNEAT)
|
# ? Jul 31, 2009 05:31 |
|
Ryouga Inverse posted:From what he's said in the QuestHelper thread, that would seem to be the case (he talks about how it tends to get "stuck" on somewhat-good solutions when a large change would be better, which is exactly the issue a GA would face, for example) If he's getting stuck in local maxima, maybe he should try something with a tuneable random component like simulated annealing.
|
# ? Jul 31, 2009 06:04 |
|
Mill Town posted:If he's getting stuck in local maxima, maybe he should try something with a tuneable random component like simulated annealing. But that is besides the point, he's currently using Ant colony optimization. I don't think even the original quest helper used a genetic algorithm, but I can't recall what specific algorithm it used.
|
# ? Jul 31, 2009 06:28 |
|
I would really like to see an implementation ant colony optimization that is 3000 lines. In this thread.
|
# ? Jul 31, 2009 09:49 |
|
Is the problem really TSP? Does WoW not have a geometry that obeys the triangle inequality?
|
# ? Jul 31, 2009 10:10 |
|
Zombywuf posted:Is the problem really TSP? Does WoW not have a geometry that obeys the triangle inequality? So you're claiming that in any geometry that obeys the triangle inequality (e.g. Euclidian), the fastest path visiting all of an arbitrary number of points can be determined without making it into a TSP?
|
# ? Jul 31, 2009 10:30 |
|
Zombywuf posted:Is the problem really TSP? Does WoW not have a geometry that obeys the triangle inequality? × slowly growing
|
# ? Jul 31, 2009 10:42 |
|
quadreb posted:So you're claiming that in any geometry that obeys the triangle inequality (e.g. Euclidian), the fastest path visiting all of an arbitrary number of points can be determined without making it into a TSP? Seriously people, stop playing WoW and just play ProgressQuest while smoking crack. It's cheaper and better for you.
|
# ? Jul 31, 2009 10:52 |
|
|
# ? May 14, 2024 18:59 |
|
For any geometry that obeys the triangle inequality, the fastest path visiting all of an arbitrary number of points can be determined without reducing the problem to that of the Travelling Salesman. I have discovered a truly marvellous proof of this, which this margin is too narrow to contain
|
# ? Jul 31, 2009 11:00 |