optimizing an expression

P

Phil Carmody

If so, such a coding standard will only work in-house, on a strictly
controlled set of hardware. It's possible, but it's harder work than a
coding standard should want to do.

False. Linux's coding standard works across hundreds of companies
and thousands of individuals, who I'm sure use a wider selection
of hardware than found amongst the proponents of any other coding
standard. Yes, it's hard work to police, but there are thousands
of people, and hundreds of robots, policing it.

Phil
 
T

Tim Rentsch

Keith Thompson said:
James Dow Allen said:
I think we'd all agree that "cutesy" shortcuts to achieve a
false keystroke economy are wrong, but this statement goes much
too far. It seems to assume that "if else" is more readable
than "? :" but that should *not* be the case for a C programmer.

I was referring specifically to cases where a ?: operator appears at
the top level of a statement expression, and the second and third
operands' results are discarded. [snip..]
I often string together ?, : and/or && and || into an involved
expression, and add newlines and tabs to make it easy to read.
IMHO the if/else equivalent would be harder to read because of
all the "if/else" clutter.

Can you give an example? [snip]

I might use ?: at the top level if it helped chunkify the code in
a useful way. I find it helps a lot to cluster certain portions
of an algorithm and let others space out more. Here are several
versions of a code fragment for doing some sort of binary search:

low = 0;
high = n;
while (low+1 < high)
{
int m = low + (high-low)/2;
if (ordering(a[m],v) < 1)
{
low = m;
}
else
{
high = m;
}
}


low = 0;
high = n;
while (low+1 < high) {
int m = low + (high-low)/2;
if (ordering(a[m],v) < 1) {
low = m;
} else {
high = m;
}
}


low = 0;
high = n;
while (low+1 < high) {
int m = low + (high-low)/2;
if (ordering(a[m],v) < 1) low = m;
else high = m;
}


low = 0, high = n;
while (low+1 < high) {
int m = low + (high-low)/2;
ordering(a[m],v) < 1 ? (low = m) : (high = m);
}


The last version best expresses how I conceptualize a binary
search algorithm. Written in words, this might be

establish loop invariant for entire array range
WHILE interval has more than one element
compute interval midpoint
restore loop invariant by choosing appropriate sub-interval
END

I don't mean to say that good conceptual match is the only
factor in making coding decisions; obviously there are
many factors. Other things being equal, however, I would
rather see code written in a way that chunks of code on
the page match up to the conceptual chunks that make up
the underlying algorithm. The particular use of ?: does
that rather well in this example (IMO, of course).
 
T

Tim Rentsch

Gareth Owen said:
Keith Thompson said:
Can you give an example? If it doesn't meet the criteria I mentioned
above, so that the transformation from ?: to if-else is absolutely
trivial, I probably wouldn't object. I don't find "if" and "else" to
be clutter; they're just part of the code.

I'm not the original poster but this did remind me of something.
About 5 years ago there was a lengthy thread on c.l.c++ about whether
designing functions to be single-entry-single-exit was still a
worthwhile goal (since exceptions were necessarily a hidden other-exit
but I digress)...

Anyway, some of the strongest proponents of one-return-statement per
function would indulge this stylistic fetish by refactoring this:

/* ... excerpt from Game Of Life Code */
if(live_neighbours == 3) return true;
if(live_neighbours == 2) return data[x][y];
return false;

into

/* ... excerpt from Game Of Life Code */
return live_neighbours == 3 ? true
: live_neighbours == 2 ? data[x][y]
: false;

I don't like the latter because it requires me to either have the
precedence rules at my fingertips, or (more likely) LISP it up until
it said

return (live_neighbours == 3 ? true :
(live_neighbours == 2 ? data[x][y] : false));

return live_neighbors == 3 || live_neighbors == 2 && data[x][y];

Simple, direct expression of the condition for life.
 
T

Tim Rentsch

