Writing an effective Résumé

Hello World! Hope it’s going well for you guys. As the blog title reads, this write-up is about some simple ways to make an effective resume. Personally, I have been on both the sides- the applicant as well as the interviewer. So, I can say that I have a fair idea of what would make a good resume :). The aim of this blog is to share what I think may be helpful to you guys. So, without further ado let’s start decoding easy ways to make a better resume.


I. Use a good editing tool

Let’s start with the basics ! This is the first thing that made my resume look and feel 100 times better.  Earlier I would use Microsoft word for my editing. And, psst, I sucked at it! I do not know the shortcuts to getting the right boundaries, page offsets and also getting alignments I want. (Sure, judge me all you want! :D). Then I started using online editors (such as overleaf) that allow me to build resume using Latex.

What better way than to “code” your resume ;). This trick has been so useful. Sure, it needs a little more effort than word. But, the outcome is worth the effort. It is easy to use them, they allow for anytime-anywhere access, since they store your profile online.

There are many templates available. I would say – do check them out~ .I have added my template picks in case you guys decide to give it a try (at the end of this blog). Let’s now focus on what should be the content, in the following two tricks.


II. Focus on your strengths and interests

This tricks needs your answer to two important questions. Firstly, what you are confident about and secondly how is it relevant to what you want out of this job.

One way to finding answer for these is, to write down what is it that you enjoy and are confident about along with how this can be aligned with the job requirements (the job that you are applying for).

For example – it can be programming in Python or Java or may be you have a good understanding of machine learning algorithms or you love to design new algorithms. Whatever it is, just write down on a paper first.

Then, begin to build your resume around this topic of interest. What is your relevant experience or how have you prepared for a career in it (do you have hobby projects, coursework etc), try and list them. Keep the focus around this through your resume.

Why? Because, the content on your resume to some extent dictates what you may be questioned about. So, when you focus on your strengths while adding content, it automatically translates to confidence in handling the questions thrown at you, during the course of your interview.


III. Avoid this !

I understand the urge to stand out, with your resume. I also understand the pressure of a job search. But, I cannot stress here enough that you should put things in your resume that is truly your work. It’s not too difficult for a person in the industry to identify when you blatantly lie or go overboard on exaggerating. Do not write about things that you have not done or do not understand completely. And, when you do put up impressive numbers or things you have done, you should be able to explain as to how you achieved that. Else, this simply adds unnecessary pressure when you are at the interview, in case you are asked about it.

For example, if you want to write about how you improved the reliability of a software by 15%, then you should be ready to justify how you achieved that number. Just throwing some metrics in the resume is not going to take you a long way. So, focus on what you have done and be prepared to answer how you achieved those numbers as well.


Few more good points I can think of :

  • Put your educational qualification (if experienced, add the highest level degree)
  • Keep your sentences short and effective (long sentences make the reader lose interest)
  • Put other work experience or projects that are not directly relevant to the job you are seeking in a separate section (For example – “Additional Experience“)
  • If you are sharing your resume online, you could add clickable links to your website or project pages, conference journals etc, throughout your resume
  • You could also mention about interpersonal skills (good team player etc). But, also be prepared to prove it or provide examples if needed

And finally proof read, also get it reviewed before using it !

Good luck !


Templates

Have selected two templates that you could start with, as listed below :

Template 1  Recent College graduate resume

  • contains all the information needed – fits into one page and is also not cluttered
  • The sections seem sufficient and the content is crisp

Template 2 Experienced candidate resume

  • clearly lists all the prior experience in a concise format

When you open the template with an overleaf account, it opens up the display to edit the contents. Left view has the Latex code that can be edited and the right side shows the output resume document, as in the snapshot below.

overleaf_snapshot

And, that’s all I had for this tidbit. Hope these simple tricks helped you guys make a better resume. As always let me know what you feel. You can reach me at decodergirlblog@gmail.com.

Note : This is not sponsored. Overleaf was useful to me and hence I am sharing info about it.

 

Char array versus string

Today’s tidbit is a very “basic” one. It’s about the difference between char array and string in C plus plus. If you have started with a low level language, this is a real deal ! Read on, to understand what this means. In short – the question we will try and answer today is : “Is there any difference between the two options below :”

string input= "Geekgirl"; //input is a string variable
char input[]="Geekgirl"; // input is a char array

Char Arrays

As suggested by the name char arrays are an array of “characters”. An example is as shown below. These are by default supported in C and C++. Strings are represented or implemented as char arrays that are null terminated in C language. Char arrays are hence also called the “c-strings“.

