Clarity vs. code reuse/generality

Lie Ryan lie.1296 at gmail.com
Fri Jul 3 18:10:42 CEST 2009


kj wrote:
> I'm will be teaching a programming class to novices, and I've run
> into a clear conflict between two of the principles I'd like to
> teach: code clarity vs. code reuse.  I'd love your opinion about
> it.

Sometimes when the decision between clarity and generality becomes too
hard; you might fare better to save the code, go out for a walk to
forget the code, and start a blank text editor. Being in a fresh mind,
you may found an alternative approach, e.g.:

from __future__ import division
def binary_search(lo, hi, func, target, epsilon):
    # reverses hi and lo if monotonically decreasing
    lo, hi = (lo, hi) if func(hi) > func(lo) else (hi, lo)

    param = (lo + hi) / 2

    # loop while not precise enough
    while abs(func(param) - target) > epsilon:
        param = (lo + hi) / 2

        if target < func(param):
            hi = param
        else:
            lo = param
    return param

> The context is the concept of a binary search.  In one of their
> homeworks, my students will have two occasions to use a binary
> search.  This seemed like a perfect opportunity to illustrate the
> idea of abstracting commonalities of code into a re-usable function.
> So I thought that I'd code a helper function, called _binary_search,
> that took five parameters: a lower limit, an upper limit, a
> one-parameter function, a target value, and a tolerance (epsilon).
> It returns the value of the parameter for which the value of the
> passed function is within the tolerance of the target value.
> 
> This seemed straightforward enough, until I realized that, to be
> useful to my students in their homework, this _binary_search function
> had to handle the case in which the passed function was monotonically
> decreasing in the specified interval...
> 
> The implementation is still very simple, but maybe not very clear,
> particularly to programming novices (docstring omitted):
> 
> def _binary_search(lo, hi, func, target, epsilon):
>     assert lo < hi
>     assert epsilon > 0
>     sense = cmp(func(hi), func(lo))
>     if sense == 0:
>         return None
>     target_plus = sense * target + epsilon
>     target_minus = sense * target - epsilon
>     while True:
>         param = (lo + hi) * 0.5
>         value = sense * func(param)
>         if value > target_plus:
>             hi = param
>         elif value < target_minus:
>             lo = param
>         else:
>             return param
> 
> 	if lo == hi:
> 	    return None
> 
> My question is: is the business with sense and cmp too "clever"?
> 
> Here's the rub: the code above is more general (hence more reusable)
> by virtue of this trick with the sense parameter, but it is also
> a bit harder to understand.
> 
> This not an unusual situation.  I find that the processing of
> abstracting out common logic often results in code that is harder
> to read, at least for the uninitiated...
> 
> I'd love to know your opinions on this.
> 
> TIA!
> 
> kj
> 



More information about the Python-list mailing list