Monthly Archives: February 2011

Error Messages Are for the Users

A few days ago, I ran across the following code snippet:

if len(sys.argv) < 2:
    print 'Need at least one argument'
    exit(1)

From a developer’s point of view, this code is fine: it stated that a required argument is missing. However, from a user point of view, the message does not make any sense. Sure, the message tells the user that a required parameter is missing, but what is it? A file name? A directory location? An URL? An email?

Even if the program is written for private use, hence the developer is the sole user, error messages like this does not help stating the real problem. Many times, I found myself having to look into my own code to learn of its usage. What about the situations when the users do not have access to the source code, or if they are not technically savvy enough to find out?

When writing code, keep the users in mind and tailor error message from their point of view. The above then could be written as:

if len(sys.argv) < 2:
    print 'An email address is required, but missing.'
    print 'Usage: myprogram email'
    print 'Example: myprogram foo@bar.com'
    exit(1)

Below are some error messages which I encountered (some I even wrote, shame on me) and suggested changes:

  • Socket is NULL ==> Cannot connect to host $hostname, please check for correct host name, or port number
  • File not found ==> File $filename does not exist, please check for correct path
  • An error has occurred, program quits ==> Too vague to suggest, the program should investigate the cause of failure and report a more detailed message.
  • Login error L01253 ==> Something like: Login error — wrong user name or password.

In conclusion, while providing a more detailed error message means extra works for a developer, it makes for a better user experience. The next time you see a vague error message, either fix it, or report to the developer.

Advertisements

How to Remove Duplicates in a Tcl List

The Problem

I want to remove all duplicates within a list

The Solutions

There are at least two solutions to this problem. The first employs the lsort -unique command and the second uses the lrmdups command from the Tclx package.

56 % package require Tclx
8.4
57 % lsort {1 3 1 2 2}
1 1 2 2 3
58 % lsort -unique {1 3 1 2 2}
1 2 3
59 % lrmdups {1 3 1 2 2}
1 2 3

Discussion

  • Line 56 imports the Tclx package needed by the lrmdups command.
  • Line 57 sorts the list, but does not remove duplicates.
  • Line 58 sorts and removes duplicates. This is what we want.
  • Line 59 achives the same result as above, but more expressive. Note that the lrmdups command returns a sorted list, the same way as the lsort -unique command.

How to Merge Tcl Lists and Eliminate Duplicates

The Problem

I want to merge two lists, and eliminate duplicates.

The Solution

Use the union command from the Tclx package.

49 % package require Tclx
8.4
50 % set list1 {Peter Paul Marry}
Peter Paul Marry
51 % set list2 {Paul John Ringo George}
Paul John Ringo George
52 % union $list1 $list2
George John Marry Paul Peter Ringo
53 % union {1 1 1 1 1} {2 2 2}
1 2

Discussion

  • Line 49 imports the Tclx package
  • Line 50 to 51 creates two lists which contain a common element: Paul
  • Line 52 merges these two lists using the union (as in set theory) command. The result is a merged list with no duplicates
  • Line 53 merges two lists with duplicate elements within themselves. The result is a single list in which each element only appears once.

The union command is good for merging lists while removing duplicates. The command does not guarantee the order of the elements so do not use it in situation when order matters.

How to Compare Two Unordered Tcl Lists

The Problem

I want to compare two lists for equality, but disregard the order. For example: {a b c} and {b a c} are equal, but {a b c} {a b c d} are not.

The Solutions

There are several solutions to this problem. The first is to first sort the two lists, then compare them. Another solution is to roll my own comparison function. Finally, I can use the equal command from the struct::set package.

The first solution involves sorting both lists, then comparing them. When the size of either list is large, then sorting will take time, thus hurts performance. Therefore, this solution works fine for small to medium size lists. I am lazy (it’s a virtue!) so rolling my own solution is out of the question, especially if there are solutions out there, such as the last solution.

This solution involves a little-known package called struct::set. This package implements sets, which is also a list. For the purpose of our discussion, we will concentrate on the struct::set equal command. Below is some examples from the Tcl shell (tclsh):

53 % package require struct::set
2.2.3
54 % struct::set equal {a b c} {b c a}
1
55 % struct::set equal {a b c} {a b c}
1
56 % struct::set equal {a b c} {a b}
0
57 % struct::set equal {a b c} {a b c a b c}
1

Discussion

  • Line 53 imports the struct::set package
  • Line 54 compares two lists for equality, regardless of order. In this instance, the equal command returns 1, which is true.
  • Line 55 compares two identical lists
  • Line 56 compares two different lists
  • Line 57 comes as a surprise: what is going on here? these two lists are equal? Recall that in a set, each element appears only once. That means {a b c a b c} is the same as {a b c} from a set point of view. This is a caveat one needs to take in consideration.

As we can see from the interactive session, the struct::set equal command compares two lists for equality–from a set standpoint. This command works well for lists with unique elements, but will fall short if a list contains duplicated elements.