Tuesday, March 14, 2017

Functions

Functions

A function is a reusable portion of a program, sometimes called a procedure or subroutine.
  • Like a mini-program (or subprogram) in its own right
  • Can take in special inputs (arguments)
  • Can produce an answer value (return value)
  • Similar to the idea of a function in mathematics

Why write and use functions?

  • Divide-and-conquer
    • Can breaking up programs and algorithms into smaller, more manageable pieces
    • This makes for easier writing, testing, and debugging
    • Also easier to break up the work for team development
  • Reusability
    • Functions can be called to do their tasks anywhere in a program, as many times as needed
    • Avoids repetition of code in a program
    • Functions can be placed into libraries to be used by more than one "program"

With functions, there are 2 major points of view

  • Builder of the function -- responsible for creating the declaration and the definition of the function (i.e. how it works)
  • Caller -- somebody (i.e. some portion of code) that uses the function to perform a task

Using Functions

  • The user of a function is the caller.
  • Use a function by making calls to the function with real data, and getting back real answers.
  • Consider a typical function from mathematics:
      f(x) = 2x + 5
    
    In mathematics, the symbol 'x' is a placeholder, and when you run the function for a value, you "plug in" the value in place of x. Consider the following equation, which we then simplify:
      y = f(10)  // must evaluate f(10)
      y = 2 * 10 + 5 // plug in 10 for x
      y = 20 + 5
      y = 25  // so f(10) gives the answer 25
    
    In programming, we would say that the call f(10) returns the value 25.
     
  • C++ functions work in largely the same way. Format of a C++ function call:
      functionName(argumentList)
    
    where the argumentList is a comma-separated list of arguments (data being sent into the function). Use the call anywhere that the returned answer would make sense.
     
  • Example. There is a pre-defined math function called sqrt, which takes one input value (of type double) and returns its square root. Sample calls:
      double x = 9.0, y = 16.0, z;
    
      z = sqrt(36.0); // sqrt returns 6.0 (gets stored in z)
      z = sqrt(x);  // sqrt returns 3.0 (gets stored in z)
      z = sqrt(x + y); // sqrt returns 5.0 (gets stored in z) 
    
      cout << sqrt(100.0); // sqrt returns 10.0, which gets printed
    
      cout << sqrt(49);     // because of automatic type conversion rules
       //  we can send an int where a double is expected
       //  this call returns 7.0
    
      // in this last one, sqrt(625.0) returns 25.0, which gets sent as the
      //  argument to the outer sqrt call.  This one returns 5.0, which gets
      //  printed
    
      cout << sqrt(sqrt(625.0)); 
    
    this code here:
    // Online Taleem
    // 
    // Simple set of function call examples using the cmath library function
    //  sqrt()
    
    #include 
    #include 
    
    using namespace std;
    
    int main()
    {
      double x = 9.0, y = 16.0, z;
    
      cout.setf(ios::fixed);
      cout.precision(1);
    
      z = sqrt(36.0); // sqrt returns 6.0 (gets stored in z)
      cout << "z = " << z << '\n';
    
      z = sqrt(x);  // sqrt returns 3.0 (gets stored in z)
      cout << "z = " << z << '\n';
    
      z = sqrt(x + y); // sqrt returns 5.0 (gets stored in z) 
      cout << "z = " << z << '\n';
    
      // sqrt returns 10.0, which gets printed
      cout << "Square root of 100: " << sqrt(100.0) << '\n'; 
    
      // because of automatic type conversion rules we can send an int where a 
      //  double is expected.  This call returns 7.0
      cout << "sqrt(49) returns " << sqrt(49) << '\n';     
    
      // in this last one, sqrt(625.0) returns 25.0, which gets sent as the
      //  argument to the outer sqrt call.  This one returns 5.0, which gets
      //  printed
      cout << "sqrt(sqrt(625.0)) = " << sqrt(sqrt(625.0)) << '\n'; 
    
      return 0;
    
    
    }
  • In keeping with the "declare before use" policy, a function call can be made ONLY if a declaration (or definition) of the function has been seen by the compiler first.
    • This can be done by placing a declaration above the call
    • This is handled in libraries by including the header file for the library with a #include directive

Predefined Functions

  • There are many predefined functions available for use in various libraries.
    • These typically include standard libraries from both C and C++
    • These may also include system-specific and compiler-specific libraries depending on your compiler
    • Typically, C libraries will have names that are prefixed with the letter 'c'. (cmath, cstdlib, cstring)
  • To make such functions available to a program, the library must be included with the #include directive at the top of your file. Examples:
      #include <iostream>  // common I/O routines
      #include <cmath>  // common math functions
      #include <cstdlib>  // common general C functions
    

Building Functions

The builder of a function (a programmer) is responsible for the declaration (also known as prototype) and the definition.

Declaring a Function

  • A function declaration, or prototype, specifies three things:
    • the function name -- usual naming rules for user-created identifiers
    • the return type -- the type of the value that the function will return (i.e. the answer sent back)
    • the parameter list -- a comma separated list of parameters that the function expects to receive (as arguments)
      • every parameter slot must list a type (this is the type of data to be sent in when the function is called)
      • parameter names can be listed (but optional on a declaration)
      • parameters are listed in the order they are expected
  • Declaration Format:
      return-type function-name( parameter-list );
    
  • Examples:
      // GOOD function prototypes
      int Sum(int x, int y, int z); 
      double Average (double a, double b, double c);
      bool InOrder(int x, int y, int z);
      int DoTask(double a, char letter, int num);
    
      double Average (double, double, double);    // Note: no parameter names here
               //       okay on a declaration
    
      // BAD prototypes (i.e. illegal) 
      double Average(double x, y, z);  // Each parameter must list a type 
      PrintData(int x);                // missing return type 
      int Calculate(int)               // missing semicolon 
      int double Task(int x);    // only one return type allowed!
    

Defining a Function

  • a function definition repeats the declaration as a header (without the semi-colon), and then adds to it a function body enclosed in a block
    • The function body is actual code that is implemented when the function is called.
    • In a definition, the parameter list must include the parameter names, since they will be used in the function body. These are the formal parameters
  • Definition Format:
      return-type function-name( parameter-list ) 
      { 
         function-body (declarations and statements) 
      } 
    
  • To send the return value out, use the keyword return, followed by an expression that matches the expected return type
      return expression;
    
  • Definition Examples:
      int Sum(int x, int y, int z)
      // add the three parameters together and return the result
      {
         int answer;
         answer = x + y + z;
         return answer;
      }
     
      double Average (double a, double b, double c)
      // add the parameters, divide by 3, and return the result
      {
         return (a + b + c) / 3.0;
      }
    
  • More than one return statement may appear in a function definition, but the first one to execute will force immediate exit from the function.
      bool InOrder(int x, int y, int z)
      // answers yes/no to the question "are these parameters in order,
      //  smallest to largest?"  Returns true for yes, false for no.
      {
         if (x <= y && y <= z)
     return true;
         else
     return false;
      }
    
  • Sample code using the functions above:
    • Example 1 -- this file uses a layout that works, but is not as desirable

    •  #include 
      using namespace std;
      
      double Average(double, double, double);
      
      int i1, i2, i3;   // global variables
      double x;   // global variable (bad idea)
      
      int main()
      {
         double a, b, c; // local variables to main()
         double x, y, z; // local variables to main()
         //  NOT the same x, y, z as in the Average function
         //  and NOT the same x as the global variable x
      
         a = 1.3;   b = 2.5;   c = 6.8;
      
         // arguments in a function call do NOT have to have the same
         //  names as formal function parameters
      
         x = Average(a, b, c); // local x (to main()) being used here
         cout << "average = " << x << '\n';
      
         return 0;
      }
      
      double Average(double x, double y, double z)
      // the variables x, y, and z are LOCAL variables to this function
      //  NOT the same as the global x.
      {
         double total;  // another LOCAL variable
         total = x + y + z;  // local x being used here
         return (total / 3.0);
      }
      
    • Example 2 -- this file illustrates a better layout for satisfying declare-before-use

    •  // Online Taleem -- functions2.cpp
      //
      // Example illustrating some function declarations, definitions, calls
      //
      // Better layout than functions1.cpp.  This one has prototypes at the top
      
      #include 
      using namespace std;
      
      // function PROTOTYPES listed here.  
      
      int Sum(int x, int y, int z);
      double Average (double a, double b, double c); 
      bool InOrder(int x, int y, int z); 
      
      // The prototypes above satisfy declare-before-use, so these functions can
      //  be CALLED anywhere below this point.
      
      int main()
      {
         int i1, i2, i3;
         double d1, d2, d3;
      
         cout << "Input three integers: ";
         cin >> i1 >> i2 >> i3;
         cout << "Input three doubles: ";
         cin >> d1 >> d2 >> d3;
      
         // samples of function calls
      
         double avg;
         int total;
         total = Sum(i1, i2, i3);
         avg = Average(d1, d2, d3);
      
         cout << "The sum of the three integers = " << total << '\n';
         cout << "The average of the three doubles = " << avg << '\n';
       
      
         // note that we can place the call in the cout statements, which 
         //  prints the return values
      
         cout << "The sum of 10, 14, and 18 = " << Sum(10, 14, 18) << '\n';
      
         cout << "The average of 1.3, 2.7, and 6.9 = " 
          << Average(1.3, 2.7, 6.9) << '\n';
      
      
         // Notice that we can pass in integers where doubles are expected
         //   legal via automatic-type-conversion rules
      
         cout << "The average of the three integers = " 
          << Average(i1, i2, i3) << '\n';
      
      
         // Testing out the boolean function (InOrder)
      
         if (InOrder(i1, i2, i3))
       cout << "The three integers were typed in ascending order\n";
         else
       cout << "The three integers were NOT typed in ascending order\n";
      
      
         return 0;
      }
      
      // function definitions can appear in any order, since the prototypes
      //  were listed at the top to handle declare-before-use
      
      int Sum(int x, int y, int z)
      // add the three parameters together and return the result
      {  
         int answer;
         answer = x + y + z;
         return answer;
      }
       
      double Average (double a, double b, double c)
      // add the parameters, divide by 3, and return the result
      {
         return (a + b + c) / 3.0;
      }
      
      bool InOrder(int x, int y, int z)
      // answers yes/no to the question "are these parameters in order,
      //  smallest to largest?"  Returns true for yes, false for no.
      //  (This kind of function often known as a "predicate function")
      {
         if (x <= y && y <= z)
       return true;
         else
       return false;
      } 

