Tuesday, January 22, 2008

Banana leaves - hyderabad

Sometime back I posted about variable parameters in Ruby. C# also supports methods that accepts variable number of arguments (e.g. Console.Writeline). In this post I'll try to cover what happens in the background. This is a long one and so bear with me :)

Consider the following two methods. Both prints out each argument passed to it. However, the first accepts variable arguments using the params keyword.

static void Print1(params int[] args)
{
foreach (int arg in args)
{
Console.WriteLine(arg);
}
}

static void Print2(int[] args)
{
foreach (int arg in args)
{
Console.WriteLine(arg);
}
}

The above methods can be called as follows

Print1(42, 84, 126); // variable argument passing
int[] a = new int[] { 42, 84, 126 };
Print2(a); // called with an array

Obviously in the case above, using variable number of parameters is easier.

If we see the generated IL for Print1 and Print2 using ILDASM or Reflector and then do a diff, we will get the following diff

.method private hidebysig static void Print2(object[] args) cil managed
.method private hidebysig static void Print1(object[] args) cil managed
{

.param [1]
.custom instance void [mscorlib]System.ParamArrayAttribute::.ctor()

.maxstack 2
.locals init (
[0] object arg,
[1] object[] CS$6$0000,
[2] int32 CS$7$0001,
[3] bool CS$4$0002)
L_0000: nop
L_0001: nop
L_0002: ldarg.0
L_0003: stloc.1
L_0004: ldc.i4.0
L_0005: stloc.2
L_0006: br.s L_0019
L_0008: ldloc.1
L_0009: ldloc.2
L_000a: ldelem.ref
L_000b: stloc.0
L_000c: nop
L_000d: ldloc.0
L_000e: call void [mscorlib]System.Console::WriteLine(object)
L_0013: nop
L_0014: nop
L_0015: ldloc.2
L_0016: ldc.i4.1
L_0017: add
L_0018: stloc.2
L_0019: ldloc.2
L_001a: ldloc.1
L_001b: ldlen
L_001c: conv.i4
L_001d: clt
L_001f: stloc.3
L_0020: ldloc.3
L_0021: brtrue.s L_0008
L_0023: ret
}

Only the lines in Green are additional in Print1 (which takes variable arguments) and otherwise both methods looks identical. In this context .param[1*] indicates that the first parameter of Print1 (args) is the variable argument. The ParamArrayAttribute is applied to the method to indicate that the method allows variable number of arguments.


Effectively all of the above means that the callee is not really bothered with being invoked with variable number of arguments. It receives an array parameter as it would even without the param keyword usage. The only difference is that the method is decorated with the some directive and attribute when param is used. Now it's the caller-code compiler's duty to read this attribute and generate the correct code so that variable number of parameters are put into a array and Print1 is called with that.

The generated IL for the call Print1(42, 84, 126); is as follows...

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 3
.locals init (
[0] int32[] CS$0$0000)
L_0000: nop
L_0001: ldc.i4.3 ; <= Array of size 3 is created, int32[3]
L_0002: newarr int32 ; <=
L_0007: stloc.0 ; <= the array is stored in the var CS$0$0000
L_0008: ldloc.0
L_0009: ldc.i4.0 ; push 0
L_000a: ldc.i4.s 0x2a ; push 42
L_000c: stelem.i4 ; this makes 42 to be stored at index 0 **
L_000d: ldloc.0
L_000e: ldc.i4.1
L_000f: ldc.i4.s 0x54
L_0011: stelem.i4 ; similarly as above stores 84 at index 1
L_0012: ldloc.0
L_0013: ldc.i4.2
L_0014: ldc.i4.s 0x7e
L_0016: stelem.i4 ; stores 126 at index 2
L_0017: ldloc.0
L_0018: call void VariableArgs.Program::Print1(int32[]) ; call Print1 with array
L_001d: nop
L_001e: ret
}

This shows that for the call an array is created and all the parameters are placed in it. Then Print1 is called with that array.