Richard Heathfield said:
In
<13a223a9-c307-4bcd-95a7-ef46b6fada62@g22g2000prf.googlegroups.com>,
James Dow Allen wrote:

I was happy to see in this thread that I'm not the only who
finds it virtuous to conserve vertical space. Evidently
Mr. Heathfield is not in our select company! :)

Right. I've found that I can afford millions of lines of vertical
space. It's much cheaper than is commonly believed.
Richard, are you one of these people with 20-10 vision who
view code in the smallest font available?

No, if anything I use a slightly oversized font. But I try to keep my
functions relatively short, [snip]

Hmmm. I think your relatives must be a lot taller than mine
are.
 
T

Tim Rentsch

Gareth Owen said:
Richard said:
[... on expressions like 'a = b ? c : d' ...]

I know its straightforward. It's still clunky and unclear. If you
really want that interpretation, but some brackets in there. Program
as you if expect other people to read your code. Express your intent,
and don't expect everyone to enjoy C's more pointless idioms as much
as you do. [snip elaboration]

The ?: operator is not some obscure corner of C, nor does using
it automatically qualify as an idiom. If someone who claims to
know C can't both read and write C code that has an occasional
use of ?:, he or she isn't worthy of being called a programmer.

Yes, I learned FORTRAN as my first language; 360 assembly as
my second. I know there are people who yearn for "the good
old days" and would be just as happy being stuck there forever.
More sensible people want to move past that.
 
T

Tim Rentsch

io_x said:
what about
e_type medianr(e_type a, e_type b, e_type c)
{e_type r;
if(a>c) {r=a, a=c, c=r;}
if(a>b) {r=a, a=b, b=r;}
if(c<b) {r=c, c=b, b=r;}
return b;
}

In all these different variations of median, it's suprising that
no one has emphasized obviousness of correctness. For example,

...
#define CHECK(a,b,c) do if ((a)<=(b) && (b)<=(c)) return (b); while(0)
CHECK(a,b,c);
CHECK(a,c,b);
CHECK(b,a,c);
CHECK(b,c,a);
CHECK(c,a,b);
CHECK(c,b,a);
assert(0);
return 0;
#undef CHECK

or instead using the return form

#define CHECK(a,b,c) ((a)<=(b) && (b)<=(c)) ? (b)
return CHECK(a,b,c)
: CHECK(a,c,b)
: CHECK(b,a,c)
: CHECK(b,c,a)
: CHECK(c,a,b)
: CHECK(c,b,a)
: (assert(0), 0);
#undef CHECK

In either of these it's easy to tell at a glance that all
cases are tested and that the appropriate value is returned
in all cases.
 
T

Tim Rentsch

Nisse Engström said:
I sometimes find chains like the above to be much
clearer than some long unwieldy if-else ladder.
Here's another example, from a few years back:

(offLmod == offRmod ? ror_d0 :
minlen <= BITS ? ror_short :
offLmod < offRmod ? ror_long_dn :
ror_long_up )
(bsL->data+offLdiv, bsR->data+offRdiv, offLmod, offRmod, minlen);

I realize now that maybe it should've been:

void (*fptr)(/* args */) =
(offLmod == offRmod ? ror_d0 :
minlen <= BITS ? ror_short :
offLmod < offRmod ? ror_long_dn :
ror_long_up);

(*fptr) (bsL->data+offLdiv, bsR->data+offRdiv, offLmod, offRmod,
minlen);

I think both are very readable and maintainable because
of the way the conditions and results are listed in
separate columns, and because the function arguments
only have to be spelled out once.

I don't see that defining then using a function pointer
variable adds anything. I don't see anything wrong with
using the long expression (assuming it appears in the
program only once) directly as the function designator.
However the overall expression should be laid out
carefully so the different chunks stand out appropriately.
Here is a plausible try:

/* Choose among several functions to call on given argument list */
/* */
( offLmod == offRmod ? ror_d0
: minlen <= BITS ? ror_short
: offLmod < offRmod ? ror_long_dn
: ror_long_up
)(
bsL->data+offLdiv, bsR->data+offRdiv, offLmod, offRmod, minlen
);

