• (cs)

    Some people take pride in writing cryptic code in perl.

  • (cs) in reply to wgh

    Its not a wtf, Its job security. :)

  • (cs) in reply to anne
    anne:
    mangobrain:
    C gives you enough rope to hang yourself with, but warns you that you probably don't want to knot it that way. Perl gives you the rope, ties a noose in the end, blindfolds you, and stands you on a rickety chair.

    Don't forget, there's more than one way to do it in Perl. Perl also hands you a loaded gun, a bottle of pills and a glass of water, and does it all on a tall bridge. Oh, and a bottle of gasoline and a lit match. (thanks, captcha, "burned", for reminding me of that one.)

    PERL is like ViseGrips - the wrong tool for every job.

  • barfing on BEGIN/END (unregistered) in reply to DrYak

    [quote user="DrYak"]I DO write such code, you insensitive clod...

    ...except mine happen to be correctly indented, fully commented, and do actually work.

    The ternary operator is useful when you have to assign multiple different possible values to 1 single var-name and may later need to rename the var. Specially in languages that don't support the GNU-C feature ({ ... ; return_value; })

    Preferring "if/then/else" clauses to ternary operators just for the sake of readability is like prefering "begin/end" to curly braces : it's just cosmetic without any real valid reason. } [/code]

    Ugh, I HATE begin/end syntax! But yeah, same ol semantics.

    Still hate it tho.

    (Living in a Delphi/Sql Server wonderland of Begin/End)

  • Botzinger Gulm (unregistered)

    I think the original claim that C is 'not only' a PDP-11 assembler on the basis of the ternary op is not quite correct. Misters K & R used the fancy machine's microcode-able instructions to implement an atomic test-and-set or something else equivalent - and I suspect how it ended up into C was the ternary op. Alternatively they could have microcoded a conditional-move instruction, which is quite handy.

    Short-circuit boolean evaluation came to C much later.

  • Juenemann (unregistered) in reply to stnever
    stnever:
    Just to add to the statistics, I too believe nesting ternaries is not readable (if it's not dead obvious, it shouldn't be used; even if you're skilled enough to understand it, the next developer might not be).
    if you aren't sure a pointer has been initialised before using it, or that it is pointing to the right thing, or that a given I/O loop isn't going to overflow a buffer, then you should rethink how you've designed your code...

    ...

    Pointer arithmetic is good fun, and a useful concept that doesn't slow things down. Buffer overflows needn't be an issue if you just think before coding, and ensure that you know certain things such as whether or not a given library call will return a NULL terminated string (as opposed to an unterminated collection of bytes). Memory leaks can be a problem, and ensuring you don't have them can be tedious in a function with multiple exit points, but that's just a matter of discipline (or of using C++ instead ;)

    On this topic, I prefer languages which will do the tedious work for me. I believe most programmers find it fun to solve problems by coding, but not ALWAYS the SAME problems. Good programmers already know what needs to be done -- consistent type checking (so obscure libraries can't "fool" you on the return type problem), consistent data types (byte array or null-terminated char array? just return a String!), always check buffer length (if it's "always", why shouldn't the language help by doing it by default?), test pointers before dereferencing, et cetera.

    Good programmers know and apply those concepts and grow tired. New programmers don't know and make hard-to-find mistakes (and these will take OUR time, not theirs, since we are the experts they'll call for help).

    I'd rather spend my time writing only what solves the problem, in a nice and readable way, and let the compiler add the now-agreed-upon best practices. To me, readability and ease of use outweight what little performance loss might occur (after all it won't do anything that you wouldn't do anyway). Keep it simple. DEAD simple. But no simpler.

    ...that's what I think. what about the rest of you?

    captcha: xevious. I don't know what this is, but the interesting thing is that it's not that distorted here -- an ocr script could probably identify it. Well it seems to be working so far to prevent robots, so whatever.

    That is why Ada works so well. If only there were more support ...

  • (cs) in reply to stnever
    stnever:
    captcha: xevious. I don't know what this is, but the interesting thing is that it's not that distorted here -- an ocr script could probably identify it. Well it seems to be working so far to prevent robots, so whatever.
    This is off-topic, but I feel compelled to answer it, because I've seen others ask the same question and because I am a huge fan of Xevious.

    It's a classic arcade game. Do a search on it sometime.

  • (cs)

    The ternary operator is nice when used sparingly and in simple expressions, for example, something like: int num += bAdvance ? 1 : -1;

    However, I have been known to abuse the operator in the past: for ( ++offset, num_conditional_constructs = 0; ( offset < expression->TokenOffset ) && ( ( expression->TokenList[ offset ].type != TOKEN_KEYWORD ) || ( expression->TokenList[ offset ].val != KW_colon ) || ( num_conditional_constructs > 0 ) ); ( ( expression->TokenList[ offset ].type == TOKEN_KEYWORD ) ? ( ( expression->TokenList[ offset ].val == KW_question ) ? num_conditional_constructs++ : ( ( expression->TokenList[ offset ].val == KW_colon ) ? num_conditional_constructs-- : 0 ) ) : 0 ), CopyToken( &subexpression->TokenList[ subexpression->TokenOffset++ ], &expression->TokenList[ offset++ ] ) );

    Yes, that for-loop has no body. Yes, that's all on one line. Yes, it's horrendously unmaintainable (and full of WTFs). No, I've never changed it despite the fact that I know it's a mess. Sadly, I'm almost proud of making something this bad.

    Incidentally, that code was part of a function that parsed ternary operators in a scripting language. Easily the most complicated part of the entire parser.

  • Ornedan (unregistered) in reply to Anonymous
    Anonymous:
    Rhamphoryncus:
    (n-ary operator)

    You can almost have this in python:

    [0-result, 1-result, 2-result, ... n-result][(expression that evaluates to a #)]
    Despite the syntactic similarity, this is different in that it will evaluate ALL expressions given in the list, then pick one of the results. If your expressions have side effects, this is critical.
    [lambda: 0-result, lambda: 1-result, lambda 2-result, ... lambda: n-result][(expression that evaluates to a #)]()
    

    That should work, even in case of side-effectfull expressions.

  • (cs) in reply to 10
    10:
    I wonder how the guys were able to actually have this work correctly.. Most of the times such long & hard to read pieces of code are bugs' favorite nest.

    The indenting actually makes it pretty easy to read. Of course, if you stripped out the formatting -- or worse, made the formatting intentionally deceiving -- that would be a nightmare.

  • anonymous guy (unregistered) in reply to MikeBeer
    MikeBeer:
    The ternary operator is nice when used sparingly and in simple expressions, for example, something like: int num += bAdvance ? 1 : -1;

    However, I have been known to abuse the operator in the past: for ( ++offset, num_conditional_constructs = 0; ( offset < expression->TokenOffset ) && ( ( expression->TokenList[ offset ].type != TOKEN_KEYWORD ) || ( expression->TokenList[ offset ].val != KW_colon ) || ( num_conditional_constructs > 0 ) ); ( ( expression->TokenList[ offset ].type == TOKEN_KEYWORD ) ? ( ( expression->TokenList[ offset ].val == KW_question ) ? num_conditional_constructs++ : ( ( expression->TokenList[ offset ].val == KW_colon ) ? num_conditional_constructs-- : 0 ) ) : 0 ), CopyToken( &subexpression->TokenList[ subexpression->TokenOffset++ ], &expression->TokenList[ offset++ ] ) );

    Yes, that for-loop has no body. Yes, that's all on one line. Yes, it's horrendously unmaintainable (and full of WTFs). No, I've never changed it despite the fact that I know it's a mess. Sadly, I'm almost proud of making something this bad.

    Incidentally, that code was part of a function that parsed ternary operators in a scripting language. Easily the most complicated part of the entire parser.

    You're in luck. The ioccc deadline is at the end of this month.

  • Mazer (unregistered)

    Could one of you people who have said that this is quite readable code explain to me how the next-to-last result ('' #'[Cancelling]') can ever be assigned to the $send_button variable?

    Oh, and for those of you discussing associativity - please don't confuse associativity with precedence.

    NOTE: the real WTF is that 'Canceling' is misspelled.

  • Jens (unregistered) in reply to Mazer
    Mazer:
    Could one of you people who have said that this is quite readable code explain to me how the next-to-last result ('' #'[Cancelling]') can ever be assigned to the $send_button variable?
    !$rAuth->permits(SEND)   -> false
    $status != 1             -> false
    $approved                -> false
    $send_now                -> true
    

    There you are.

  • (cs) in reply to ChrisF
    ChrisF:
    Never thought I'd write a classic "The Real WTF(TM) Is" comment, but here we go. Some people have commented that the cascading ternary operators are the WTF, but at least the code is nicely indented. I beg to differ - nested ternaries would be perfectly okay in this case (even recommended in Perl Best Practices for conditional assignment), but with a tabular layout that resembles a lookup table like so:
             # Condition            # Value
    my $post = $comment_number == 1 ? 'Frist psot 111 eleventy-one'
             : $comment_number == 2 ? 'The Real WTF'
             : $comment_number == 3 ? 'Not really a WTF'
             : $comment_number == 4 ? 'FILE_NOT_FOUND'
             : $comment_number == 5 ? 'Brillant'
             : $comment_number == 6 ? 'Where\'s the wooden table'
             : $comment_number == 7 ? 'Needs more XML'
             :                        "This wouldn't have happened if they had used $my_favourite_language in the first place" # (default)
    ;
    
    Not necessarily less readable than cascaded ifs/elsifs, but much more compact.

    Well, that's very readable if you're familiar with the idiom. If you aren't, you're going to have to unravel the semantics yourself. And that's not easy in a case like the one you presented.

  • David Reiner (unregistered) in reply to TimF

    Right associative means that

    a ? b : c ? d : e

    is evaluated as

    a ? b : (c ? d : e)

    not

    (a ? b : c) ? d : e

    You are confusing assocativity and precedence

  • !Z (unregistered)

    Actually, it's some of the nicest perl code i've ever seen. Good work.

  • Mazer (unregistered) in reply to Jens
    Jens:
    !$rAuth->permits(SEND)   -> false
    $status != 1             -> false
    $approved                -> false
    $send_now                -> true
    

    There you are.

    Thanks.

  • Anon (unregistered) in reply to anonymous workaholic
    anonymous workaholic:
    OK, so it's cleanly indented and perfectly readable code that - horror of horrors - uses the ternary operator. Why do people hate this little operator so much? It's essentially the same thing as an if clause, nothing complicated or obscure going on here. IMHO the actual WTF here is the use of global flag variables, especially the way $send_now is checked once and then again in the else branch of the first check.

    http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.6

    s the ?: operator evil since it can be used to create unreadable code?

    No, but as always, remember that readability is one of the most important things.

    Some people feel the ?: ternary operator should be avoided because they find it confusing at times compared to the good old if statement. In many cases ?: tends to make your code more difficult to read (and therefore you should replace those usages of ?: with if statements), but there are times when the ?: operator is clearer since it can emphasize what's really happening, rather than the fact that there's an if in there somewhere.

    Let's start with a really simple case. Suppose you need to print the result of a function call. In that case you should put the real goal (printing) at the beginning of the line, and bury the function call within the line since it's relatively incidental (this left-right thing is based on the intuitive notion that most developers think the first thing on a line is the most important thing):

     // Preferred (emphasizes the major goal — printing):
     std::cout << funct();
     
     // Not as good (emphasizes the minor goal — a function call):
     functAndPrintOn(std::cout);
    

    Now let's extend this idea to the ?: operator. Suppose your real goal is to print something, but you need to do some incidental decision logic to figure out what should be printed. Since the printing is the most important thing conceptually, we prefer to put it first on the line, and we prefer to bury the incidental decision logic. In the example code below, variable n represents the number of senders of a message; the message itself is being printed to std::cout:

     int n = /*...*/;   // number of senders
     
     // Preferred (emphasizes the major goal — printing):
     std::cout << "Please get back to " << (n==1 ? "me" : "us") << " soon!\n";
     
     // Not as good (emphasizes the minor goal — a decision):
     std::cout << "Please get back to ";
     if (n == 1)
       std::cout << "me";
     else
       std::cout << "us";
     std::cout << " soon!\n";
    

    All that being said, you can get pretty outrageous and unreadable code ("write only code") using various combinations of ?:, &&, ||, etc. For example,

     // Preferred (obvious meaning):
     if (f())
       g();
     
     // Not as good (harder to understand):
     f() && g();
    

    Personally I think the explicit if example is clearer since it emphasizes the major thing that's going on (a decision based on the result of calling f()) rather than the minor thing (calling f()). In other words, the use of if here is good for precisely the same reason that it was bad above: we want to major on the majors and minor on the minors.

    In any event, don't forget that readability is the goal (at least it's one of the goals). Your goal should not be to avoid certain syntactic constructs such as ?: or && or || or if — or even goto. If you sink to the level of a "Standards Bigot," you'll ultimately embarass yourself since there are always counterexamples to any syntax-based rule. If on the other hand you emphasize broad goals and guidelines (e.g., "major on the majors," or "put the most important thing first on the line," or even "make sure your code is obvious and readable"), you're usually much better off.

    Code must be written to be read, not by the compiler, but by another human being.

  • Rhamphoryncus (unregistered) in reply to Anon
    Anon:
     int n = /*...*/;   // number of senders
    

    // Preferred (emphasizes the major goal — printing): std::cout << "Please get back to " << (n==1 ? "me" : "us") << " soon!\n";

    // Not as good (emphasizes the minor goal — a decision): std::cout << "Please get back to "; if (n == 1) std::cout << "me"; else std::cout << "us"; std::cout << " soon!\n";

    The soon-to-be-maligned "third option":

    if (n == 1)
        who = "me";
    else
        who = "us";
    std::cout << "Please get back to " << who << " soon!\n";

    Or, since saving newlines is one of the major advantages of the ternary operator, you have this slightly obfuscated version:

    who = "us"; if (n == 1) who = "me";
    std::cout << "Please get back to " << who << " soon!\n";

    Oddly enough, Python 2.5 has a ternary operator (called a conditional expression), so the above could be written as:

    who = "me" if n == 1 else "us"
    print "Please get back to %s soon!" % who

    For any vaguely complicated code (such as using two ternary operators) I'm strongly in favour of refactoring into something else entirely. However, there are examples (such as what we've just given) that aren't so complicated. For such simple cases I don't know which method would produce the most readable code in the long run.

    Code must be written to be read, not by the compiler, but by another human being.
    Here here!
  • (cs)

    I'm surprised that my remark about eliminating the entire extended selection statement never got any mention.

    My initial thought was some sort of filter function, but that would be too alien to most C/Perl programmers :P, and besides, an even better idea occurred to me.

    Would anyone else here think that the Right Thing for this would actually have been a lookup table or similar data structure (Perl hash tables are nice for this) that would allow you to handle the whole thing in one (easily extended) fell swoop? Just wondering.

    Alternately, representing the different options as separate subclasses of a master controller class of some sort might work, pushing the whole problem into the underlying dispatch mechanism so the programmer can forget it entirely. It would probably be more work than is justified for the size of the problem, especially given what I recall of Perl's rather wonky OO support, and wouldn't really fit this particular problem well in any case, but it once it was done it would be a snap to maintain and extend.

  • anonymous guy (unregistered) in reply to Rhamphoryncus
    Rhamphoryncus:
    ... Here here!
    There there!
  • mneme (unregistered) in reply to clayne
    Are you high? Perl is in no way easier to read than C. It is WORSE.

    says someone who presumably doesn't know perl.

    No, it's not harder to read than C. Properly written Perl is much easier -- because it can do far more with the basic primitives than you can do in C. Yes, badly written perl is hard to read -- but then, so is badly written C.

    Compare, say:

    ($a,$mid,$b)  = $s =~ /(a+)(.*)(b+)/;
    

    with

    char *a=0;
    char *b=0;
    char *mid=0;
    int start = 0;
    int len = strlen(s);
    int state = 0;
    for(int i=0; i<len; i++) [
       switch(state) {
          0: if(start) {  if (s[i] != 'a') { 
                            state=1;
                            a=malloc(sizeof(char)*i-start+1);
                            for(my j=0; j<(i-start); j++) { a[j]='a' }
                            a[i-start+1]='\0';
                            start=0;
                          } else { break }
             } else { if(s[i] == 'a') { start=i; break };
          1: if(start) {
    ...
       }
    }
    </pre>
    
  • iw (unregistered)

    I remember I used to program a TI-86 like this, because it didn't have an 'if' operator (or I couldn't figure out what it was).

  • quamaretto (unregistered)

    Yeah... I didn't actually have the slightest problem understanding this. I guess it's a Lisp thing.

  • Derekwaisy (unregistered)
    Comment held for moderation.
  • Jimmyanten (unregistered)
    Comment held for moderation.

Leave a comment on “Turn it up to Eleven”

Log In or post as a guest

Replying to comment #:

« Return to Article