[racket] Size matters

From: Sean Kanaley (skanaley at gmail.com)
Date: Sat Jun 8 17:07:52 EDT 2013

Sorry about the multiple posts, but an idea just occurred to me to save 
on name length in the case of name conflicts, like in the posted 
fill-sack example.  Example code:

(let ([n #1])
   (let ([n-that-needs-new-name #2])
     (+ n n-that...)

The inner most n will overwrite the outer one if the same name is used, 
but sometimes, logically, the same name /should/ be used.  There's two 
"n"s at work, and it's the entire point of lexical scope to provide the 
proper one.  So there should be a mechanism to access a different one.  
Behold:

(let (x [n #1])
   (let ([n #2])
     (+ x:n n)

On 06/08/2013 03:49 PM, Eli Barzilay wrote:
> (Possible irrelevant rambling, but I generally am very aware of my
> code formatting, and this looked like a good point to highlight.  It's
> still probably picking on something that not many people care about as
> much as I do...)
>
> The style guide has a section labeled "size matters" -- it also has
> another section on names, encouraging using readable names.  Both of
> these are perfectly reasonable goals, but they can pull you in
> different directions, and I ran into an example that demonstrates it
> in a very nice way.  There is also Yaron's quote about favoring
> readers, which is IMO more important than both of these -- since they
> are just sub-points aiming towards that goal.
>
> I have recently experimented more with taking code compactness more
> seriously.  I still keep my own code under 80 characters, which I know
> many people don't like, but on the other hand, I always try to fill
> the lines as much as possible while maintaining readability.  The idea
> is that the principle of favoring readers means that it should be easy
> to read code -- and lines that are too long, or suffer from rightward
> drift, or names that are too long are all things that delay reading.
> I can also see the recent tendency to use `define' where possible as
> something that goes to this end too: prefer using the same kind of
> binding construct to make it easier to read code.
>
> So to get to my point, there's the decision of what name to use for
> your identifiers some people (*ahem*) stick to an always-readable
> names, to the point of not using names like `i', `n', and `x'.  I'm on
> the complete other side of this -- I strongly prefer these names
> because they make code shorted and therefore easier to read.  The same
> goes for the style guide's recommendation to name intermediate values
> instead of nesting function calls -- it's obviously a line that the
> author should decide on (exaggerated examples on both sides are easy,
> and contribute nothing), and I tend to go further with nested calls
> than defining intermediates.  (This same point applies to naming
> functions vs using lambda expressions.)
>
> My point here is that using longer names and binding intermediates can
> explain the code better, but it's easy to carry this over to hurting
> overall readability.  The example that made me post this is below (no
> need to look up who wrote the original code, it's irrelevant since it
> *is* following many of the style's guidelines).  The first chunk is
> the original code, the second is my rewrite.  There are some obvious
> things like using `match' to make the code more concise and more
> readable, but note that the `cond' expression in the loop is very hard
> to read in the first version -- the descriptive names are long enough
> that the overall structure of the loop is not obvious.
>
> In my revision, the much shorter names are less readable, but on the
> flip side they allow laying out the code in a way that makes it much
> more obvious, and since I started this by just doing mechanical
> transformations, it was surprising to see that what the shrunk code is
> doing becomes very clear.  This clarity has the obvious advantages,
> which is why it's so important to "favor readers".
>
> As a sidenote -- there are two named intermediates in this code that I
> didn't get rid of: on one hand losing them won't save space (and
> therefore I consider removing them as something that would only
> obfuscate things), and on the other hand the result would be nesting
> the verbose computation into a place where it is not relevant.  (And
> yes, there'd be an advantage for having an infix syntax here, IMO...)
>
> (In any case, sorry for the noise.  I'll avoid further replies...)
>
> -------------------------------------------------------------------------------
>
> (define (fill-sack (items items) (volume-left 0.25) (weight-left 25) (sack null) (sack-value 0))
>    (if (null? items)
>        (values (list sack) sack-value)
>        (let* ((item (first items))
>               (item-wgt (item-weight item))
>               (max-q-wgt (floor (/ weight-left item-wgt)))
>               (item-vol (item-volume item))
>               (max-q-vol (floor (/ volume-left item-vol))))
>          (for/fold
>              ((best-sacks (list sack))
>               (best-sack-value sack-value))
>            ((qty (in-range 0 (add1 (min max-q-vol max-q-wgt)))))
>            (let-values (((inner-best-sacks inner-best-sack-value)
>                          (fill-sack (cdr items)
>                                     (- volume-left (* qty item-vol))
>                                     (- weight-left (* qty item-wgt))
>                                     (cons (cons qty item) sack)
>                                     (+ sack-value (* qty (item-value item))))))
>              (cond
>                [(> inner-best-sack-value best-sack-value)
>                 (values inner-best-sacks inner-best-sack-value)]
>                [(= inner-best-sack-value best-sack-value)
>                 (values (append best-sacks inner-best-sacks) inner-best-sack-value)]
>                [else (values best-sacks best-sack-value)]))))))
>
> -------------------------------------------------------------------------------
>
> (define (fill-sack items volume-left weight-left sack sack-value)
>    (match items
>      ['() (values (list sack) sack-value)]
>      [(cons (and (item _ _ item-val weight volume) item) items)
>       (define max-q-wgt (floor (/ weight-left weight)))
>       (define max-q-vol (floor (/ volume-left volume)))
>       (for/fold ([best (list sack)] [best-val sack-value])
>                 ([n (exact-round (add1 (min max-q-vol max-q-wgt)))])
>         (define-values [best* best-val*]
>           (fill-sack items
>                      (- volume-left (* n volume))
>                      (- weight-left (* n weight))
>                      (cons (cons n item) sack)
>                      (+ sack-value (* n item-val))))
>         (cond [(> best-val* best-val) (values best* best-val*)]
>               [(= best-val* best-val) (values (append best best*) best-val*)]
>               [else                   (values best best-val)]))]))
>
> -------------------------------------------------------------------------------
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20130608/3ff8ddea/attachment.html>

Posted on the users mailing list.