The two main parts (function designator, and function arguments)
stand out as clearly distinct chunks, with the function call
indication (ie, the left parenthesis) separating them. Granted,
it may look a bit strange, but the situation is unusual, so
looking strange has some positive benefits in this case.

Of course, all this assumes that the pattern is isolated and
appears only once or twice in the program. If it appears
more often than that, very likely the program needs some
refactoring to separate out the common pattern.
 
T

Tim Rentsch

Richard Heathfield said:
Better still, wrap up the complexity in a function:

typedef void ror_function_type( /* args */ );

/* ... */

ror_function_type *p = get_ror_function(offLmod, offRmod, etc!);

(*p)(args);

Besides having an unneeded variable, this pattern seems suspect
because the two argument lists have a lot of overlap. If we're going
to go to the trouble of writing a function, it seems like there should
be one function with all the parameters, as for example,

rorX( offLmod, offRmod, minlen, BITS, bsL->data+offLdiv, bsR->data+offRdiv );

making the commonality more evident.
 
D

Dik T. Winter

>
> I understand what that does. However, for those of us who read left
> to right, the problem is the ? modifies what the = does, so I have to
> mentally backtrack.
>
> As I scan the line, my mind first sees
>
> a = b ...
>
> Thinks: "OK, I'm setting a to the value of b" ...

Do you think the same when you read a line containing a = b + c; ?
 
D

Dik T. Winter

> I can read it as "assign b to a then add c".
>
> As opposed to
>
> double a,b,c;
> double *p;
>
> a = p ? b : c;
>
> The beginning of that looks like the assignment of a pointer to a
> double.

As does a = p[2]; .
 
B

Ben Pfaff

True, but when you need that, there is no single printf() call that
prints that sentence reliably. Even

if (nships==1)
printf(inter("I see 1 ship.\n"));
else
printf(inter("I see %u ships.\n"), nships);

while a lot more internationalisable, is not enough to guarantee
portability to all languages.

Fortunately, i18n libraries can deal with that, e.g.:
printf (ngettext ("I see %u ship.", "I see %u ships.", nship), nships);
 
K

Keith Thompson

Gareth Owen said:
Yup. If you parse that as "I'm setting the value of a to b, then
adding c", you get the meaning exactly.

Except that that's not what "a = b + c;" means. The value of b is
never assigned to a; only the sum b+c is assigned to a.

Your interpretation happens to be equivalent in this case, but there
are probably cases where it could lead you astray.
 
S

Squeamizh

So would the equivalent if else. I am aware that in practical
internationalization, programmers are directed to use "resources" of
strings with the "right" translations normally prepared by technical
writers and translators. Correspondingly, they are discouraged in my
experience from writing code like the above which seems and is rather
clever...but ties the program to English, thereby increasing le
dominance Anglo-Saxonne over tout le monde in fashion neo-colonialiste
alors peut etre Fasciste.

My own theory is that programmers should be widely and deeply cultured
with the resulting awareness of when they are building in such a
dependency. The problem being that most programmers are what the
Russians would call ne kulterni and what Indian chaps call jungly.

Who do you get your weed from, and is he within driving distance of
San Francisco?
 
U

user923005

io_x said:
"user923005" <[email protected]> ha scritto nel messaggio
[snip performance questions]
typedef int     e_type;
e_type          median3(e_type a, e_type b, e_type c)
{
   return (a < b) ? ((b < c) ? b : ((a < c) ? c : a)) : ((a < c) ?
a : ((b < c) ? c : b));
}
what about
e_type   medianr(e_type a, e_type b, e_type c)
{e_type  r;
 if(a>c) {r=a, a=c,  c=r;}
 if(a>b) {r=a, a=b,  b=r;}
 if(c<b) {r=c, c=b,  b=r;}
 return  b;
}

