Simplifying Loops 

and Logic 



Trying minimize 


the “mental baggage” of your code

 





Making Control Flow Easy to Read 


KEY IDEA

Make all your conditionals, loops, and other changes to control flow as “natural” as possible



1. The Order of Arguments in Conditionals 




 if (length > 10)  Vs  if (10 < length)


whichis more readable?



Left-hand side 

Right-hand side 

The expression “being interrogated,” whose value is more in flux. 

The expression being compared against, whose value is more constant. 



2. The Order of if/else Blocks


 if (a == b) {
     // case one
 } else {
     // case two
 }


 if (a != b) {
     // case two
 } else {
     // case one
 }



Prefer dealing?


  1. positive case first instead of the negative
  • e.g., if (debug) instead of if (!debug)
  • simpler case first to get it out of the way
  • more interesting or conspicuous case first
  •  
      if (!url.HasQueryParameter("expand_all")) {
          response.Render(items);
          ...
      } else {
          for (int i = 0; i < items.size(); i++) {
              items[i].Expand();
          }
      }


    Don't think a pink elephant


    yep! expanding_all is our pink elephant
    because it's more interesting and positive


    but  sometimes 

    negative case is the simpler and more interesting/dangerous one 


     if (not file) {
         Log the error
     } else {
         // ...
     }
    



    3. Avoid do/while Loops




    do {
    if(node.name().equals(name)) return true; node = node.next(); } while (node !== null && --max_length > 0)

    Avoid like above!!

    
    while (node != null && max_length-- > 0) {
      if (node.name().equals(name)) return true;
      node = node.next();
    }
    

    We should dealing like above!

    Bjarne Stroustrup, the creator of C++, says it best 

    In my experience, the do-statement is a source of errors and confusion. ... I prefer the condition

    “up front where I can see it.” Consequently, I tend to avoid do-statements. 



    4. Returning Early from a Function





    
     function foo(str) {  if (typeof str !== 'string') return false;  body statment... }
    



    4. Minimize Nesting



    Each level of nesting pushes an extra condition onto the reader’s “mental stack.  


    
      if (user_result === SUCCESS) {    if (permission_result != SUCCESS) {
          reply.WriteErrors("error permission");
          reply.Done();
          return;
        }
        reply.WriteErrors("");
      } else {
        reply.WriteErrors(user_result);
      }
      reply.Done();        
    

     


    Mental stack?



    its's disadvantage:
    • we have to keep values of  user_result  and  permission_result  in head at all times
    • as each if { } block closes, you have to toggle the corresponding value in your mind



    Key Idea

    Look at your code from a fresh perspective when you’re making changes. Step back and look at it as a whole. 





    How to make this more readble?

    Removing Nesting by Returning Early 

      if (user_result != SUCCESS) {
        reply.WriteErrors(user_result);
        reply.Done();
        return;
     }
     if (permission_result != SUCCESS) {
        reply.WriteErrors(permission_result);
        reply.Done();
        return;
     }
      reply.WriteErrors("");
     reply.Done();

    but sometimes it doesn't work...like below

    
        	  for (var i = 0; i < results.size(); i++) {
                   if (results[i] !== NULL) {
                       non_null_count++;
                       if (results[i].name !== "") {
                            console.log("Considering candidate...");
                            ...
                        } 
                   }
               }
    					
    

    Solutions

    
        	  for (var i = 0; i < results.size(); i++) {
                   if (results[i] === NULL) continue;
                   non_null_count++;
    
                   if (results[i].name !== "") continue;
                   console.log("Considering candidate...");
                            ...
               }
    					
    





    Breaking Down Giant Expressions 



    KEY IDEA

    Break down your giant expressions into more digestible pieces.




    so what's degestible pieces?

    1.Explaining Variables 

     if (line.split(':')[0].strip() == 'root') {
         ...
    }


    using explaining varibles:

     var username = line.split(':')[0].strip(); if (username == 'root') {
         ...
     } 
    


    2.Summary Variables 

     if (request.user.id === document.owner_id) { // can edit the doc }
         ...
    if (request.user.id !== document.owner_id) { // can't ... }


    using summary varibles:

     var canEditable = request.user.id === document.owner_id; if (canEditable) { ... }     ...
     if (!canEditable) { ... }


    3.Morgan's Laws

            1) not (a or b or c) (not a) and (not b) and (not c)
            2) not (a and b and c)  (not a) or (not b) or (not c)

    Factor out the 'not'!
    examples:

          
            if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");
            // after change 
            if (!file_exists || is_protected) Error("Sorry, could not read file.");
            


    4.Abusing short-circuit logic


     
      assert((!(bucket = FindBucket(key))) || !bucket.IsOccupied());
         

    Readable?If not, how?


    solution!

              var bucket = FindBucket(key);    if (bucket !== null) Assert(!bucket.IsOccpied());
    
            





    Example:    Wrestling with Complicated Logic


    Suppose you’re implementing the following Range class:

          struct Range {          int begin;          int end;          // For example, [0, 5) overlaps with [3, 8)          bool OverlapsWith(Range other);      }





    Some solution...but is it the best? 

    
     bool OverlapsWith(Range other) {    // Check if 'begin' or 'end' falls inside 'other'    return (begin >= other.begin && begin <= other.end) ||            (end >= other.begin && end <= other.end) ||            (begin <= other.begin && end >= other.end);  }   


    A more Elegant  Approach!

     bool OverlapsWith(Range other) {    if (other.end <= begin) return false; // It end before we begin    if (other.begin >= end) return false; // It begin after we end    return true; // Only possibility left: they overlap  }   


    5.Breaking Down Giant Statements

      var updateHighlight = function (msg_num) {     if ($("#vote_value" + msg_num).html() === "UP") {        $("#thumbs_up" + message_num).addClass("highlighted");        $("#thumbs_down" + message_num).removeClass("highlighted");
    } else if ($("#vote_value" + msg_num).html() === "DOWN") { $("#thumbs_up" + message_num).removeClass("highlighted");
    $("#thumbs_down" + message_num).addClass("highlighted");
    } else { $("#thumbs_up" + message_num).removeClass("highlighted");
    $("#thumbs_down" + message_num).removeClass("highlighted");
    } }



    Don't Repeat Yourself

      var updateHighlight = function (msg_num) {      var thumbs_up = $("#thumbs_up" + message_num);      var thumbs_down = $("#thumbs_down" + message_num);
    var vote_value = $("#vote_value" + msg_num).html(); var hi = "highlighted" if (vote_value === "UP") { thumbs_up.addClass(hi); thumbs_down.removeClass(hi);
    } else if (vote_value === "DOWN") { thumbs_up.removeClass(hi);
    thumbs_down.addClass(hi);
    } else { thumbs_up.removeClass(hi);
    thumbs_down.removeClass(hi);
    } }







    Variables and Readability 



    PROBLEMS:

    1. The more variables there are, the harder it is to keep track of them all.

    2. The bigger a variable's scope, the longer you have to keep track of it.

    3. The more often a variable changes, the harder it is to keep track of its current
         value. 




    HOW?




    1. Eliminating Variables


    2.Shrink the Scope of Your Varibles

    1. Eliminating Variables


    Useless Temporary Variables

        var now = datetime.now();    root_msg.last_view_time = now;
    
    


        var root_msg.last_view_time = datetime.now();


    Eliminating Intermediate Results



    var remove_one = function (array, value_to_remove) {     var index_to_remove = null;
        for (var i = 0; i < array.length; i += 1) {
            if (array[i] === value_to_remove) {            index_to_remove = i;
               break;
            }    }
        if (index_to_remove !== null) {        array.splice(index_to_remove, 1);
        } };  
    





    index_to_remove ?
    solution

    var remove_one = function (array, value_to_remove) {     for (var i = 0; i < array.length; i += 1) {
            if (array[i] === value_to_remove) {            array.splice(i, 1);
               return;
            }    }
    };


    Eliminating Control Flow Variables

        var done = false;
        while (/* condition */ && !done) {
            ...
            if (...) {
                done = true;
                continue;         }
        }
    




    Variables like ‘done’ are 

    what we call “control flow variables.” 


    Their sole purpose is 

    to steer the program’s execution 


    Better solution

     
        while (/* condition */) {
            ...
            if (...) {
                break;         }
        }
    







    But how if in complicated cases like that?







    moving code into a new function
    (either the code inside the loop, or the entire loop itself)

    2. Shrink the Scope of Your Variables


    KEY IDEA

    Make your variable visible by as few lines of code as possible.

    problem

        class LargeClass {         string str_;
            void Method1() {             str_ = ...;
                Method2();         }
            void Method2() {         // Uses str_
            }
            // Lots methods that don't use str_ ...     };
    







    The fewer mini-globals, the better.
    ( it’s hard to keep track of all the member variables)

    solution

        class LargeClass {         void Method1() {             str_ = ...;
                Method2(str_);         }
            void Method2() {         // Uses str_
            }
            // Now other methods can't see str_     };
    


    Creating “Private” Variables in JavaScript

    
        submitted = false; // Note: global variable
        var submit_form = function (form_name) {         if (submitted) {
                return;  // don't double-submit the form
            }
            ...
            submitted = true;     };
    
    

    Solution

    
        var submit_form = (function () {         submitted = false; 
    return function (form_name) { if (submitted) { return; } ... submitted = true;  } }());


    Moving Definitions Down

    
       function test () {       var test_1,           test_2,           test_3,           test_4;
    ... useTest_1(test_1); }







    Mental Baggage
    (it forced the reader to think about all those variables right away)


    Moving Definitions Down

    
       function test () {           var test_2,           test_3,           test_4;
    ... var test_1; useTest_1(test_1); }







    thx
    Made with Slides.com