<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>The Intellectual Wilderness</title>
	<atom:link href="http://zxq9.com/feed" rel="self" type="application/rss+xml" />
	<link>http://zxq9.com</link>
	<description>On Government: &#34;There is nothing more useless than doing efficiently that which should not be done at all.&#34;</description>
	<lastBuildDate>Fri, 24 May 2013 03:48:06 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Recursive Lineage Queries in SQL and RyuQ</title>
		<link>http://zxq9.com/archives/840</link>
		<comments>http://zxq9.com/archives/840#comments</comments>
		<pubDate>Wed, 22 May 2013 03:47:15 +0000</pubDate>
		<dc:creator>zxq9</dc:creator>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[CTE]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[Postgres]]></category>
		<category><![CDATA[recursion]]></category>
		<category><![CDATA[RyuQ]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://zxq9.com/?p=840</guid>
		<description><![CDATA[Without explaining every detail, here is a &#8220;proper&#8221; (as in, non-NULL dependent) method for handling tree data within a recursive query. In the example below table foo has an id, a parent and a dtg (date-time-group). Obviously real world examples will have more fields, but this illustrates the technique. This sort of thing is quite handy [...]]]></description>
				<content:encoded><![CDATA[<p>Without explaining every detail, here is a &#8220;proper&#8221; (as in, non-NULL dependent) method for handling tree data within a recursive query. In the example below table <strong>foo</strong> has an <strong>id</strong>, a <strong>parent</strong> and a <strong>dtg</strong> (date-time-group). Obviously real world examples will have more fields, but this illustrates the technique. This sort of thing is quite handy in a huge variety of situations as quite a lot of data is hierarchical in nature.</p>
<p>Adding a <strong>layer</strong> type attribute on the view is also a good way to accomplish things like, say, providing visual layout variables for threaded comments on a website, even if your (stupid) web framework doesn&#8217;t understand such things &#8212; you can bypass the framework garbage by <a href="http://zxq9.com/archives/616">defining a lineage-type view directly in the database and a &#8220;model&#8221; on top of it</a>.</p>
<pre>CREATE FUNCTION default_parent() RETURNS TRIGGER AS
  $$
  BEGIN
    IF NEW.parent IS NULL THEN
      NEW.parent := NEW.id;
    END IF;
    RETURN NEW;
  END;
  $$ LANGUAGE plpgsql;

CREATE TABLE foo
 (id         SERIAL PRIMARY KEY,
  parent     integer REFERENCES foo ON UPDATE CASCADE ON DELETE SET NULL NOT NULL,
  created_on timestamptz DEFAULT current_timestamp NOT NULL);

CREATE TRIGGER foo_default_parent
  BEFORE INSERT ON foo
  FOR EACH ROW EXECUTE PROCEDURE default_parent();

CREATE TRIGGER foo_reset_parent
  BEFORE UPDATE ON foo
  FOR EACH ROW EXECUTE PROCEDURE default_parent();

CREATE VIEW lineage AS
  WITH RECURSIVE
  parents AS
    (SELECT * FROM foo WHERE parent = id),
  children AS
    (SELECT * FROM foo WHERE parent != id),
  tree AS
   (SELECT id,
           id::text AS branch,
           0 AS layer,
           parent
      FROM parents
   UNION ALL
    SELECT c.id,
           t.branch || '.' || c.id::text AS branch,
           t.layer + 1 AS layer,
           c.parent
    FROM children AS c, tree AS t
    WHERE c.parent = t.id)
  SELECT * FROM tree ORDER BY branch;

CREATE OR REPLACE FUNCTION show_descendants(parent integer) RETURNS TABLE
   (id      integer,
    lineage text,
    layer   integer,
    parent  integer) AS
  $$
    WITH RECURSIVE
    parents AS
      (SELECT * FROM foo WHERE id = $1),
    children AS
      (SELECT * FROM foo WHERE parent != id),
    tree AS
     (SELECT id,
             id::text AS branch,
             0 AS layer,
             parent
        FROM parents
     UNION ALL
      SELECT c.id,
             t.branch || '.' || c.id::text AS branch,
             t.layer + 1 AS layer,
             c.parent
      FROM children AS c, tree AS t
      WHERE c.parent = t.id)
    SELECT * FROM tree ORDER BY branch;
  $$ LANGUAGE SQL STABLE;

CREATE OR REPLACE FUNCTION
  get_lineage(parent_id integer DEFAULT 0, hide_parent boolean DEFAULT false)
  RETURNS TABLE
   (id      integer,
    lineage text,
    layer   integer,
    parent  integer) AS
  $$
    BEGIN
      IF parent_id &gt; 0 AND hide_parent IS true THEN
        RETURN QUERY EXECUTE $query$ SELECT * FROM show_descendants($1) OFFSET 1 $query$
          USING parent_id;
      ELSIF parent_id &gt; 0 AND hide_parent IS false THEN
        RETURN QUERY EXECUTE $query$ SELECT * FROM show_descendants($1) $query$
          USING parent_id;
      ELSE
        RETURN QUERY EXECUTE $query$ SELECT * FROM lineage $query$;
      END IF;
    END;
  $$ LANGUAGE plpgsql STABLE;</pre>
<p>Let&#8217;s see some invocations:</p>
<pre>insert into foo values (default);
insert into foo values (default);
insert into foo values (default);
insert into foo values (default, 1);
insert into foo (parent) values (1);
insert into foo (parent) values (1);
insert into foo (parent) values (1);
insert into foo (parent) values (2);
insert into foo (parent) values (3);
insert into foo (parent) values (3);
insert into foo (parent) values (4);
insert into foo (parent) values (5);
insert into foo (parent) values (7);
insert into foo (parent) values (7);
insert into foo (parent) values (10);
insert into foo (parent) values (15);

select * from lineage;
 id |   branch   | layer | parent 
----+------------+-------+--------
  1 | 1          |     1 |      1
  4 | 1.4        |     2 |      1
 11 | 1.4.11     |     3 |      4
  5 | 1.5        |     2 |      1
 12 | 1.5.12     |     3 |      5
  6 | 1.6        |     2 |      1
  7 | 1.7        |     2 |      1
 13 | 1.7.13     |     3 |      7
 14 | 1.7.14     |     3 |      7
  2 | 2          |     1 |      2
  8 | 2.8        |     2 |      2
  3 | 3          |     1 |      3
 10 | 3.10       |     2 |      3
 15 | 3.10.15    |     3 |     10
 16 | 3.10.15.16 |     4 |     15
  9 | 3.9        |     2 |      3
(16 rows)

select * from get_lineage(3);
 id |  lineage   | layer | parent 
----+------------+-------+--------
  3 | 3          |     0 |      3
 10 | 3.10       |     1 |      3
 15 | 3.10.15    |     2 |     10
 16 | 3.10.15.16 |     3 |     15
  9 | 3.9        |     1 |      3
(5 rows)

select * from get_lineage(3, true);
 id |  lineage   | layer | parent 
----+------------+-------+--------
 10 | 3.10       |     1 |      3
 15 | 3.10.15    |     2 |     10
 16 | 3.10.15.16 |     3 |     15
  9 | 3.9        |     1 |      3
(4 rows)</pre>
<p>Notice that there are no NULLs. Most formulations of tree type data uses NULL to represent not having a parent &#8212; but this is wrong. NULL means something is unknown. If the top-level nature of an entity is known, then NULL is the wrong value and complicates any logic involving that field unnecessarily. If, instead, we define a top level entity as any entity which is its own parent, then we have no ambiguity in the structure, and we are providing a correct model. For this reason the <strong>parent</strong> column above is NOT NULL. If we wanted to provide the possibility of maintaining records where the parent was actually unknown, we could remove the NOT NULL constraint and unset the trigger.</p>
<p>That may sound nitpicky, but there are several places where this simplifies things or makes query execution downright unreliable because of a dependence on conditional logic shortcuts. Consider the common SQL function solution comparable to <strong>show_descendants()</strong> above:</p>
<pre>CREATE OR REPLACE FUNCTION show_descendants(parent integer) RETURNS TABLE
   (id      integer,
    lineage text,
    layer   integer,
    parent  integer) AS
  $$
    WITH RECURSIVE tree AS
     (SELECT id,
             id::text AS branch,
             0 AS layer,
             parent
        FROM foo
        WHERE (parent IS NULL AND $1 = 0) OR (parent = $1)
     UNION ALL
      SELECT c.id,
             t.branch || '.' || c.id::text AS branch,
             t.layer + 1 AS layer,
             c.parent
      FROM children AS c, tree AS t
      WHERE c.parent = t.id)
    SELECT * FROM tree ORDER BY branch;
  $$ LANGUAGE SQL STABLE;</pre>
<p>This works just fine in Postgres 9.2 &#8212; but that is a quirk of implementation. SQL is supposed to be declarative in nature, and as such there is no guarantee which side of the OR condition in WHERE will be executed first. It is cheaper to check the single condition on the right side than the AND condition on the left, so it is likely a future execution optimization will detect that and run the query this way. The problem is that the right side expression <em>can be true in invalid cases</em>. There is no way to write this query safely as long as NULL is a possible value for parent.<em></em><em></em></p>
<p>Another problem is that using a NULL parent value in any calculation will nuke the entire operation because of NULL propagation. Say you want to do &#8220;parent::text || foo&#8221; somewhere later. Surprise! Any NULL is going to kill the entire string. There are convoluted ways to avoid that, but we&#8217;re fighting against the nature of an unnatural data definition instead of providing a natural definition at the outset and letting the positive consequences follow.</p>
<p>An argument can be made that handling for-real parent values instead of just using NULL definitions complicates the code above unnecessarily. Its true, in SQL you have to define a trigger to handle the top-level case and be a little more specific in the view and function definitions that follow. But that&#8217;s the end of it. We&#8217;ve trapped the NULL and completely contained the complexity involved in modeling an accurate parent relationship, not mention guarded against the undefined nature of logical OR execution (this is a <em>huge</em> issue on its own). If we were to permit a NULL here and didn&#8217;t <em>really</em> intend for that to mean <em>unknown</em>, then we would have traded the convenience of writing a quick and dirty definition for letting the complex and unpredictable side effects escape our model and propagate throughout the rest of the schema and every application that touches this table. With that in mind it is obviously a better solution to pay the complexity tax up front and write the insert trigger and function definitions and nip the issue in the bud.</p>
<p>In <a href="http://zxq9.com/ryuq/index.html">RyuQ</a> this can be defined as:</p>
<pre>create [relation]
  foo
    attributes
      id         Serial
      parent     Integer
      created_on TimestampTZ
    conditions
      pk id
      fks
        parent -&gt; foo.id
      defaults
        parent = id
        created_on = current_timestamp</pre>
<p>And define some elabels to operate on the data:</p>
<pre>tree = union
         project [id, lineage = id::text, layer = 1, parent]
           select [parent == id] foo
         project [id = foo.id,
                  lineage = tree.lineage + '.' + foo.id,
                  layer = tree.layer + 1,
                  parent = foo.parent]
           join
             tree
             select [parent != id] foo

branch ID =
       union
         project [id, lineage = id::text, layer = 1, parent]
           select [id == ID] foo
         project [id = foo.id,
                  lineage = branch.lineage + '.' + foo.id,
                  layer = branch.layer + 1,
                  parent = foo.parent]
           join
             branch
             select [parent != id] foo

headless ID = select [parent != id] branch ID</pre>
<p>And that&#8217;s it. Notice that there are no NULLs here either; we are not using any <em>MaybeType</em> attributes. If we wanted to make the definition of a top-level entity &#8220;Something without a parent&#8221; then using the type <em>NoneInteger</em> would be correct. If we wanted to include the possibility that a parent relationship is unknown (but that this does <em>not</em> define a top-level entity) we could use <em>MaybeInteger</em>. If we want to include both then we should define a domain that includes integers, None, and _|_ as members, possibly calling it <em>MaybeNoneInteger</em>.</p>
<p>Invokation:</p>
<pre>insert [foo]
  {id}
  (default), (default), (default)
  {parent}
  (1), (1), (1), (2), (3), (3), (4), (5), (7), (7), (10), (15)

tree
 id |   branch   | layer | parent 
----+------------+-------+--------
  1 | 1          |     1 |      1
  4 | 1.4        |     2 |      1
 11 | 1.4.11     |     3 |      4
  5 | 1.5        |     2 |      1
 12 | 1.5.12     |     3 |      5
  6 | 1.6        |     2 |      1
  7 | 1.7        |     2 |      1
 13 | 1.7.13     |     3 |      7
 14 | 1.7.14     |     3 |      7
  2 | 2          |     1 |      2
  8 | 2.8        |     2 |      2
  3 | 3          |     1 |      3
 10 | 3.10       |     2 |      3
 15 | 3.10.15    |     3 |     10
 16 | 3.10.15.16 |     4 |     15
  9 | 3.9        |     2 |      3
(16 rows)

branch 3
 id |  lineage   | layer | parent 
----+------------+-------+--------
  3 | 3          |     0 |      3
 10 | 3.10       |     1 |      3
 15 | 3.10.15    |     2 |     10
 16 | 3.10.15.16 |     3 |     15
  9 | 3.9        |     1 |      3
(5 rows)

headless 3
 id |  lineage   | layer | parent 
----+------------+-------+--------
 10 | 3.10       |     1 |      3
 15 | 3.10.15    |     2 |     10
 16 | 3.10.15.16 |     3 |     15
  9 | 3.9        |     1 |      3
(4 rows)</pre>
<p>Pretty simple. I find the RyuQ easier and more clear, but obviously I&#8217;m biased.</p>
]]></content:encoded>
			<wfw:commentRss>http://zxq9.com/archives/840/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>More on a new Data Language</title>
		<link>http://zxq9.com/archives/836</link>
		<comments>http://zxq9.com/archives/836#comments</comments>
		<pubDate>Mon, 20 May 2013 13:17:44 +0000</pubDate>
		<dc:creator>zxq9</dc:creator>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[Science & Tech]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[relational algebra]]></category>
		<category><![CDATA[relational model]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[tutorial d]]></category>

		<guid isPermaLink="false">http://zxq9.com/?p=836</guid>
		<description><![CDATA[I&#8217;ve given more thought to the new data language I&#8217;m working on, and finally decided on a name as well. The new baby shall be called RyuQ &#8212; a reference to where I live respelled to accommodate the mandatory &#8220;Q&#8221; in any shiny new query language. I&#8217;ve settled on a modified form of S-expressions, calling [...]]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve given more thought to <a href="http://zxq9.com/ryuq/index.html">the new data language I&#8217;m working on</a>, and finally decided on a name as well.</p>
<p>The new baby shall be called RyuQ &#8212; a reference to where I live respelled to accommodate the mandatory &#8220;Q&#8221; in any shiny new query language. I&#8217;ve settled on a modified form of S-expressions, calling them SP-expressions to distinguish between real S-expressions and my mutated version. The (still infantile) language description is peppered with examples, so you can see pretty quickly if SP-expressions feel comfortable or more alien relative to SQL.</p>
<p>I have to say, the further I go on this the more I wonder what was going through the minds on the committee that created SQL. So many things are just <em>so obvious </em>when sticking to an algebraic notation rather than trying to form some new thing that is neither relational algebra nor relational calculus.</p>
<p>Until I have a complete implementation worked into a fork of Postgres I won&#8217;t be able to do anything but toy with this language &#8212; but the more thought I give it the less I am satisfied with the work I am currently compelled to do in SQL.</p>
]]></content:encoded>
			<wfw:commentRss>http://zxq9.com/archives/836/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Thinking About a Data Langauge</title>
		<link>http://zxq9.com/archives/824</link>
		<comments>http://zxq9.com/archives/824#comments</comments>
		<pubDate>Thu, 16 May 2013 12:43:47 +0000</pubDate>
		<dc:creator>zxq9</dc:creator>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[langauge development]]></category>
		<category><![CDATA[Postgres]]></category>
		<category><![CDATA[query langauge]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://zxq9.com/?p=824</guid>
		<description><![CDATA[I&#8217;ve been thinking a lot lately about how a query and data definition language would look if I were able to write one myself. Well, it turns out I can write one myself, it just takes a lot of time. I don&#8217;t have a lot of time, but I&#8217;ve written down some of my ideas [...]]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve been thinking a lot lately about how a query and data definition language would look if I were able to write one myself. Well, it turns out I can write one myself, it just takes a lot of time. I don&#8217;t have a lot of time, but I&#8217;ve written down some of my ideas to clarify them and placed them <a href="http://zxq9.com/ryuq/">here</a>. This is mostly just a text-friendly way of writing relational algebra, but it has a few extras that would make it much nicer to use than SQL.</p>
<p>The mid-term goal is to implement at least some of the query language either as part of a runtime that sits outside of Postgres and feels like psql with something better than SQL, or hack an alternate parser into Postgres that would provide parsed Query trees to the optimizer. I haven&#8217;t decided which will be more time consuming in the long run just yet.</p>
<p>I&#8217;ll keep that area updated as I have time to work on the language spec more. There are quite a few ideas I&#8217;ve got left to commit to writing but just can&#8217;t yet due to time.</p>
]]></content:encoded>
			<wfw:commentRss>http://zxq9.com/archives/824/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Issues related to CAC on Linux (as of early 2013)</title>
		<link>http://zxq9.com/archives/812</link>
		<comments>http://zxq9.com/archives/812#comments</comments>
		<pubDate>Sun, 21 Apr 2013 05:35:25 +0000</pubDate>
		<dc:creator>zxq9</dc:creator>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[CAC]]></category>
		<category><![CDATA[coolkey]]></category>
		<category><![CDATA[DoD]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[login]]></category>
		<category><![CDATA[military]]></category>
		<category><![CDATA[pkcs11]]></category>

		<guid isPermaLink="false">http://zxq9.com/?p=812</guid>
		<description><![CDATA[Over the last month I&#8217;ve experienced a small explosion in email traffic about CAC on Linux, especially Ubuntu derivatives &#8212; most of it bad. It seems like some version of Firefox has a nasty bug where Firefox crashes hard as soon as you stick your CAC in the pooter, and then can&#8217;t get up again [...]]]></description>
				<content:encoded><![CDATA[<p>Over the last month I&#8217;ve experienced a small explosion in email traffic about <a href="http://zxq9.com/dodcac/">CAC on Linux</a>, <a href="http://zxq9.com/dodcac/U10.4-LTS-32/Ubuntu10.4-LTS-32.html">especially Ubuntu derivatives</a> &#8212; most of it bad. It seems like some version of Firefox has a nasty bug where Firefox crashes hard as soon as you stick your CAC in the pooter, and then can&#8217;t get up again for as long as your thing is in there.</p>
<p>There are a few other issues, but this is the worst one affecting the largest number of users. There are three problems I&#8217;ve got resolving this:</p>
<ol>
<li>The error reports I get in email are vague. Everything I know is written above. I don&#8217;t know what version, what derivative of Ubuntu (one guy said &#8220;Ubuntu&#8221; another said &#8220;Xubuntu&#8221;, another said &#8220;Kubuntu, I think&#8221;), what version of Firefox, what version of pcsc-tools, coolkey, etc. Nothing. Some of the reports may even be about CAC on SL6 for all I know, because nobody has told me anything.</li>
<li>I no longer have a CAC nor any involvement in CAC tool development. I&#8217;m out of the Army, nobody is going to pay me to deal with this (I&#8217;d love to, but I&#8217;ve gotta eat), and I&#8217;ve got no way to register for a CAC and all the different portal sites again to test it even if I had one.</li>
<li>I can&#8217;t update the instructionals for the current versions of the Fedora or Debian families because I can&#8217;t test anything without actually having a CAC and a place to use it (DTS, AKO, DKO, labs, etc.)&#8230; which sucks. I&#8217;m considering opening a wiki so the community can help itself maintain a current list of tutorials/versions. But I need help gardening it, and no volunteers have stepped forward.</li>
</ol>
<p>The second problem is, actually, the smaller of the two. If I know a bit more about the distros, packages and versions installed I can get a pretty good lock on where the problem is and raise the attention of people who <i>are</i> getting paid to maintain CAC tools and squish Firefox bugs.</p>
<p>If you know anything about this <i>please</i> leave a note below with more detailed information so we can start getting some direction on a fix.</p>
]]></content:encoded>
			<wfw:commentRss>http://zxq9.com/archives/812/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Version String Comparison in Python</title>
		<link>http://zxq9.com/archives/797</link>
		<comments>http://zxq9.com/archives/797#comments</comments>
		<pubDate>Sat, 20 Apr 2013 11:12:32 +0000</pubDate>
		<dc:creator>zxq9</dc:creator>
				<category><![CDATA[Computing]]></category>
		<category><![CDATA[comparison]]></category>
		<category><![CDATA[list comprehension]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[sorting]]></category>
		<category><![CDATA[version numbers]]></category>

		<guid isPermaLink="false">http://zxq9.com/?p=797</guid>
		<description><![CDATA[Comparing and sorting version strings in Python scripts has come up a few times lately. Here are some simple approaches. These examples assume that the version strings will be numeric representations and not include elements like &#8220;rc-1&#8243; or &#8220;alpha&#8221; or whatever. If your problem includes these kinds of elements, don&#8217;t worry &#8212; they are solvable [...]]]></description>
				<content:encoded><![CDATA[<p>Comparing and sorting version strings in Python scripts has come up a few times lately. Here are some simple approaches. These examples assume that the version strings will be numeric representations and not include elements like &#8220;rc-1&#8243; or &#8220;alpha&#8221; or whatever. If your problem includes these kinds of elements, don&#8217;t worry &#8212; they are solvable by applying a touch of regex-fu to the processes below.</p>
<p>First, sorting a bunch of version number strings. The problem is that many version strings reported by various packages are just that: strings. Usually the only way to get version information is to ask for it (&#8220;[prog] &#8211;version&#8221; in a shell script, or &#8220;SELECT version();&#8221; from a database, or whatever) and interpret whatever gets sent to stdout. Those strings don&#8217;t mean the same thing to comparison operators that they mean to us so long as they remain strings. For example, the string &#8217;3.5.10&#8242; i<em></em>s greater alphabetically than &#8217;3.15.1&#8242; but is a higher version. So we need to convert them to tuples of integers to make comparison of them natural (again, all these examples assume integer-only version strings, but minor changes to the process can allow you to compare anything &#8212; and assigning a custom collation order can allow you to sort against any arbitrary order of arbitrary symbols, but that&#8217;s beyond the scope of the basic nature of the problem I&#8217;m addressing here):</p>
<pre>&gt;&gt;&gt; vers
['3.5.10', '3.15.1', '2.5.7', '0.20.0', '2.12.5', '10.4.3']
&gt;&gt;&gt; s_vers = [tuple([int(x) for x in n.split('.')]) for n in vers]
&gt;&gt;&gt; s_vers
[(3, 5, 10), (3, 15, 1), (2, 5, 7), (0, 20, 0), (2, 12, 5), (10, 4, 3)]
&gt;&gt;&gt; vers[0] &gt; vers[1]
True
&gt;&gt;&gt; s_vers[0] &gt; s_vers[1]
False
&gt;&gt;&gt; cmp(vers[0], vers[1])
1
&gt;&gt;&gt; cmp(s_vers[0], s_vers[1])
-1
&gt;&gt;&gt; s_vers.sort()
&gt;&gt;&gt; s_vers
[(0, 20, 0), (2, 5, 7), (2, 12, 5), (3, 5, 10), (3, 15, 1), (10, 4, 3)]</pre>
<p>The list comprehension (actually, two nested list comprehensions) assignment to s_vers is the important part of this. Once that is done you can compare whatever you want. If the version number is buried as an element in a dict or larger list (likely) you can do this conversion in place by adding a new element to the contained structures and then sort the greater list based on that element:</p>
<pre>&gt;&gt;&gt; packages
[{'version': '3.5.10', 'name': 'foo'}, {'version': '3.15.1', 'name': 'foo'}, {'version': '2.5.7', 'name': 'foo'}, {'version': '0.20.0', 'name': 'foo'}, {'version': '2.12.5', 'name': 'foo'}, {'version': '10.4.3', 'name': 'foo'}]</pre>
<p>OK, that&#8217;s pretty ugly (uglier depending on how your browser renders &lt;pre&gt; type text), so I&#8217;ll print them in order so we can watch the list change more easily.</p>
<pre>&gt;&gt;&gt; for p in packages:
...   print p
...
{'version': '3.5.10', 'name': 'foo'}
{'version': '3.15.1', 'name': 'foo'}
{'version': '2.5.7', 'name': 'foo'}
{'version': '0.20.0', 'name': 'foo'}
{'version': '2.12.5', 'name': 'foo'}
{'version': '10.4.3', 'name': 'foo'}
&gt;&gt;&gt; for p in packages:
...   p.update({'version_tuple': tuple([int(x) for x in p['version'].split('.')])})
...
&gt;&gt;&gt; for p in packages:
...   print p
...
{'version_tuple': (3, 5, 10), 'version': '3.5.10', 'name': 'foo'}
{'version_tuple': (3, 15, 1), 'version': '3.15.1', 'name': 'foo'}
{'version_tuple': (2, 5, 7), 'version': '2.5.7', 'name': 'foo'}
{'version_tuple': (0, 20, 0), 'version': '0.20.0', 'name': 'foo'}
{'version_tuple': (2, 12, 5), 'version': '2.12.5', 'name': 'foo'}
{'version_tuple': (10, 4, 3), 'version': '10.4.3', 'name': 'foo'}
&gt;&gt;&gt; packages.sort(key = lambda x:x['version_tuple'])
&gt;&gt;&gt; for p in packages:
...   print p
...
{'version_tuple': (0, 20, 0), 'version': '0.20.0', 'name': 'foo'}
{'version_tuple': (2, 5, 7), 'version': '2.5.7', 'name': 'foo'}
{'version_tuple': (2, 12, 5), 'version': '2.12.5', 'name': 'foo'}
{'version_tuple': (3, 5, 10), 'version': '3.5.10', 'name': 'foo'}
{'version_tuple': (3, 15, 1), 'version': '3.15.1', 'name': 'foo'}
{'version_tuple': (10, 4, 3), 'version': '10.4.3', 'name': 'foo'}</pre>
<p>We started out with a list of dictionaries, each containing a package name and a version string. The first loop updates each dictionary to include a version tuple, and the next orders the dictionaries within the list by the tuple values. Viola! We have a list of dictionaries sorted by version number. Of course, if there are more than one package name involved you will want to sort on the package name first, then the version tuple as a secondary criteria (so you don&#8217;t compare versions of package &#8216;foo&#8217; against versions of package &#8216;bar&#8217;, or sort glibc against firefox, for example).</p>
<p>If lambdas are unfamiliar to you, don&#8217;t be scared off by the package.sort() line up there &#8212; lambdas are perfectly safe, reliable and quite concise once you understand the way they are used.</p>
<p>From here writing a sort function for lists of version strings should be pretty obvious. And&#8230; that means that writing a comparison function for two individual elements that works the same way the built-in cmp() function works is trivial:</p>
<pre>&gt;&gt;&gt; def ver_tuple(z):
...   return tuple([int(x) for x in z.split('.') if x.isdigit()])
...
&gt;&gt;&gt; def ver_cmp(a, b):
...   return cmp(ver_tuple(a), ver_tuple(b))
...
&gt;&gt;&gt; vers
['3.5.10', '3.15.1', '2.5.7', '0.20.0', '2.12.5', '10.4.3']
&gt;&gt;&gt; ver_cmp(vers[0], vers[1])
1
&gt;&gt;&gt; ver_cmp(vers[0], vers[0])
0
&gt;&gt;&gt; ver_cmp(vers[3], vers[4])
-1</pre>
<p>Nice and easy.</p>
<p>Now I can&#8217;t figure out why comparison functions I&#8217;ve seen floating around occupy so much space and are hard to follow &#8212; full of class declarations and exec loops within exec loops (!!!) and other nonsense. At the most you will need to add some regular expression matching to extract/split on the correct substrings from the version string. That means you would have to import the re module and the list comprehension will grow by a few (maybe 10) characters.</p>
]]></content:encoded>
			<wfw:commentRss>http://zxq9.com/archives/797/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>