In all these different variations of median, it's suprising that
no one has emphasized obviousness of correctness.  For example,

   ...
   #define CHECK(a,b,c) do if ((a)<=(b) && (b)<=(c)) return (b); while(0)
      CHECK(a,b,c);
      CHECK(a,c,b);
      CHECK(b,a,c);
      CHECK(b,c,a);
      CHECK(c,a,b);
      CHECK(c,b,a);
      assert(0);
      return  0;
   #undef CHECK

or instead using the return form

   #define CHECK(a,b,c)  ((a)<=(b) && (b)<=(c))  ?  (b)
      return  CHECK(a,b,c)
         :    CHECK(a,c,b)
         :    CHECK(b,a,c)
         :    CHECK(b,c,a)
         :    CHECK(c,a,b)
         :    CHECK(c,b,a)
         :    (assert(0), 0);
   #undef CHECK

In either of these it's easy to tell at a glance that all
cases are tested and that the appropriate value is returned
in all cases.

There is a test for correctness in my test driver.
 
U

user923005

"io_x" <[email protected]> ha scritto nel messaggio





what about
e_type   medianr(e_type a, e_type b, e_type c)
{e_type  r;
if(a>c) {r=a, a=c,  c=r;}
if(a>b) {r=a, a=b,  b=r;}
if(c<b) {r=c, c=b,  b=r;}
return  b;
}
; medianrAsm(r, a, c)
; 0ra, 4P1, 8P2, 12P3
_medianrAsm:
         mov     edx,  dword[esp+  4]
         mov     eax,  dword[esp+  8]
         mov     ecx,  dword[esp+ 12]
         cmp     edx,  ecx
         jle     .1
         xchg    edx,  ecx
.1:       cmp     edx,  eax
         jle     .2
         xchg    eax,  edx
.2:       cmp     ecx,  eax
         jge     .3
         xchg    eax,  ecx
.3:
         ret
-------------------------

with
-------
section _DATA use32 public class=DATA

global _medianrAsm

section _TEXT use32 public class=CODE

; a r c
; 0 1 2 -
; medianrAsm(r, a, c)
; 0ra, 4P1, 8P2, 12P3
          align   8
_medianrAsm:
          mov     eax,  dword[esp+  4]
          mov     edx,  dword[esp+  8]
          mov     ecx,  dword[esp+  12]
          cmp     eax,  ecx
          jle     .1
          xchg    eax,  ecx
;   a<=c   0<=2
.1:       cmp     edx,  eax
          jg      .2
          ret                   ; r<=a<=c  1<=0<=2
.2:       cmp     ecx,  edx
          jg      .3
          mov     eax,  ecx
          ret                   ; a<=c<=r  0<=2<=1
.3:       mov     eax,  edx     ; a<=r<=c
          ret
---

Prova median3
m=843962432  Result: 29.000000
--------------
Prova median3a
m=843962432  Result: 28.000000
--------------
Prova median3b
m=843962432  Result: 43.000000
--------------
Prova medianr
m=843962432  Result: 29.000000
--------------
Prova medianrAsm
m=843962432  Result: 28.000000
--------------

they are all of the same speed but
median3b
seems slower

------------------------
#define P printf

/*
Surprising to me, the switch ran 4x slower than the simple if() tests.

It appears that the missed branch predictions are killer.
I guess that profile guided optimization will help your routine
quite a bit if the data has some particular pattern.
*/

typedef int     e_type;

e_type          median3(e_type a, e_type b, e_type c)
{
    return (a < b) ? ((b < c) ? b : ((a < c) ? c : a)) : ((a < c) ?
a : ((b < c) ? c : b));

}

e_type   medianrAsm(e_type a, e_type b, e_type c);

e_type   medianr(e_type a, e_type b, e_type c)
{e_type  r;
 if(a>c) {r=a; a=c;  c=r;}
/*  a<c  */
 if(a>b) return  a;
 if(c<b) return  c; /*  a<c<b  */
 return  b;

}