chararr
Fig 1. Char array with null terminated character  ‘\0’ (c-string)
// Usage of char arrays
//A. Declare char array
char input_text[10]; 
//Assign value via command line input
cin.getline(input_text,10);

//B. Declare char array and assign value
char input_text[]={'G','e','e','k','\0'}; 

//C. Declare and assign value using double quote enclosed literal constants
//Literal constants are by automatically null terminated (String literals)
char input_text[]="Geek";

//D. using char*
char* input_text_ptr = "Geek";

Since above are char array declarations, all rules of a regular array apply to them. They cannot be assigned a value once they have been declared.

//Once input_text is declared and assigned a value as in B and C options 
above, the following assignment operations are invalid:
input_text ="Geek"; // not allowed
input_text[]="Geek"; //not allowed
input_text ={'G','i','r','l','\0'}; // not allowed

//But, they can be edited using indexing into individual elements as below:
//valid assignments listed below.
input_text[0]="G";
input_text[1]="i";
input_text[2]="r";
input_text[3]="l";

// But, when declared using char* (pointer to character) as in case D above: :
*(input_text_ptr+0) ='D'; // not allowed. 
// But, if we allocate memory 
// and then assign a value, then we can assign char value as above:
char *input_text_ptr= (char *)malloc(sizeof(char)*size); /*Stored in heap segment*/
 *(input_text_ptr+0) = 'D'; //valid

As in case of normal arrays, the name input_text is nothing but a pointer to the start of char array: address of input_text[0].

char* temp = input_text; // using char pointer 
char temp[] = input_text; // invalid operation

//below is valid 
char temp[5]; 
temp[0]=input_text[0];
temp[1]=input_text[1];
temp[2]=input_text[2];
temp[3]=input_text[3];

String

So, what is a string in C++ then? Well, string class in C++ provides support for common string related operations such as compare, search, swap, copy and also offer dynamic sizing  , exception handling when there is access out of bounds.

Char arrays have a “fixed” size specified either implicitly or explicitly during declaration. But, strings do not require it. This is because strings are dynamically sized during runtime, where as char arrays are sized during compile time.

string mystr = "geekgirl"; // has no size or [] in the declaration
// we are creating a string object named mystr above
char mystr[] = "geekgirl"; // c style string declaration

Memory allocation

The memory for char arrays is allocated on stack unless malloc() is used to create a char array. In case malloc() is used, memory is allocated on the Heap and the user is responsible for releasing the memory after use. Since, cstrings are created with definite size in mind, it is harder to resize them, user has to be careful about invalid access. Think of String object as a utility in C++ that manages the dynamic allocations for us and provides an easier interface to perform operations on char arrays.


Passing to a function

String passing to a function is similar to how char arrays are passed, as in the example below.

char str[]="geek";
displayCharArray(str);

string str1="geek";
displayString(str1);

// passing char array to a function
void displayCharArray(char s[])
{
 cout << "Char array contains" << s <<;
}

// passing string to a function
void displayString(string s)
{
 cout << "String contains  " << s << endl;
}

Which one should one use?

In case you are implementing code to be compatible with legacy C code, then you may be better off using char arrays. String class objects are easy to use, provide features like bound check exceptions and manage the allocations – sizing and dynamic resizing, managing the free operations on the memory allocated for the user string. So, if you are using C++ and no legacy code to deal with, you could go for string objects.

As always, think of your use case and decide which one is suitable for your case. That’s all in this tidbit. There is a tiny mention about using char* in this blog (pointer to char), but will cover it in another tidbit. Until then, keep decoding!~


 

Tuples in Python

This blog assumes you know about Python “lists“.

Today’s tidbit is about Tuples in Python. Why? : Because tuples are special. They are immutable but their values can change !  Wondering what does this mean? Then, read on.


What are tuples?

Tuples refer to a sequence of immutable Python objects. They are similar to “lists” except the objects are not mutable. Tuples use parentheses, lists use square braces.

Tuples can store heterogeneous data together. For example : if you want to share a ip address of a host or domain name along with port for access

machine1=(“10.1.1.2”, ’80’);

More details here , under server_address parameter.

You use tuples when you don’t want to edit the values once defined, as in this case.

If you want to hold list of heterogeneous items that need to be modified, you would use a list.


Immutable Property

To get a good understanding, I would suggest that you check this : Data model in Python 3. I am adding an example code snippet, so it gives you a clear picture what it means to be “immutable” in Python.

