Clarity vs. code reuse/generality
Dave Angel
davea at ieee.org
Tue Jul 7 15:04:27 EDT 2009
pdpi wrote:
> On Jul 7, 2:16 am, Steven D'Aprano <st... at REMOVE-THIS-
> cybersource.com.au> wrote:
>
>> On Mon, 06 Jul 2009 16:43:43 +0100, Tim Rowe wrote:
>>
>>> 2009/7/4 kj <no.em... at please.post>:
>>>
>>>> Precisely. As I've stated elsewhere, this is an internal helper
>>>> function, to be called only a few times under very well-specified
>>>> conditions. The assert statements checks that these conditions are as
>>>> intended. I.e. they are checks against the module writer's programming
>>>> errors.
>>>>
>>> Good for you. I'm convinced that you have used the assertion
>>> appropriately, and the fact that so many here are unable to see that
>>> looks to me like a good case for teaching the right use of assertions.
>>> For what it's worth, I read assertions at the beginning of a procedure
>>> as part of the specification of the procedure, and I use them there in
>>> order to document the procedure. An assertion in that position is for me
>>> a statement to the user of the procedure "it's your responsibility to
>>> make sure that you never call this procedure in such a way as to violate
>>> these conditions". They're part of a contract, as somebody (maybe you)
>>> pointed out.
>>>
>>> As somebody who works in the safety-critical domain, it's refreshing to
>>> see somebody teaching students to think about the circumstances in which
>>> a procedure can legitimately be called. The hostility you've received to
>>> that idea is saddening, and indicative of why there's so much buggy
>>> software out there.
>>>
>> LOL.
>>
>> Maybe the reason for "so much buggy software" is that people
>> inappropriately use assert, thus changing the behaviour of code depending
>> on whether it is run with the -O flag or not.
>>
>> I don't know what "hostility" you're seeing. The only hostility I'm
>> seeing is from the OP, which is bizarre considering that he asked for
>> advice and we gave it. What I see is a bunch of people concerned that the
>> OP is teaching novices a bad habit, namely, using assert for error
>> checking. He claims -- angrily and over and over again -- that in his
>> code, the assertions should never fail. Great. Good for him for knowing
>> when to use assert. (...)
>>
>
> But he doesn't.
>
> He asserts:
> assert lo < hi
> but then compares:
> sense =mp(func(hi), func(lo))
>
> sense can't ever be anything other than 1. I actually find it amusing
> that this threat got to 50 posts of raving discussion about assertions
> without anyone spotting that.
>
>
That's because the assert and the comparison are unrelated to each
other. If the function is monotonically decreasing, then by definition
lo<hi would guarantee that func(lo)>= func(hi), which would yield a
sense of 0 or -1.
Trivial example of monotonically decreasing:
def func(inp):
return 53.0 - inp
> Personally, I think the code is an unreadable mess, but that's mostly
> because of all the micro optimizations, not the generality of it.
> Here's my unoptimized, but still equally generic, version:
>
> def _binary_search(lo, hi, func, target, epsilon):
> sense =mp(func(hi), func(lo))
> if sense =0:
> return None
> guess =lo + hi) / 2.
> while abs(func(guess) - target) > epsilon:
> guess =lo + hi) / 2.
> if func(guess) > target:
> hi =uess
> elif func(guess) < target:
> lo =uess
> elif lo =hi:
> return None
> return guess
>
>
And of course your clarified function will fail if the func is
monotonically decreasing.
I still think that rather than using sense in the loop, one should
simply swap lo and hi, and continue.
> This is a newbie course, right? A while True loop might be faster, but
> it's all sorts of wrong for teaching newbies. Likewise, calculating a
> midpoint as mid =hi + lo) * .5 is an aberration in a beginner
> environment. You want your students asking why you're calculating an
> average, not asking why you're multiplying by 0.5. In the same vein, I
> have no words for your target_plus/target_minus cleverness.
>
> The right way to go about it, IMO, is to give them my version, let
> them digest it, make all the necessary changes to it to turn it into
> your (faster) version. Present benchmarks for both, then let the
> readability/performance trade-off sink in. What you achieve with this
> exercise is that, instead of making your students think "I have to
> read that crud!?", they'll respect that ugly code is often the result
> of eking out every last drop of performance from a program as you
> possibly can.
>
>
(untested)
def _binary_search(lo, hi, func, target, epsilon):
""" lo, hi are floats representing the desired range of input values to func()
func() is a function that takes a float argument, and returns a float result
target is the desired result value of func()
epsilon is the allowable error compared to target. If set too small, this function will fail by returning None
precondition: func is monotonic over the range of floating point inputs from lo to hi """
return a float value between lo and hi (inclusive) which yields approximately target
if func(lo) > func(hi):
lo, hi = hi, lo
if not (func(lo) <= target <= func(hi)):
return None #function doesn't have target value within the input range
guess = lo
while abs(func(guess) - target) > epsilon:
guess = (lo + hi) / 2.
if func(guess) > target:
hi = guess
elif func(guess) < target:
lo = guess
elif lo == hi:
return None #function is too steep to have a value within epsilon
return guess
The reason for the "too steep" condition is that with a finite
resolution for 'guess' epsilon could be enough smaller than target as
to make a result impossible. For example, if the slope is 45 degrees,
this happens when epsilon is about 2**51 smaller.
More information about the Python-list
mailing list