e_type             median3a(e_type a, e_type b, e_type c)
{
    if (a < b) {
        if (b < c)
            return b;           /* a b c */
        if (c < a)
            return a;           /* c a b */
        return c;               /* a c b */
    } else {
        if (a < c)
            return a;           /* b a c */
        if (c < b)
            return b;           /* c b a */
        return c;               /* b c a */
    }

}

e_type             median3b(e_type a, e_type b, e_type c)
{
    switch ((a > b) + ((b > c) << 1) + ((c > a) << 2)) {
    case 0:
        return a;
    case 1:
        return c;
    case 2:
        return a;
    case 3:
        return b;
    case 4:
        return b;
    case 5:
        return a;
    case 6:
    default:
        return c;
    }

}

#include <stdio.h>
#include  <time.h>
#include  <stdlib.h>

double  prova(e_type  (*f)(e_type, e_type, e_type))
{time_t   t1, t2;
 double        d;
 e_type  i, j, k, m=0, m1, m2;

 t1=time(0);
 for(i=0; i<1000; i++)
   for(j=0; j<1000; j++)
      for (k=0; k<3000; k++)
        {m1=i^j; m2=i^k;
         m += f(m1, i, m2);
        }
 t2=time(0);
 d=difftime(t2, t1);
 P("m=%u  ", (unsigned) m);
 return  d;

}

double  prova1(e_type  (*f)(e_type, e_type, e_type))
{time_t   t1, t2;
 double        d;
 e_type  i, j, k, m=0, kk;

 t1=time(0);
 for(kk=0; kk<300000000 ; ++kk)
       {i=rand(); j=rand(); k=rand();
        m+=f(i, j, k);
       }
 t2=time(0);
 d=difftime(t2, t1);
 P("m=%u  ", (unsigned) m);
 return  d;

}

int             main(void)
{   double          h;
    e_type          i,
                    j,
                    k,
                    m,
                    m2,
                    m3;
    for (i = 0; i < 300; i++)
        for (j = 0; j < 300; j++)
            for (k = 0; k < 300; k++) {
                m = median3(i, j, k);
                m2 = medianr(i, j, k);
                m3 = medianrAsm(i, j, k);
                if (m3 != m)
                   {printf("Disagreement of %d verses %d\n", m3, m);
                    printf("median of %d %d %d is %d\n", i, j, k, m3);
                    return  0;
                   }
                if (m2 != m)
                   {printf("Disagreement of %d verses %d\n", m2, m);
                    printf("median of %d %d %d is %d\n", i, j, k, m2);
                    return  0;
                   }
            }

 P("Prova median3\n");
 h=prova(median3);
 P("Result: %f\n", h);
 P("--------------\n");

 P("Prova median3a\n");
 h=prova(median3a);
 P("Result: %f\n", h);
 P("--------------\n");

 P("Prova median3b\n");
 h=prova(median3b);
 P("Result: %f\n", h);
 P("--------------\n");

 P("Prova medianr\n");
 h=prova(medianr);
 P("Result: %f\n", h);
 P("--------------\n");

 P("Prova medianrAsm\n");
 h=prova(medianrAsm);
 P("Result: %f\n", h);
 P("--------------\n");

 return 0;



}

My result:

typedef int e_type;

e_type
median3(e_type a, e_type b, e_type c)
{
return (a < b) ? ((b < c) ? b : ((a < c) ? c : a)) : ((a < c) ?
a : ((b < c) ? c : b));
}

e_type
median3a(e_type a, e_type b, e_type c)
{
if (a < b) {
if (b < c)
return b; /* a b c */
if (c < a)
return a; /* c a b */
return c; /* a c b */
} else {
if (a < c)
return a; /* b a c */
if (c < b)
return b; /* c b a */
return c; /* b c a */
}
}

