Heap Pollution

This valid Java code:

List numList = new ArrayList<Number>();
List<String> strList = numList;

The assignment to strList has resulted in what is known as a Heap Pollution. Java is a strongly typed language and the compiler is expected to raise an error but it will emit only an "unchecked warning". There are two issues that prevent the compiler from being strict here:
  1. Non-parameterized containers are allowed for compatibility with legacy code.
  2. The compiler cannot tell that the type of numList is not List<String> at the point of assignment to strList because this is possible:

    numList = new ArrayList<String>();
    List<String> strList = numList;

If at the point of assignment, numList is still referring to an ArrayList<Number>, then the following code will result in a ClassCastException at runtime:

numList.add(1);
List<String> strList = numList;
System.out.println (strList.get(0)); // ClassCastException

The solution is to follow the best programming practice of ensuring that all "unchecked warnings" are cleared before executing the code.

Design pattern solutions as language features

When programming in a very-high-level language like Lisp, the user is relieved from inventing solutions to design patterns, as the solutions are built-into the language itself.  I present two examples here.  

 
The first is for the Visitor pattern, which provide an  opportunity to add new operations to a data structure, without modifying it.  In  other words, adding new methods to a class without sub-classing.  We present the solution in Java and Common Lisp.  The example specifies a Component base class from which different components like HDD, RAM etc are derived.  There are three versions of Java code.  Each present an improvement over the previous.  The third version provides an interface that supports a visitor, so that the user of the Component class can add new features, without touching the class hierarchy.  The problem is that, the classes has to be re-defined once so that it can support a visitor.  Next we present the same library in Common Lisp, where the designer need not even worry about coding a solution to the visitor pattern, as the language already provides a solution in the form of generic functions. 
 
The next example shows how first-class functions can act as the natural solution for Strategy and many other patterns.
 

Click here to download:
Components_ver1.java (2 KB)

Click here to download:
Components_ver2.java (4 KB)

Click here to download:
Components_ver3.java (4 KB)

Click here to download:
components.lisp (2 KB)

Click here to download:
strategy.ss (0 Bytes)

Lisp in Lisp

The simplest meta-circular interpreter for Common Lisp:

> (loop (print (eval (read))))
(+ 2 3)
=> 5

(defun sqr (x) (* x x))
=> SQR

(sqr 10)
=> 100

[abort]
; Evaluation aborted

To write the same in Scheme, we should first define the loop form.

> (define-syntax loop
      (syntax-rules ()      
        ((_ body ...)
         (let _loop ()
   body ...
  (_loop)))))

> (loop (print (eval (read))))
(+ 2 3)
=> 5

(define (sqr x) (* x x))
=> <void>

(sqr 10)
=> 100

[abort]
; Evaluation aborted

Problems for interviews

Some simple problems to ask in programming interviews.  They can be used for initial screening and to put the candidate in a comfort zone.  These problems are easy to explain and understand and can be implemeneted in less than 30 lines of code.  Though they are simple, solving them require understanding of recurssion, invariants, data structures, time/space complexity, pointers etc. All solutions presented here are in C/C++.

Problem 1: Find the length of a NULL terminated array.

Obvious first solution using iteration:

int length (int* arr[])
{
    int len = 0;
    int i = 0;
    while (arr[i] != NULL)
    {
        ++len;
++i;
    }
    return len;
}

Test code:

int main ()
{
  int x = 45;
  int y = 30;
  int z = 50;
  int* a[4] = {&x, &y, &z, NULL};
  printf ("%d\n", length (a)); // => 3
  return 0;
}

Can you do that using recursion? 

Solution where candidate demonstrates knowledge about recursion and pointers:

int length (int* arr[])
{
  if (*arr == NULL) return 0;
  return (1 + length (++arr));
}

How will you take advantage of tail-call optimization, if the language implementation supports that?

Solution where candidate shows that he is familiar with non-imperative programming styles, pointer arithmetic and recursion:

int length_helper (int* arr[], int len_so_far)
{
    if (*arr == NULL) return len_so_far;
    return length_helper (++arr, ++len_so_far);
}

int length (int* arr[])
{
    return length_helper (arr, 0);
}

Is it possible to combine length and length_helper into one function? 

Answer: Use optional arguments (or varargs).

Problem 2: Write a function to compute the sum of the first n squares.

Iterative solution:

int add_n_squares (int n)
{
    int sum = 0;
    int i = 0;
    for (; i < n+1; ++i)
    {
        sum += (i * i);
    }
    return sum;
}

int main ()
{
  int x = 4;
  int y = 3;
  int z = 5;
  printf ("%d\n", add_n_squares (x)); // 30
  printf ("%d\n", add_n_squares (y)); // 14
  printf ("%d\n", add_n_squares (z)); // 55
  return 0;
}

What is the time complexity of this function?

Correct answer: O(n)

Can you make this function execute in constant time and space?

One possible Solution:

int add_n_squares (int n)
{
    return n * (n + 1) * (2 * n + 1) / 6;
}

As we are not hiring a mathematician, it can be ignored even if the candidate fails to come up with this solution.

Problem 3: Write a function to compute the dot-product of two vectors.A dot product is computed by multiplying corresponding elements and then adding up the resulting products. For example, the dot product of [45, 30] and [10, 5] is 600.(From Paradigms of Artificial Intelligence Programming by Peter Norvig).

Iterative solution:

int dot_product (int a[], int b[], int size)
{
  int i = 0;
  int result = 0;
  while (i < size)
    {
      int prod = a[i] * b[i];
      result += prod;
      ++i;
    }
  return result;
}

Recursive solution:

int dot_product (int a[], int b[], int size)
{
  if (size == 0) return 0;
  return ((a[0] * b[0]) + dot_product (++a, ++b, --size));
}

Problem 4: Write a program that encode and decode text using Caeser Shift. (http://en.wikipedia.org/wiki/Caesar_cipher)

Complete solution in C++:

// shift.cpp

#include <iostream>
#include <string>

void shift (const std::string& s, int by, 
        bool encode, std::string& res)
{
  size_t len = s.size ();
  for (size_t i = 0; i < len; ++i) 
    {
      if (encode) res += (s[i] + by);
      else res += (s[i] - by);
    }  
}

// Pass the string to shift and number of characters 
// to shift as arguments.
int 
main (int argc, char **argv)
{
  int shift_by = atoi (argv[2]);
  std::string encoded;
  shift (argv[1], shift_by, true, encoded);
  std::cout << "encoded: " << encoded << '\n';
  std::string decoded;
  shift (encoded, shift_by, false, decoded);
  std::cout << "decoded: " << decoded << '\n';
  return 0;
}

Test runs of the program:

> ./shift hello 3
encoded: khoor
decoded: hello

> ./shift hello 5
encoded: mjqqt
decoded: hello

> ./shift hello 0
encoded: hello
decoded: hello

> ./shift hello -1
encoded: gdkkn
decoded: hello

Circular lists in Common Lisp

There are two ways to create a circular list in Common Lisp.  The first method is to use the sharp-equal notation. Here we assign a label to a cons cell, so that we can refer to it later:

> '#1='(1 2 3 . #1#)
(1 2 3 1 2 3 1 2 3 1 ...)

The other method is to nconc a list to itself:

> (setq a '(1 2 3))
(1 2 3)
> (nconc a a)
(1 2 3 1 2 3 1 2 3 1 ...)