Saturday, June 03, 2006

Sets as an Elegant Alternative to Logic

python :: programming :: math



A few years ago I was working on a sub-project of CoyoteMonitoring
where we were writing a database "layer" for the file system,
essentially allowing us to query for files using standard SQL commands.
We needed to do some exclusionary logic for filtering bad user input.
Though mathematically equivalent, I deal with Set Theory and Logic very
differently: I'm a set guy, and I really hate using series of if/else
statements for problems that lend themselves easily to an approach that
is "set theoretic" (we're talking super-mundane set stuff -- nothing
really sexy).

On IRC, I was gently preparing a fellow project developer for some code
changes I had made that eliminated a series of ugly ifs:

 
[03-Oct-2004 02:02] you know, we could do this really
quickly with sets :-)
[03-Oct-2004 02:02] check this out:
[03-Oct-2004 02:02] lets say our table has 4 fields:
[03-Oct-2004 02:03] ['A','B','C','D']
[03-Oct-2004 02:03] and we're doing a select for ['D', 'A',
'C']
[03-Oct-2004 02:05] and lets say we've got a bad query
['Z', 'B', 'C']
[03-Oct-2004 02:07] then, if we perform a set difference on
select
tables against defs, the returned set will be zero
[03-Oct-2004 02:07] and if we do the same with bad against
defs,
we'll get a non-empty set, in this case a set with one element, 'Z'


I then shared some python 2.3 code (no built-in set object) that
basically illustrated the changes I had made. For me, the results were
much more readable. The code was certainly much shorter.

Since then, I've found all sorts of uses for this approach. I've
recently made use of this in a Nevow project that has to authenticate
off of a Zope instance. There are a series of group and role names that
can be qualified under one of three "group-groups" (tiers):


# setup sets for each class of user
admins = set(cfg.user_database.admin_roles)
libs = set(cfg.user_database.tiertwo_roles)
vols = set(cfg.user_database.tierone_roles)
# and then a convenience collection
legal_roles = admins.union(libs).union(vols)

Later in the code, we do checks like this:


if admins.intersection(avatarRoles):
return IAdministratorResource

I like this much more than explicitly checking for the presence of
elements in lists.


No comments: