Monday, January 30, 2017

D - Functions

Basic function definition

return_type function_name( parameter list )
{
   body of the function
}
A basic function definition consists of a function header and a function body. Here are all the parts of a function:

  • Return Type: A function may return a value. The return_type is the data type of the value the function returns. Some functions perform the desired operations without returning a value. In this case, the return_type is the keyword void.
  • Function Name: This is the actual name of the function. The function name and the parameter list together constitute the function signature.
  • Parameters: A parameter is like a placeholder. When a function is invoked, you pass a value to the parameter. This value is referred to as actual parameter or argument. The parameter list refers to the type, order, and number of the parameters of a function. Parameters are optional; that is, a function may contain no parameters.
  • Function Body: The function body contains a collection of statements that define what the function does.

Calling a Function

We can a call a function using the following syntax
function_name(parameter_values)

Function Types

D programming supports a wide range of functions and they are listed below.
  • Pure Functions
  • Nothrow Functions
  • Ref Functions
  • Auto Functions
  • Variadic Functions
  • inout Functions
  • Property Functions
The various functions are explained below.

Pure Functions

Pure functions are functions which cannot access global or static, mutable state save through their arguments. This can enable optimizations based on the fact that a pure function is guaranteed to mutate nothing which isn't passed to it, and in cases where the compiler can guarantee that a pure function cannot alter its arguments, it can enable full, functional purity (i.e. the guarantee that the function will always return the same result for the same arguments).
import std.stdio;
int x = 10;
immutable int y = 30;
const int* p;

pure int purefunc(int i,const char* q,immutable int* s)
{
   //writeln("Simple print"); //cannot call impure function 'writeln'

   debug writeln("in foo()"); // ok, impure code allowed in debug statement
   // x = i;  // error, modifying global state
   // i = x;  // error, reading mutable global state
   // i = *p; // error, reading const global state

   i = y;     // ok, reading immutable global state
   auto myvar = new int;     // Can use the new expression:
   return i;
}

void main()
{
   writeln("Value returned from pure function : ",purefunc(x,null,null));
}
When we run the above program, we will get the following output.
Value returned from pure function : 30

Nothrow Functions

Nothrow functions do not throw any exceptions derived from class Exception.Nothrow functions are covariant with throwing ones.
nothrow guarantees that a function does not emit any exception.
import std.stdio;

int add(int a, int b) nothrow
{
   //writeln("adding"); This will fail because writeln may throw
   int result;

   try {
      writeln("adding"); // compiles
      result = a + b;
   } 

   catch (Exception error) { // catches all exceptions
   }

   return result;
}

void main()
{
   writeln("Added value is ", add(10,20));
}
When we run the above program, we will get the following output.
adding
Added value is 30

Ref Functions

Ref functions allow functions to return by reference. This is analogous to ref function parameters.
import std.stdio;

ref int greater(ref int first, ref int second)
{
   return (first > second) ? first : second;
}

void main()
{
   int a = 1;
   int b = 2;

   greater(a, b) += 10;  
   writefln("a: %s, b: %s", a, b);  
}
When we run the above program, we will get the following output.
a: 1, b: 12

Auto Functions

Auto functions can return value of any type. There is no restriction on what type to be returned. A simple example for auto type function is given below.
import std.stdio;

auto add(int first, double second)
{
   double result = first + second;
   return result;
}

void main()
{
   int a = 1;
   double b = 2.5;

   writeln("add(a,b) = ", add(a, b));
}
When we run the above program, we will get the following output.
add(a,b) = 3.5

Variadic Functions

Variadiac functions are those functions in which the number of parameters for a function is determined in runtime. In C, there is a limitation of having atleast one parameter. But in D programming, there is no such limitation. A simple example is shown below.
import std.stdio;
import core.vararg;

void printargs(int x, ...) {

   for (int i = 0; i < _arguments.length; i++)
   {

      write(_arguments[i]);

      if (_arguments[i] == typeid(int))
      {
         int j = va_arg!(int)(_argptr);
         writefln("\t%d", j);
      }

      else if (_arguments[i] == typeid(long))
      {
         long j = va_arg!(long)(_argptr);
         writefln("\t%d", j);
      }

      else if (_arguments[i] == typeid(double))
      {
         double d = va_arg!(double)(_argptr);
         writefln("\t%g", d);
      }

   }
}

void main() 
{
   printargs(1, 2, 3L, 4.5);
}
When we run the above program, we will get the following output.
int 2
long 3
double 4.5

inout functions

The inout can be used both for parameter and return types of functions. It is like a template for mutable, const, and immutable. The mutability attribute is deduced from the parameter i.e. inout transfers the deduced mutability attribute to the return type. A simple example showing how mutability gets changed is shown below.
import std.stdio;

inout(char)[] qoutedWord(inout(char)[] phrase)
{
   return '"' ~ phrase ~ '"';
}

void main()
{
   char[] a = "test a".dup;

   a = qoutedWord(a);
   writeln(typeof(qoutedWord(a)).stringof," ", a);

   const(char)[] b = "test b";
   b = qoutedWord(b);
   writeln(typeof(qoutedWord(b)).stringof," ", b);

   immutable(char)[] c = "test c";
   c = qoutedWord(c);
   writeln(typeof(qoutedWord(c)).stringof," ", c);
}
When we run the above program, we will get the following output.
char[] "test a"
const(char)[] "test b"
string "test c"

Property Functions

Properties allow using member functions like member variables. It uses the @property keyword. The properties are linked with related function that return values based on requirement. A simple example for property is shown below.
import std.stdio;

struct Rectangle
{
   double width;
   double height;

   double area() const @property
   { 
      return width*height; 
   }

   void area(double newArea) @property
   { 
      auto multiplier = newArea / area;
      width *= multiplier;
      writeln("Value set!"); 
   }
}

void main()
{
   auto rectangle = Rectangle(20,10);
   writeln("The area is ", rectangle.area);

   rectangle.area(300);
   writeln("Modified width is ", rectangle.width);
}
When we run the above program, we will get the following output.
The area is 200
Value set!
Modified width is 30

No comments:

Post a Comment