e_type
median3b(e_type a, e_type b, e_type c)
{
switch ((a > b) + ((b > c) << 1) + ((c > a) << 2)) {
case 0:
return a;
case 1:
return c;
case 2:
return a;
case 3:
return b;
case 4:
return b;
case 5:
return a;
case 6:
default:
return c;
}
}

e_type
median3c(e_type a, e_type b, e_type c)
{
e_type elist[7] =
{a, c, a, b, b, a, c};
return elist[(a > b) + ((b > c) << 1) + ((c > a) << 2)];
}

e_type
median3d(e_type a, e_type b, e_type c)
{
e_type *pa = &a;
e_type *pb = &b;
e_type *pc = &c;
const e_type *elist[7] =
{pa, pc, pa, pb, pb, pa, pc};
return *elist[(a > b) + ((b > c) << 1) + ((c > a) << 2)];
}

e_type
median3e(e_type a, e_type b, e_type c)
{
/* 2.0 1.1 0.2 0 */
switch ((0x2148U >> ((((a > b) << 1) + ((b > c) << 2) + ((c > a)
<< 3)))) & 3) {
case 0:
return a;
case 1:
return b;
case 2:
default:
return c;
}
}

e_type
median3f(e_type a, e_type b, e_type c)
{
/* 2.0 1.1 0.2 0 */
return (((0x2148U >> ((((a > b) << 1) + ((b > c) << 2) + ((c > a)
<< 3)))) & 3) == 0) * a
+ (((0x2148U >> ((((a > b) << 1) + ((b > c) << 2) + ((c >
a) << 3)))) & 3) == 1) * b
+ (((0x2148U >> ((((a > b) << 1) + ((b > c) << 2) + ((c >
a) << 3)))) & 3) == 2) * c
;
}

e_type
median3g(e_type a, e_type b, e_type c)
{
/* register */
unsigned tmp;

/* 2.0 1.1 0.2 0 */
tmp = ((0x2148U >> ((((a > b) << 1) + ((b > c) << 2) + ((c > a) <<
3)))) & 3);
return tmp & 2 ? c : tmp ? b : a;
}

e_type
median3h(e_type a, e_type b, e_type c)
{
e_type r;
if (a > c) {
r = a, a = c, c = r;
}
if (a > b) {
r = a, a = b, b = r;
}
if (c < b) {
r = c, c = b, b = r;
}
return b;
}

#ifdef UNIT_TEST
#include <stdio.h>
int main(void)
{
e_type i,
j,
k,
m,
m2,
m3,
m4,
m5,
m6,
m7,
m8,
m9;
for (i = 0; i < 3000; i++)
for (j = 0; j < 3000; j++)
for (k = 0; k < 3000; k++) {
m = median3(i, j, k);
m2 = median3a(i, j, k);
m3 = median3b(i, j, k);
m4 = median3c(i, j, k);
m5 = median3d(i, j, k);
m6 = median3e(i, j, k);
m7 = median3f(i, j, k);
m8 = median3g(i, j, k);
m9 = median3h(i, j, k);
#ifdef SHOW_RESULTS
printf("median of %d %d %d is %d\n", i, j, k, m3);
#endif
if (m9 != m)
printf("Disagreement (9) of %d verses %d\n", m9,
m);
if (m8 != m)
printf("Disagreement (8) of %d verses %d\n", m8,
m);
if (m7 != m)
printf("Disagreement (7) of %d verses %d\n", m7,
m);
if (m6 != m)
printf("Disagreement (6) of %d verses %d\n", m6,
m);
if (m5 != m)
printf("Disagreement (5) of %d verses %d\n", m5,
m);
if (m4 != m)
printf("Disagreement (4) of %d verses %d\n", m4,
m);
if (m3 != m)
printf("Disagreement (3) of %d verses %d\n", m3,
m);
if (m2 != m)
printf("Disagreement (2) of %d verses %d\n", m2,
m);
}
return 0;
}