Since tuples are immutable objects in Python, when you have a tuple1+=(4,) kind of operation – it essentially means a new object shall be created. This is demonstrated by the difference in the ids of the first tuple1 instance and tuple1  post the addition of  new elements : as marked in RED below. Whereas, with a list, even after addition of new elements, the id remains the same- because they are “mutable” data structure in Python. 

tuple_test.png
Snapshot 1. Immutable property of Tuples

Immutable Tuples and mutable objects

Quoting from the data model page :

The value of an immutable container object that contains a reference to a mutable object can change when the latter’s value is changed; however the container is still considered immutable, because the collection of objects it contains cannot be changed. So, immutability is not strictly the same as having an unchangeable value, it is more subtle.) 

Essentially, when tuples hold a “list” object, Tuples are the immutable containers to mutable object whose value can change. This means values contained in the Tuple cannot change, while the data contained in the list object can still change.

An example would most definitely add clarity. Here goes an example :

Marked with a red line is the list that is initialized and the one that is held as one of the objects in the Tuple  (container) tuple_with_list. Then, marked in yellow are the modifications to the list, addition of two new values – 4 and 5. So, essentially , a mutable object’s value can be updated even when contained within an immutable container (Tuple). This, as expected updates the value of the tuple.  And, as highlighted with a blue line, the id of the Tuple will remain the same before and after updates to the list object.

t_w_l.png
Snapshot 2. Tuple with a List object

An object’s mutability is determined by its type; for instance, numbers, strings and tuples are immutable, while dictionaries and lists are mutable.


Are tuples better than Lists?

What do you think ? Do let me know in the comments below. 😉

I will add my take soon. Do check back. Until then , keep decoding! 🙂

Tail Recursion

In my previous blog on recursion, we noted the comparison between iterative and recursive implementations. Now, if we want the memory consumption of  an iterative approach and the elegance of a recursive implementation, then I had mentioned that we would need to take a look at : Tail recursion! So, let’s start decoding this.


What is Tail Recursion ?

Tail call refers to an operation where we call a function say, g() and immediately return it’s return value as the return value of our function f(). In this case, we will not  need to preserve any of the state of the current code (state of g()) – since we are just about to throw it away and return.

Tail recursion is a form of recursion where the recursive call (call to same procedure f() itself) is the absolute last operation performed (called recursive tail call). Basically, meaning that there is nothing left to execute in the function after recursive calls – ie., the function returns the result of a recursive call.

The result of recursive call in this case can be passed directly for extra computation,  instead of waiting for it , which translates to no extra stack space. Normal recursive call, on the other hand, has a stack frame allocated, so that the compiler remembers where to get back to and to have the necessary variable values ready after recursive call is finished. This optimization is called – tail call optimization (TCO).

To write tail recursive version of the function, it means that we need to make sure recursive call is the absolute last operation in the function – there should not be any extra operations performed after the recursive call.


Let’s consider a very simple example to understand this concept.

Example

Consider a recursive function that returns the sum of ‘n’ positive numbers.

I .Recursive implementation

  • The recursive formula for sum of “n” numbers is given by: sum(n) = n+sum(n-1)
  • and sum is 1 when input is 1. (base case)

int sum(int n)

{

// Base condition for input = 1.

if(n==1) {

return 1;

}

// cases greater than 1.

else {

return n+sum(n-1); //This has extra operation of adding “n” to sum returned from recursive  function call

}

}

II. Implementing tail recursive version

  • To re-write sum function above in tail recursive form, all we have to do is :
    1. Identify the extra operation performed between return and recursive call . In this case addition of “n” to what is returned from recursive call.
    2. Modify and re-write the function to do this extra operation internally and then return, usually involves passing extra state variable called accumulation.
    3. Retain only recursive function call as the last operation in the method.

Now, let’s give tail recursive implementation a shot using the three steps outlined above.

  • In the recursive implementation above (under I),  we can see that there is an extra operation of adding output of recursive call to “n” at the end as highlighted in red.
  • For step 1 : The last line in the implementation can be split and written as :
  • y = sum(n-1);  
  • result = n+ y ;  //
  • return result ; 

For steps 2/3 : The new function version will take an extra argument which is an accumulation of current value. So, the extra operation to be performed is taken care of in the function call itself and not after return from recursive function call. Note : This style of passing control explicitly is called “Continuous Passing Style“. This makes sure that recursive call is also the last operation performed.