Scope of Identifiers

  • The scope of an identifier (i.e. variable) is the portion of the code where it is valid and usable
  • A global variable is declared outside of any blocks, usually at the top of a file, and is usable anywhere in the file from its point of declaration.
    • "When in doubt, make it global" == BAD PROGRAMMING PRACTICE
    • Best to avoid global variables (except for constants, enumerations. Sometimes)
    • Function names usually global. (prototypes placed at the top of a file, outside any blocks)
  • A variable declared within a block (i.e. a compound statement) of normal executable code has scope only within that block.
    • Includes function bodies
    • Includes other blocks nested inside functions (like loops, if-statements, etc)
    • Does not include some special uses of block notation to be seen later (like the declaration of a class -- which will have a separate scope issue)
  • Variables declared in the formal parameter list of a function definition have scope only within that function.
    • These are considered local variables to the function.
    • Variables declared completely inside the function body (i.e. the block) are also local variables

void functions and empty parameter lists

  • Parameter lists
    • Mathematical functions must have 1 or more parameters
    • C++ functions can have 0 or more parameters
    • To define a function with no parameters, leave the parintheses empty
    • Same goes for the call. (But parintheses must be present, to identify it as a function call)
  • Return types
    • A mathematical function must return exactly 1 answer
    • A C++ function can return 0 or 1 return value
    • To declare a function that returns no answer, use void as the return type
    • A void function can still use the keyword return inside, but not with an expression (only by itself). One might do this to force early exit from a function.
    • To CALL a void function, call it by itself -- do NOT put it in the middle of any other statement or expression
  • Sample declarations:
      char GetALetter();    // no parameters
      void PrintQuotient(int x, int y);  // void return type
      void KillSomeTime();    // both
    

Functions and the compiler

  • The reason for the declare-before-use rule is that the compiler has to check all function CALLS to make sure they match the expectations.
    • the "expectations" are all listed in a function declaration
    • function name must match
    • arguments passed in a call must match expected types and order
    • returned value must not be used illegally
  • Decisions about parameters and returns are based on type-checking.
    • legal automatic type conversions apply when passing arguments into a funcion, and when checking what is returned against the expected return type

No comments:

Post a Comment