Thursday, December 4, 2008

Testing for length in dynamic languages

Testing for the length of some object in dynamic languages, or, statically-typed languages for that matter, is a common task in every single application. Especially in statically-typed languages. In cases where some task needs to be carried out for every object in a set, we simply iterate through that set. We perform the task needed on every object, possibly invoking some operation with the object as a parameter.

In Python, and other similar languages, it is fairly trivial to iterate over a set and is done quite often. For example.
#Python iterate.

list_obj=[1,[2,3],[4,[5,6],7],8,9,10]
for i in list_obj:
print i
Here, we have a simple list of integers and sub-lists. We iterate through this list and print its' elements.

Now, say we want to generate some HTML output for a web page. We need to display a list of all list elements on this HTML page. I'll implement a simple function to generate list elements.
#Python iterate.

list_obj=[1,[2,3],[4,[5,6],7],8,9,10]
html_list='<ul>%s</ul>'
temp_content=''

def generate_list_element(obj):
return '<li>%s</li>'%(str(obj))

for i in list_obj:
temp_content+=generate_list_element(i)

print html_list%(temp_content)
Here, we set up a temporary variable to hold all li tags generated by the generate_list_element() function. We also have the html_list template variable defined which, after the iteration has completed, will give us the final ul tag.

One problem with this approach is that if list_obj is empty, we have an empty ul element. I most cases, this isn't the desired output. The better solution would be to initialize the template based on what is available to the template. For example.
#Python iterate.

class MyHTML:
list_obj=[1,[2,3],[4,[5,6],7],8,9,10]
html_list=False
template='<ul>%s</ul>'
error='Error. List is Empty.'

@classmethod
def generate_list_element(cls, obj):
return '<li>%s</li>'%(str(obj))

@classmethod
def _set(cls, content):
cls.html_list=cls.template%(content)

@classmethod
def set(cls, content):
content and cls._set(content)

@classmethod
def get(cls):
return cls.html_list or cls.error

temp_content=''
for i in MyHTML.list_obj:
temp_content+=MyHTML.generate_list_element(i)
MyHTML.set(temp_content)
print MyHTML.get()
This code will produce the exact same output as the previous example. The main difference being that here, we can handle empty sets. I've better organized the code by introducing a class; MyHTML. This class defines an error message in the case of an empty list. The message isn't the important idea here. What is important is that MyHTML.error could be an empty string or invoke some other behavior to produce the desired output in the case of an empty set.

If you take a look at the main program, ignoring the class for now, you'll notice that we do not test for length. We simply iterate through our set and build our content. If the set is empty, the loop is not entered and that is our indicator of a false value. In the context of an alternate output based on an object having any elements or not, boolean logic should be used.

Just because we use the and and or operators doesn't make our code any less readable. It is encapsulated by our MyHTML class. In my opinion, the length of an object really only needs to be calculated for logic that "does this object have a length?" does not support.