#endif
/*
Profile results were:
Function Name,Exclusive Samples,Exclusive Samples %,
"median3f","73,811",18.50,
"main","58,858",14.75,
"median3d","58,253",14.60,
"median3g","49,352",12.37,
"median3c","44,832",11.23,
"median3b","42,708",10.70,
"median3e","33,287",8.34,
"median3h","16,757",4.20,
"median3a","10,822",2.71,
"median3","10,380",2.60,
*/
 
D

Dik T. Winter

>
> Except that that's not what "a = b + c;" means. The value of b is
> never assigned to a; only the sum b+c is assigned to a.
>
> Your interpretation happens to be equivalent in this case, but there
> are probably cases where it could lead you astray.

Yup: a = b + a;
 
D

Dik T. Winter

>
> Oh, certainly. Although part of the problem is trying to describe in
> words a process that is not confined to the concious parts of
> thinking. A computer parser doesn't worry about the complexity of
> expressions, but my eyes/brain do.

The base problem is of course that assignment is a right to left process,
contrary to normal reading. When the first designers of computer languages
had done it the other way around (the expression on the left, the variable
to which it is assigned on the right) there would be fewer problems.
 
N

Nisse Engström

I don't see that defining then using a function pointer
variable adds anything. I don't see anything wrong with
using the long expression (assuming it appears in the
program only once) directly as the function designator.

It adds a little readability for me. Or at least it did.
On further inspection, it seems that the long version
wasn't actually bad. Your milage may vary. Mine does.
However the overall expression should be laid out
carefully so the different chunks stand out appropriately.
Here is a plausible try:

/* Choose among several functions to call on given argument list */
/* */
( offLmod == offRmod ? ror_d0
: minlen <= BITS ? ror_short
: offLmod < offRmod ? ror_long_dn
: ror_long_up
)(
bsL->data+offLdiv, bsR->data+offRdiv, offLmod, offRmod, minlen
);

It's not god-awful, but it's far from an improvement.
My money stays on the first horse.


/Nisse
 
K

Keith Thompson

Gareth Owen said:
Oh, certainly. Although part of the problem is trying to describe in
words a process that is not confined to the concious parts of
thinking. A computer parser doesn't worry about the complexity of
expressions, but my eyes/brain do.

I mentally chunk complicated expressions into simpler parts, and then
reassemble them to construct the whole. If the interrelation between
the parts is so complex that my chunking - which looks for brackets
and other clues about structure - never gets me a bit that I can
digest, I get mental indigestion.

If an expression has a overly complex subexpression, I'll pull it out,
assign it to a temporary variable with a meaningful name, and trust
the compiler to do the optimal thing.

The writing analogy here is the run-on sentences. In clear (if
unpoetic) writing, one thought per sentence will usually suffice.

That's fine if you're *writing* code and can control the complexity.
(You'll probably end up with code that's too simpified for my taste,
but my taste doesn't, and shouldn't, control what you write.)

But if you interpret
a = b + c;
as equivalent to:
assign b to a
add c to a
then you're misunderstanding what it means. As Dik pointed out,
your interpretation fails for
a = b + a;

My advice is that you re-train yourself, so that when you see
a = b + c;
you think:
compute the sum of b and c
assign the result to a

In other words, align your thought processes to reality, not the other
way around. It will take some practice, but I think it will be well
worth it.
 
R

Richard Bos

Ben Pfaff said:
Fortunately, i18n libraries can deal with that, e.g.:
printf (ngettext ("I see %u ship.", "I see %u ships.", nship), nships);

That, OTOH, seems overkill to me; all you should need (as well as what
you need at the very least) is

printf(ngettext("I see %u ships.\n", nship), nship);

What you need is the properly internationalised text for "I see N
ship(s)." The _internationalisation_ library should not need to know
that the _English_ singular for ships is ship. (Unless, of course, your
local language is English; but in that case, _it_ should give you that
English singular, and not rely on the user-programmer to specify it for
that language alone.)

Richard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,138
Latest member
NevilleLam
Top