Hello C# 007

"A good designer must rely on experience, on precise, logic thinking; and on pedantic exactness. No magic will do." – Niklaus Wirth (maybe)

Functions

Code reuse is about reducing the amount the code you need to write while you solve the same problem.
Functions (procedures, methods, subroutines and so on) enables using the idea of the stack as a way to make individual pieces of code work independent of memory location.
They are comprised of a series of statements that receive arguments may yield a value.

This is achieved by using the stack to define the local/temporary variables, value to return and instruction to continue from.
Our plan is to implement functions using what we have before using proper C# functions.

Structure

We are going to reorganize our memory structure to properly utilize functions:

And the code is going to follow a predefined structure:

// initialization
var m = new int[512];
m[0] = 1; // main function name
m[1] = 0; // empty stack
m[511] = 0; // internal function step
// if m[0] == 0 the program exits
while(m[0] != 0)
{
if(m[0] == X) // X is the function 'name'
{
// function code
}
// add other functions here
}

We need to define a interface between function calls:

Sample

This is an example of a code that implement he Collatz Conjecture. First we outline the functions:
// not code, just a draft
Main()
{ // name = 1
number = GetNumber()
steps = Collatz(number)
Print(number)
}

GetNumber()
{ // name = 2
return int.Parse(System.Console.ReadLine());// return number typed in the terminal
}

Collatz(number)
{ // name = 3
steps = 0 // notice this step is unrelated to main's step
while(number > 1)
{
if number is even: number = number / 2
else number = 3*number + 1;
steps++
Print(number) }
return steps;
}

Print(number)
{ // number = 4
System.Console.WriteLine(number)
}

Let's map this code into a console application (it is alright to not understand everything):

// Program.cs
var m = new int[512];
m[0] = 1; // main function
m[1] = 0; // empty stack
m[511] = 0; // internal step
while(m[0] != 0)
{
if(m[511] == -1)
{
m[511] = m[1 + m[1]--]; // used to return from functions
}

// main
if (m[0] == 1)
{

if (m[511] == 0)
{
m[1 + (++m[1])] = 1; // push next internal step
m[1 + (++m[1])] = 1; // this function `name`
m[0] = 2; m[511] = 0; // call GetNumber
}
else if (m[511] == 1)
{ // stack = [number]
m[1 + (++m[1])] = 2; // push next internal step
m[1 + (++m[1])] = 1; // this function `name`
// stack = [number, nis, this]
m[1 + (++m[1])] = m[1 + m[1] - 3]; // push list reference
m[0] = 3; m[511] = 0; // call Collatz
}
else if (m[511] == 2)
{ // stack = [number, steps]
m[1 + (++m[1])] = 3; // push next internal step
m[1 + (++m[1])] = 1; // this function `name`
m[1 + (++m[1])] = m[1 + m[1] - 3]; // push steps
m[0] = 4; m[511] = 0; // call print
}
else if (m[511] == 3)
{ // stack = [number, steps]
m[1]--; m[1]--; // pop values
m[0] = 0; // exit
}
}
else // GetNumber
if (m[0] == 2)
{
m[0] = m[1 + m[1]]; // restore m[0];
m[511] = -1; // unset internal step;

m[1 + m[1]] = m[1 + m[1] - 1]; // swap next step;
m[1 + m[1] - 1] = int.Parse(System.Console.ReadLine() ?? "0"); // set return value;
}
else // Collatz
if (m[0] == 3)
{
if (m[511] == 0)
{
// stack = [... nstep, oldfun, number]
m[1 + (++m[1])] = 0; // push our temp `steps` into stack
m[511] = 1; // enter while
}

if (m[511] == 1)
{
if (m[1 + m[1] - 1] <= 1) // exit loop
{
m[511] = 2;
}
else
{
if (m[1 + m[1] - 1] % 2 == 0)
m[1 + m[1] - 1] /= 2; // x /= y is shorthand for x = x / y;
else
m[1 + m[1] - 1] = m[1 + m[1] - 1] * 3 + 1;
m[1 + m[1]]++; // steps ++

// function call
m[1 + (++m[1])] = 1; // push next internal step
m[1 + (++m[1])] = 3; // this function `name`
m[1 + (++m[1])] = m[1 + m[1] - 4]; // push number
m[0] = 4; m[511] = 0; // call print
}
}

if (m[511] == 2)
{
// stack = [... nstep, oldfun, number, steps]
m[0] = m[1 + m[1] - 2]; // restore m[0];
m[511] = -1; // unset internal step;

m[1 + m[1] - 2] = m[1 + m[1] - 3]; // swap next step;
m[1 + m[1] - 3] = m[1 + m[1]]; // set return value;

m[1] -= 2; // pop number and steps;
}
}
else // Print
if(m[0] == 4)
{
System.Console.WriteLine(m[1 + m[1]--]); // print and pop value

m[0] = m[1 + m[1]]; // restore m[0];
m[511] = -1; // unset internal step;

m[1]--; // pop oldthis
}
}

100 lines of very obscure code, don't you think? Luckily we think alike and hardly anyone programs like that.
But there are some important bits to highlight:

So instead of coding with pointers, we use variables which have a name and the compiler latter on maps to pointers in the memory.

An important bit is that data can be much more complex that what we are working with, so variables also have types assigned to it. You may use `var <name> = <value>;` to create a variable with the type being inferred by the compiler and `<type> <name>;` to create a variable with an explicit type.

Syntax

Functions have the following syntax:

<return type> <name> (<argument type> <argument name>(, <repeat for extra arguments>)
{
...code goes here
return <expression>; <- exits and return value, does not need to be the last statement
}

// or with just a return expression
<return type> <name> (<argument type> <argument name>(, <repeat for extra arguments>) => <expression>;

And here is the same program as before, but written with functions and variables (finally we are free of the memory variable):

var number = GetNumber();
var steps = Collatz(number);
Print(steps);

int GetNumber() => int.Parse(System.Console.ReadLine() ?? "0");

int Collatz(int number)
{
var steps = 0;
while (number > 1)
{
if (number % 2 == 0)
number /= 2;
else
number = number * 3 + 1;
steps++;
Print(number);
}
return steps;
}

// function without return type use the void type
void Print(int number) => System.Console.WriteLine(number)

1/4 of the code and a much easier solution to reason about is always welcome. But understanding that under the hood things are mapped into a stack is important to properly find what happens when somethings goes wrong.

[Try by yourself] Try to implement a program that reads a number i and returns the i-th Fibonacci number.

[Try by yourself] Download an IDE like Visual Studio (the non Code). It is going to become more useful as the projects get bigger and more complex.

Up Next

Bubble sort.


Go home.

☆Powered by Azure Blob