r2


Role of Compiler

Modern compilers support tail call elimination for tail recursive functions- an optimization called “TCO” (Tail Call optimization) as briefly mentioned above. TCO is the process by which a compiler can make a call to a function and take no additional stack space. The only case this would happens is if the last instruction executed in a function f() is a call to a function g(). Of course, in recursive case the call will be to itself f().

I have personally programmed in C and Python. GCC compiler (C compiler) offers TCO (Tail call optimizations).

Essentially, when we re-write the function in tail recursive form, the compiler optimization looks like below. It would be converted to a goto instead of recursive calls with stack allocation for it to remember the outcome of each recursive call to be used for the extra operation. This means that we  will not hit the stack over flow problem. Yay!

int tail_recursive_sum(int n, int accum=0) {
label:
    if (n == 1) return n+accum;
    accum += n--;
    goto label;
}

However, python implementation does not support TCO internally, which means it is expected that we judge the depth of recursion and re-write the iterative version, if we want to be safe with stack usage.

Generally, the need for recursive call optimization is dependent on the performance of your algorithm. If you have to implement a program that takes O(n) operations, it is not a good idea to use recursive version, while if it is O(logn) like tree implementations, we should be okay using the recursive version, we may not blow up stack.

Of course, I have shown a simple example here. There may be more complicated use cases. May be I will write about them soon in another blog. 😉

I want to keep the tidbit entries concise !


Conclusion

  • The function executes a little faster, since no stack pushes and pops are required
  • Function recursion depth is not a constraint any more, as there is no linear growth of stack usage with recursive calls.
  • We will not hit the stack overflow issue with this implementation.

So, if you are coding in C , you could implement a tail recursive way to get the benefit of compiler TCO, while for python use the iterative version! :). That’s all for today !

Check this for more details on this topic w.r.t C++ as mentioned by John in the comments below.  I will need to catch up on this as well. So, until next time,  keep decoding !

Byte Ordering

What is Endianness?

Endianness or Byte ordering refers to two different ways of storing multi-byte data in memory by the processor. In the embedded world where the data memory is shared and there is some hardware writing data to this block for some other piece of hardware to consume, it becomes important to understand and agree upon the endianness.

Network and Host Byte order

Yes, same applies to over the network data transfer protocols, in case you are familiar with them. The network uses big endian format while the end device may be little or big endian – hence we have the htons() and ntohs() socket level apis made available for usage. htons refers to “host to network” while ntohs refers to “network to host”  byte order conversions.


Examples

Consider a 16-bit integer in hex format 0x1234. This is represented in a “little endian” system with lower order byte first “34”. And,is represented in a “big endian” system  with higher order byte “12” first as summarized in the table below.

Address Little endian Big endian 
0x4000 34 12
0x4001 12 34

Example 32-bit data : 0xABCD1234

Address Little endian Big endian 
0x4000 34 AB
0x4001 12 CD
0x4002 CD 12
0x4003 AB 34

Detecting  the Byte order

Now that we know what big and little endian is, let’s see how we could potentially identify the endianess of a system.

Let’s assume we have a integer variable with value set to 1 – represented on a 16-bit system as 0x0001. If the first byte is 1 (that is data at lower address) then, it a Little endian system else it is a Big endian system. To check if the system is little endian we could write the sample program below.

boolean isLittleEndian()

{

int i = 0x0001;  // 16-bit variable

char* a = (char*) &i; // size of char value is a byte (8 bits)

return (*a)?1:0; // terenary operator (condition?value_if_true value_if_false)

}

If above returns true – the system is little endian else the system is big endian.


Conversion

As I had briefly mentioned above, the socket libraries support api’s for conversions such as htons() and ntohs(). Now, let’s see how we could implement our own functions for this.

htons() is used to convert from host to network order [hton] for short variable [s] : that is little endian to big endian.

 As seen in the example below, all we have to do is swap the bytes passed. Do check the post about bit level operations to understand the trick used here.

Address Little endian Big endian 
0x4000 34 12
0x4001 12 34

uint16 htons(uint16 input)

{

//reuse the endian check function

if(isLittleEndian())

{

//host is little endian – conversion needed

//use bit manipulation tricks to swap the bytes in-place as below

return ((input&0xFF00>>8)|(input&0x00FF<<8));

}

else

{

//this means it is in big endian format

// no conversion needed

return input;

}

}


That’s all in this tidbit.

Until next tidbit, keep decoding ! Happy programming !