I know the answer (it's 42)

A blog on coding, .NET, .NET Compact Framework and life in general....

January, 2008

Posts
  • I know the answer (it's 42)

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

    • 4 Comments

    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

    Capture1

    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.

  • I know the answer (it's 42)

    Variable Parameter passing in C#

    • 2 Comments

    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 [..|array|index|value] and replaces the value in array at index with value

    Cross posted here

  • I know the answer (it's 42)

    Learn a new dynamic or functional language every year

    • 2 Comments


    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)

    >> Cross posted here

  • I know the answer (it's 42)

    Expanding and compressing in Ruby Method calls

    • 1 Comments

    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

    >> Cross posted here

Page 1 of 1 (4 items)