On Tue, 01 Apr 2014 01:33:09 +1100, Chris Angelico wrote: > Call this a code review request, if you like. I'm wondering how you'd go > about coding something like this.
I wouldn't. I'd start off by analysing the problem, and putting it into the simplest format possible, and *then* start writing code if and only if needed. See below. The first mistake of computational mathematics is to do the computation before the mathematics, and the Holy Grail is to avoid the computation altogether. > Imagine you're in a train, and the brakes don't apply instantly. The > definition, in the interests of passenger comfort, Ah, you're using comfort in the modern sense, i.e. what people used to call discomfort :-P The scenario you describe has (effectively) infinite rate-of-change-of- acceleration, often called "jerk". (A jerk is a rapid change in acceleration.) Human comfort is (within reasonable limits) more affected by jerk than acceleration. The passengers will feel three quite distinctive jerks, one when the brakes are first applied (which is probably reasonable), then one at 1s, then again at 2s. That's not comfortable by any stretch of the imagination. > is that the first > second of brake application has an acceleration of 0.2 m/s/s, the next > second has 0.425 m/s/s, and thereafter full effect of 0.85 m/s/s. In mathematics, this is called hybrid function, and is usually written like this: g(t) for t < 0 f(t) = { 42 for t == 0 h(t) for t > 0 or something similar. (The opening brace { ought to be large enough to cover all three lines, and there is no closing brace.) In your case, you have three constant functions. > You > have a state variable that says whether the brakes have just been > applied, have already been applied for at least two seconds, or haven't > yet been applied at all. I wouldn't model it that way. Especially since you've missed at least one state :-) I'd model the acceleration as a function of time, otherwise you have to convert a time into a state. Nevertheless, if you insist, we can use a state variable instead: 0 for state == "BRAKES NOT APPLIED YET" accel = { 0.2 for state == "BRAKES ON FOR BETWEEN 0 AND 1 SECOND" 0.425 for STATE == BRAKES ON FOR BETWEEN 1 AND 2 SECOND" 0.85 for state == "BRAKES ON FOR MORE THAN 2 SECONDS" Implied, but not stated, is that once the train stops, acceleration also goes to zero -- the train does not start moving backwards. Haskell has nifty pattern-matching syntax for this that looks quite close to the mathematical hybrid function syntax, but in Python, we're limited to explicitly using an if. If I were coding this, and I'm not, I'd wrap it in a function. One advantage of a state variable rather than a continuous time function is that we can do this: def accel(state): return {NO_BRAKING: 0.0, LOW_BRAKING: 0.2, MID_BRAKING: 0.425, HIGH_BRAKING: 0.85}[state] which is simple enough to skip using a function in the first place, and just use the dict lookup directly. But for a more general question you'll want acceleration as a function of time. If you prefer if...elif over a dict, I'd still hide it in a function. > Problem: Work out how far you'll go before the > brakes reach full power, and how fast you'll be going at that point. Given that problem, we actually only care about LOW_BRAKING and MID_BRAKING. The problem becomes quite simple: At t=0, the train is travelling at u m/s and the brakes are applied with acceleration of 0.2m/s^2 for one second, then 0.425m/s^2 for an additional one second. What is the speed of the train after those two seconds, and the distance travelled. This becomes a simple question for the four standard equations of motion: (1) v = u + at (2) s = 1/2(u + v)t (3) s = ut + 1/2(at^2) (4) v^2 = u^2 + 2as Only (1) and (3) are needed. The distance travelled in the first second is: s1 = u - 0.1 m and the speed of the train at that time becomes: v = u - 0.2 m/s Applying this for the second second gives: s2 = (u - 0.2) - 0.2125 m v = (u - 0.2) - 0.425 m/s and the total distance: s = s1 + s2 No programming required, just a calculator :-) Given that solving the entire problem is barely five lines, writing code to do so is probably unnecessary. The only thing I haven't done is check that the train isn't travelling so slowly that it comes to a complete halt within two seconds. I leave that as an exercise. (Hint: if the final speed is negative, the train came to a halt.) > Here's how I currently have the code. The variable names are a tad long, > as this was also part of me teaching my brother Python. Whatever you used to post this, ate the indentation. > # Already got the brakes fully on > if mode=="Brake2": distance_to_full_braking_power, speed_full_brake = > 0.0, curspeed > # The brakes went on one second ago, they're nearly full elif > mode=="Brake1": distance_to_full_braking_power, speed_full_brake = > curspeed - 0.2125, curspeed - 0.425 # Brakes aren't on. > else: distance_to_full_braking_power, speed_full_brake = (curspeed - > 0.1) + (curspeed - 0.4125), curspeed - 0.625 # If we hit the brakes now > (or already have hit them), we'll go another d meters and be going at s > m/s before reaching full braking power. Using sequence unpacking just to save a newline is naughty :-) dist, speed = (curspeed - 0.1) + (curspeed - 0.4125), curspeed - 0.625 is an abuse of syntax, not far removed from: dist = (curspeed - 0.1) + (curspeed - 0.4125); speed = curspeed - 0.625 It will be much more comprehensible written as two statements. > But I don't like the layout. I could change it to a single assignment > with expression-if, but that feels awkward too. How would you lay this > out? > > (Note that the "else" case could have any of several modes in it, so I > can't so easily use a dict.) It could have, but doesn't. Is your aim to build a general purpose Newtonian linear motion solver, or to solve this one problem? -- Steven -- https://mail.python.org/mailman/listinfo/python-list