Footnote:
*interestingly it starts at 1 and not 0 because 0 is used for the return value.
**stelem takes the stack [..arrayindexvalue] and replaces the value in array at index with value

Monday, January 14, 2008

The differences between int[,] and int[][]


Mumbai roadside drinks

A friend asked me the differences between the two. Here goes the answer

int[,]

This represents a two dimensional rectangular array. Let's take the following array definition

int[,] ar = new int[3, 3] { { 0, 1, 2}, 
{ 3, 4, 5},
{ 6, 7, 8}};

The array actually looks like the following



Which as we can see is rectangular. This kind of array is required when for every items represented in the rows there's exactly the same number of items represented in the column. E.g. a board game like chess.


int[][]


This is defined as array of arrays or as jagged arrays. They are created as follows

int[][] ar2 = new int[3][];
ar2[0] = new int[] { 0, 1, 2 };
ar2[1] = new int[] { 3, 4 };
ar2[2] = new int[] { 5, 6, 7, 8 };

The array looks like


Capture


Here the number columns is not the same for each row. A good example of this kind of usage is when we have a array of polygons where each row contains the coordinates of the vertices of the polygon. Since each polygon has different number of vertices (square, triangle, ...) this data-structure is useful in defining it.


Since it's jagged it has to be referenced carefully.

Console.WriteLine(ar2[0][2]); // Prints 2
Console.WriteLine(ar2[1][2]); // Crashes as row 1 has only 2 columns

The jagged array can be padded on the right to convert it to a rectangular array, but this will result in a lot of space wastage. In case of the jagged array the usage of space is sizeof(int) * 9 but if we pad it we will use sizeof(int) * max (column). This additional space can be significant.


Note:


int and 2D arrays were used only as example. This applied equally well to other data-types and higher dimensions.

Expanding and compressing in Ruby Method calls


2007_0520_184605

One of the things I didn't like in Ruby at all is the support for method overloading. You have no ways to support it in a straight forward way other than to define a single method that takes a variable number of arguments.

def foo(*f)
f.each { |a|
puts a
}
end

foo("Hello", "world")

What the above does is that it converts the multiple parameters into a single array and passes it to the method call. I think this is really bad because method overloading is a very basic requirement.


However, Ruby seemed to support another really weird feature of expanding arrays in method calls. What this means is that if a method accepts a number of parameters and it's called with an array then the array is expanded such that the i'th element in the array is passed as the i'th argument.

def bar(name, address, age)
puts (name, address, age)
end

details = ["Abhinaba", "Hyderabad, India", 42]
bar(*details)

So details[0] is passed to the name parameter and details[1] is passed as address, and so on

Learn a new dynamic or functional language every year


2007_0501_091707

Specially for folks using the C* languages (C/C++/C#) and Java it's very important to learn dynamic/functional languages.

There are three reasons.

  1. With time we will see more and more high level dynamic languages and at the same time many languages like C# will take up dynamic/functional language attributes and hence it's good to be prepared
  2. It'll give a fresh perspective to do the mundane day to day coding. Lambda's in C# won't look alien any more :)
  3. It's fun and will help you survive going through gazillion bulleted lists like this ..

I think the fun part is the most important.

``I think that it's extraordinarily important that we in computer science keep fun in computing. When it started out, it was an awful lot of fun. Of course, the paying customers got shafted every now and then, and after a while we began to take their complaints seriously. We began to feel as if we really were responsible for the successful, error-free perfect use of these machines. I don't think we are. I think we're responsible for stretching them, setting them off in new directions, and keeping fun in the house. I hope the field of computer science never loses its sense of fun. Above all, I hope we don't become missionaries. Don't feel as if you're Bible salesmen. The world has too many of those already. What you know about computing other people will learn. Don't feel as if the key to successful computing is only in your hands. What's in your hands, I think and hope, is intelligence: the ability to see the machine as more than when you were first led up to it, that you can make it more.''

Alan J. Perlis (April 1, 1922-February 7, 1990)