<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-27708939</id><updated>2012-01-23T03:49:52.944-05:00</updated><category term='NUnit'/><category term='Python'/><category term='MBunit'/><category term='TDD'/><category term='F# Linq'/><category term='NAntContrib'/><category term='SICP'/><category term='Subversion'/><category term='F# functional programming'/><category term='functional programming'/><category term='Huffman'/><category term='CruiseControl'/><category term='XML'/><category term='xslt'/><category term='F#'/><category term='NAnt'/><category term='F# SICP Huffman'/><title type='text'>SlideGuitarist</title><subtitle type='html'>La règle guérit tout. — Colette</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>60</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-27708939.post-2201777752934856166</id><published>2011-11-18T23:44:00.001-05:00</published><updated>2011-11-18T23:45:08.569-05:00</updated><title type='text'>I just gave money to Wikipedia</title><content type='html'>&lt;a href="https://wikimediafoundation.org/wiki/Support_Wikipedia/en"&gt;&lt;img border="0" alt="Support Wikipedia" src="//upload.wikimedia.org/wikipedia/commons/d/d3/Fundraising_2009-square-share-en.png" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-2201777752934856166?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/2201777752934856166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=2201777752934856166' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2201777752934856166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2201777752934856166'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2011/11/i-just-gave-money-to-wikipedia.html' title='I just gave money to Wikipedia'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-880806340088809014</id><published>2011-10-01T13:36:00.001-04:00</published><updated>2011-10-01T13:36:40.880-04:00</updated><title type='text'>XSLT to Produce Multiple Results from a Single Input</title><content type='html'>&lt;p&gt;I was recently confronted with some XML structured in this way: a &amp;lt;person&amp;gt; at the top, followed by 0 or more entities to whom the person has a certain sort of relationship. These may include other persons, and the same persons may appear in several input documents. I’d like to split up all these documents, and apply additional XSLTs to them. Moreover, I can only decide on where the output XML is going to go based on information not available to the XSLT processor, i.e. I can’t simply calculate a URI in &amp;lt;xsl:result-document&amp;gt; and let the processor open the file for me. This is the first time I’ve used the Saxon processor’s setOutputURIResolver() method; in fact, I didn’t know it existed until I did some hunting in Eclipse.&lt;/p&gt;  &lt;p&gt;Here’s what the input looks like, more or less:&lt;/p&gt;  &lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;&amp;lt;person-with-relationships&amp;gt;&lt;br /&gt; &amp;lt;person id=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;name&amp;gt;Anthony Albert Nassar&amp;lt;/name&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;phone-number&amp;gt;800-555-1212&amp;lt;/phone-number&amp;gt;&lt;br /&gt; &amp;lt;/person&amp;gt;&lt;br /&gt; &amp;lt;relationships&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;relationship&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;employment&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;start-date/&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;/employment&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;organization id=&amp;quot;2&amp;quot;&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;organization-name&amp;gt;Palantir Technologies, Inc.&amp;lt;/organization-name&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;url&amp;gt;palantir.com&amp;lt;/url&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;/organization&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;/relationship&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;relationship&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;marriage&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;start-date/&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;/marriage&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;person id=&amp;quot;3&amp;quot;&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;name&amp;gt;Donavan Arizmendi&amp;lt;/name&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;/person&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;/relationship&amp;gt;&lt;br /&gt; &amp;lt;/relationships&amp;gt;&lt;br /&gt;&amp;lt;/person-with-relationships&amp;gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The XSLT looks like this:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;&amp;lt;xsl:stylesheet version=&amp;quot;2.0&amp;quot; xmlns:xsl=&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot; xpath-default-namespace=&amp;quot;&amp;quot;&amp;gt;&lt;br /&gt; &amp;lt;xsl:import href=&amp;quot;identity.xslt&amp;quot;/&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;xsl:template match=&amp;quot;relationship/*[2]&amp;quot;&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;xsl:message&amp;gt;Opening document for &amp;lt;xsl:value-of select=&amp;quot;local-name()&amp;quot;/&amp;gt; with ID &amp;lt;xsl:value-of select=&amp;quot;@id&amp;quot;/&amp;gt;&amp;lt;/xsl:message&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;xsl:result-document href=&amp;quot;{@id}.xml&amp;quot;&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;xsl:apply-imports/&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;/xsl:result-document&amp;gt;&lt;br /&gt; &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;#160; &amp;lt;xsl:template match=&amp;quot;/person-with-relationships/person&amp;quot;&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;xsl:message&amp;gt;Opening document for person with ID &amp;lt;xsl:value-of select=&amp;quot;@id&amp;quot;/&amp;gt;&amp;lt;/xsl:message&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;xsl:result-document href=&amp;quot;{@id}.xml&amp;quot;&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;!-- Invoke the identity template, i.e. just copy this subtree to the output. --&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;!-- If you have some local template with lower priority that you'd like to &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; invoke, use &amp;lt;xsl:next-match/&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; --&amp;gt;&lt;br /&gt;&amp;#160;&amp;#160; &amp;lt;xsl:apply-imports/&amp;gt;&lt;br /&gt;&amp;#160; &amp;lt;/xsl:result-document&amp;gt;&lt;br /&gt; &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/xsl:stylesheet&amp;gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Let’s say that I want to turn each output in a DOM, or a dom4j Document, before I do anything else with it, i.e. I can’t just write the output to files. Moreover, I want to avoid overwriting files I’ve already created, &lt;i&gt;and &lt;/i&gt;I may need to aggregate information from all the inputs in a way not suited to XSLT (I could do some of this in XQuery…but I digress). The Java for this purpose might look like the following. I’m using dom4j to set the output subtrees aside. I could have used DOM (&lt;i&gt;quel horreur&lt;/i&gt;), or, probably, something in Saxon, but I actually wanted to stay closer to JAXP. So:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;package&lt;/b&gt; com.demo.xml;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;import&lt;/b&gt; java.io.File;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; java.io.IOException;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; java.util.concurrent.ConcurrentHashMap;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; java.util.concurrent.ConcurrentMap;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;import&lt;/b&gt; javax.xml.transform.Result;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; javax.xml.transform.Templates;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; javax.xml.transform.Transformer;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; javax.xml.transform.TransformerException;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; javax.xml.transform.sax.TransformerHandler;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; javax.xml.transform.stream.StreamResult;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; javax.xml.transform.stream.StreamSource;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;import&lt;/b&gt; net.sf.saxon.Controller;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; net.sf.saxon.FeatureKeys;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; net.sf.saxon.OutputURIResolver;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; net.sf.saxon.TransformerFactoryImpl;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; net.sf.saxon.event.SequenceWriter;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; net.sf.saxon.om.Item;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; net.sf.saxon.trans.XPathException;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;import&lt;/b&gt; org.apache.commons.io.FileUtils;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; org.dom4j.Document;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; org.dom4j.io.DocumentResult;&lt;br /&gt;&lt;b&gt;import&lt;/b&gt; org.dom4j.io.DocumentSource;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;import&lt;/b&gt; com.google.common.io.NullOutputStream;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;public&lt;/b&gt; &lt;b&gt;class&lt;/b&gt; InputSplitter {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;private&lt;/b&gt; &lt;b&gt;final&lt;/b&gt; Templates splitterTemplates;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // &lt;a href="http://dhruba.name/2009/08/05/concurrent-set-implementations-in-java-6/"&gt;http://dhruba.name/2009/08/05/concurrent-set-implementations-in-java-6/&lt;/a&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;private&lt;/b&gt; &lt;b&gt;final&lt;/b&gt; ConcurrentMap&amp;lt;String,DocumentResult&amp;gt; urisProcessed = &lt;b&gt;new&lt;/b&gt; ConcurrentHashMap&amp;lt;String,DocumentResult&amp;gt;();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;private&lt;/b&gt; &lt;b&gt;final&lt;/b&gt; TransformerFactoryImpl factory;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;public&lt;/b&gt; InputSplitter() &lt;b&gt;throws&lt;/b&gt; TransformerException {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; factory = &lt;b&gt;new&lt;/b&gt; TransformerFactoryImpl();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // I also have the requirement of removing elements with only &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // whitespace nodes among their descendants. This attribute&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // lets the parser throw away such whitespace nodes. The &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // XPath expression to discard elements with no content &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // then becomes trivial.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; factory.setAttribute(FeatureKeys.&lt;i&gt;STRIP_WHITESPACE&lt;/i&gt;, &amp;quot;all&amp;quot;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; File splitterXlstFile = &lt;b&gt;new&lt;/b&gt; File(&amp;quot;resources/splitter.xsl&amp;quot;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Calling newTemplates(), rather than newTransformer(), gives me &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // on thread-safe object that I can use repeatedly. Each time I &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // want to transform an input, I have to create a new Transformer.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;this&lt;/b&gt;.splitterTemplates = factory.newTemplates(&lt;b&gt;new&lt;/b&gt; StreamSource(splitterXlstFile));&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;public&lt;/b&gt; &lt;b&gt;void&lt;/b&gt; splitFile(File xmlFile) &lt;b&gt;throws&lt;/b&gt; TransformerException {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;final&lt;/b&gt; StreamSource xmlSource = &lt;b&gt;new&lt;/b&gt; StreamSource(xmlFile);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; TransformerHandler handler = factory.newTransformerHandler(splitterTemplates);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Transformer transformer = handler.getTransformer();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Controller controller = (Controller) transformer;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // You might not want an anonymous implementation of OutputURIResolver,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // but that's irrelevant to the example. In any case, this is Saxon's&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // back door to &amp;lt;xsl:result-document&amp;gt;.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; controller.setOutputURIResolver(&lt;b&gt;new&lt;/b&gt; OutputURIResolver() {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @Override&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;public&lt;/b&gt; &lt;b&gt;void&lt;/b&gt; close(Result result) &lt;b&gt;throws&lt;/b&gt; TransformerException {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // If you opened a Stream in resolve(), you'd want to close it&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // here.&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @Override&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;public&lt;/b&gt; Result resolve(String href, String base) &lt;b&gt;throws&lt;/b&gt; TransformerException {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; DocumentResult result = &lt;b&gt;new&lt;/b&gt; DocumentResult();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; DocumentResult existingResult = urisProcessed.putIfAbsent(href, result);&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;if&lt;/b&gt; (existingResult == &lt;b&gt;null&lt;/b&gt;) {&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;return&lt;/b&gt; result;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } &lt;b&gt;else&lt;/b&gt; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Throw the results away. There might be a way to implement &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // a null SAXResult, but I'll leave that as an exercise for the &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // reader.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;return&lt;/b&gt; &lt;b&gt;new&lt;/b&gt; StreamResult(&lt;b&gt;new&lt;/b&gt; NullOutputStream());&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }});&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; controller.setMessageEmitter(&lt;b&gt;new&lt;/b&gt; SequenceWriter() { &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; @Override&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;public&lt;/b&gt; &lt;b&gt;void&lt;/b&gt; write(Item item) &lt;b&gt;throws&lt;/b&gt; XPathException {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; System.&lt;i&gt;out&lt;/i&gt;.println(item.getStringValue())&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }});&lt;br /&gt;              // Discard the output from the entire document.&lt;br /&gt;              transformer.transform(xmlSource, &lt;b&gt;new&lt;/b&gt; StreamResult(&lt;b&gt;new&lt;/b&gt; NullOutputStream()));&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;public&lt;/b&gt; &lt;b&gt;void&lt;/b&gt; transformFolder(File folder) &lt;b&gt;throws&lt;/b&gt; TransformerException {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;for&lt;/b&gt; (File xmlFile : folder.listFiles()) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; splitFile(xmlFile);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;static&lt;/b&gt; &lt;b&gt;public&lt;/b&gt; &lt;b&gt;void&lt;/b&gt; main(String[] args) &lt;b&gt;throws&lt;/b&gt; TransformerException, IOException {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; InputSplitter splitter = &lt;b&gt;new&lt;/b&gt; InputSplitter();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;assert&lt;/b&gt; args.length == 2;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; File inputXml = &lt;b&gt;new&lt;/b&gt; File(args[0]);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; splitter.splitFile(inputXml);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;final&lt;/b&gt; File outputDirectory = &lt;b&gt;new&lt;/b&gt; File(args[1]);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;if&lt;/b&gt; (!outputDirectory.mkdirs())&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; FileUtils.&lt;i&gt;cleanDirectory&lt;/i&gt;(outputDirectory);&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;b&gt;for&lt;/b&gt; (String entry : splitter.urisProcessed.keySet()) {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; File outputFile = &lt;b&gt;new&lt;/b&gt; File(outputDirectory, entry);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; // Use the identity transform to turn the dom4j tree into a file.&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Transformer newTransformer = splitter.factory.newTransformer();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; newTransformer.setOutputProperty(&amp;quot;indent&amp;quot;, &amp;quot;yes&amp;quot;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Document document = splitter.urisProcessed.get(entry).getDocument();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; newTransformer.transform(&lt;b&gt;new&lt;/b&gt; DocumentSource(document), &lt;b&gt;new&lt;/b&gt; StreamResult(outputFile));&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;}&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-880806340088809014?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/880806340088809014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=880806340088809014' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/880806340088809014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/880806340088809014'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2011/10/xslt-to-produce-multiple-results-from.html' title='XSLT to Produce Multiple Results from a Single Input'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-1454684972909258954</id><published>2011-09-08T11:53:00.003-04:00</published><updated>2011-09-08T12:51:02.373-04:00</updated><title type='text'>Tokenizing a String with Oracle SQL</title><content type='html'>This problem actually comes up pretty frequently for me. Audit log records at my place of employment are written to the DB. I often get requests to pull out and aggregate the objects IDs in a set of rows. The IDs are space-separated within a VARCHAR2 column. The details aren't that interesting, though. &lt;br /&gt;&lt;br /&gt;The first trick to know is the by-now conventional way of generating a sequence of integers in Oracle SQL:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT ROWNUM i FROM DUAL CONNECT BY LEVEL &lt;= 10;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The 10 above is a necessary but arbitrary cutoff. My sample data happens to have &lt; 10 tokens per row; if there were more, I'd boost the cutoff. Anyway, the next trick to know concerns Oracle's REGEXP_SUBSTR() function, namely that it has an optional argument for the match. You can see where this is going, right? If I JOIN each row of the audit log to the sequence of integers, then I can use the latter integers as match indexes. &lt;br /&gt;&lt;br /&gt;Since Oracle's regex implementation doesn't include look-ahead operators, the token separator will be part of the match, and I'll have to remove it, hence TRIM(). If your data is comma-separated, your SQL will look a little different. But enough:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT * FROM (&lt;br /&gt;  SELECT s, i, TRIM(REGEXP_SUBSTR(s, '\d+( |$)', 1, i)) token FROM (&lt;br /&gt;    SELECT '123 456' FROM DUAL&lt;br /&gt;    UNION&lt;br /&gt;    SELECT '789 101112' FROM DUAL&lt;br /&gt;), (&lt;br /&gt;  SELECT ROWNUM i FROM DUAL CONNECT BY LEVEL &lt;= 10&lt;br /&gt;  )&lt;br /&gt;) WHERE token IS NOT NULL;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-1454684972909258954?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/1454684972909258954/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=1454684972909258954' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1454684972909258954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1454684972909258954'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2011/09/tokenizing-string-with-oracle-sql.html' title='Tokenizing a String with Oracle SQL'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-6958367187025699523</id><published>2011-09-07T22:31:00.001-04:00</published><updated>2011-09-07T22:36:05.649-04:00</updated><title type='text'>Refactoring Groovy to Generate XML</title><content type='html'>There are tons of examples out there about how to generate XML using Groovy’s builders. The usual pattern is use StreamingMarkupBuilder, then create a massive nested closure resembling almost exactly the XML you want as output, then passing that to StreamingMarkupBuilder#bind(). This does create problems, though. The first is that even when the closure represents the structure of a single object with a data source and a few properties, it's already pretty big. The second is that it quickly gets cluttered with programmatic logic: checks for invalid property values, calls to some external function to translate or normalize some input, base-64-encoding of raw text or the content of external binary files, etc. I finally found out how to avoid these problems, after hours of trial and error. I might have saved myself that time if I’d just looked at the code for StreamingMarkupBuilder, but that’s life. In the following: &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import groovy.xml.*&lt;br /&gt;&lt;br /&gt;// Note that this function has no dependency on the instance of StreamingMarkupBuilder, below.&lt;br /&gt;def createPersonMarkup(builder, name, occupation, age) {&lt;br /&gt;    // Putting this check inside a function means I can just return, without&lt;br /&gt;    // generating *anything*, yet not add a nesting level to my code.&lt;br /&gt;        if (!value)&lt;br /&gt;         return&lt;br /&gt;    assert name // This assertion will fire only when the closure is bound!&lt;br /&gt;    // Note the use of the "Elvis operator" to avoid a null attribute value.&lt;br /&gt;    builder.person(occupation: (occupation ?: ‘Unemployed’)) {&lt;br /&gt;         builder.name(name)&lt;br /&gt;         if (occupation)&lt;br /&gt;           builder.occupation(occupation)&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;builder = new StreamingMarkupBuilder()&lt;br /&gt;&lt;br /&gt;xml = builder.bind { &lt;br /&gt;    // This is the strange part: the builder actually gets passed into each closure,&lt;br /&gt;    // but you have to declare a closure argument to get at it. You can't rely on the&lt;br /&gt;    // variable declaration for "builder," above, because that binding is no longer available&lt;br /&gt;    // when the Builder actually constructs the XML, and you'll get some hellacious error &lt;br /&gt;    // meaning, basically, "unbound variable name 'builder'".&lt;br /&gt;    persons { builder -&gt; &lt;br /&gt;        createPersonMarkup builder, 'Anthony Albert Nassar', null, 49&lt;br /&gt;        createPersonMarkup builder, ‘Donavan Arizmendi’, ‘Teacher’, 40&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;XmlUtil.serialize(xml, System.out)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you don't name the single closure argument, it must already be available as "it," and so it is in this case. This code works:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xml = builder.bind {&lt;br /&gt;    palantir {&lt;br /&gt;       createPropertyAsRawValue it, 'com.palantir.property.Name', 'Anthony Nassar', null&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;XmlUtil.serialize(xml, System.out)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So that's how the StreamingMarkupBuilder works: it interprets the strings that you intend as element names, as method invocations, and tries to invoke them on itself. The builder itself is always the first argument to any of these methods, and it passes itself into whatever methods (i.e. nested elements) are invoked in turn. When you call bind(), it intercepts all these method calls to generate XML.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-6958367187025699523?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/6958367187025699523/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=6958367187025699523' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/6958367187025699523'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/6958367187025699523'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2011/09/refactoring-groovy-to-generate-xml.html' title='Refactoring Groovy to Generate XML'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-5951742961361602120</id><published>2011-03-27T09:56:00.001-04:00</published><updated>2011-03-27T09:57:56.946-04:00</updated><title type='text'>Exporting Word Documents to HTML</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;I mean real HTML. Here's what I get if I save &lt;em&gt;this&lt;/em&gt; blog entry (which I'm editing in Word 2007) as HTML:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" &lt;br/&gt;        xmlns:m=&lt;a href='http://schemas.microsoft.com/office/2004/12/omml'&gt;&lt;span style='color:blue; text-decoration:underline'&gt;http://schemas.microsoft.com/office/2004/12/omml&lt;/span&gt;&lt;/a&gt;&lt;br/&gt;        xmlns="http://www.w3.org/TR/REC-html40"&amp;gt;&lt;br/&gt;    &amp;lt;head&amp;gt;&lt;br/&gt;        &amp;lt;meta http-equiv=Content-Type content="text/html; charset=windows-1252"&amp;gt;&lt;br/&gt;        &amp;lt;meta name=ProgId content=Word.Document&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;If I save as "filtered HTML," I get something similar:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;html&amp;gt;&lt;br/&gt;    &amp;lt;head&amp;gt;&lt;br/&gt;        &amp;lt;meta http-equiv=Content-Type content="text/html; charset=windows-1252"&amp;gt;&lt;br/&gt;        &amp;lt;meta name=Generator content="Microsoft Word 12 (filtered)"&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;In the first case, I get a namespace declaration that I don't want. What I want is legitimate XHTML (&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;&lt;span style='color:#000090'&gt;http://www.w3.org/1999/xhtml&lt;/span&gt;)&lt;/span&gt;. C'mon, people, it's 2011! I don't care who's using browsers or authoring tools that can't handle it; they should upgrade. What I really, really, really want, however, is &lt;em&gt;not&lt;/em&gt; to export with the Windows-1252 character encoding. However, there seems to be no way to prevent that if it's the default encoding on your Windows machine. If you export to plain text, you can specify an encoding, but not if you export to HTML (as far as I can tell). Weird. What I also really, really, really want is attribute values enclosed in quotation marks, as has been the standard for, oh, about 12 years. If I attempted (I did attempt it, actually) to further process this HTML &lt;em&gt;as&lt;/em&gt; XML, any proper XML parser would choke on it.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;There are parsers, however, that will read this garbage and turn it into legitimate XHTML. I prefer &lt;a href='http://ccil.org/~cowan/XML/tagsoup/'&gt;TagSoup&lt;/a&gt; for this purpose. John Cowan, the author, recommends that you &lt;em&gt;not&lt;/em&gt; use the JAXP interface, but that's exactly what I want. I'll demonstrate below.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Let me backtrack for a minute. A good part of my job at &lt;a href='http://palantir.com/careers/positions'&gt;Palantir&lt;/a&gt; requires me to deal with unfriendly input and output formats. A lot of these formats are XML, and usually not very nice XML (if there's a schema, it's almost certain to be misleading). So I've gotten used to dealing with character encodings, and I've gravitated toward Groovy for a lot of my work, as I can often whip something up in minutes without needing an IDE. A colleague recently asked me, quite reasonably, if it would be "easy" to convert Word documents automatically to wiki markup. "Sure!" I said. It was not easy, as it turned out. &lt;br /&gt;&lt;/p&gt;&lt;p&gt;The first step was to convert every Word document on hand to HTML. I use Groovy+Scriptom to do this:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;import org.codehaus.groovy.scriptom.ActiveXObject&lt;br/&gt;import org.codehaus.groovy.scriptom.Scriptom&lt;br/&gt;import org.codehaus.groovy.scriptom.tlb.office.word.WdSaveFormat&lt;br/&gt;import org.codehaus.groovy.scriptom.tlb.office.MsoEncoding&lt;br/&gt;&lt;br/&gt;userHome = new File(System.properties.'user.home')&lt;br/&gt;myDocuments = new File(userHome, 'My Documents')&lt;br/&gt;output = new File(userHome, "Desktop/Tony's Output")&lt;br/&gt;&lt;br/&gt;def word = new ActiveXObject('Word.Application')&lt;br/&gt;Scriptom.inApartment {&lt;br/&gt;    try {&lt;br/&gt;        word.Visible = false // ('Visible', new Variant(false))&lt;br/&gt;        word.DisplayAlerts = false // ', new Variant(false))&lt;br/&gt;        def documents = word.Documents // ').toDispatch()&lt;br/&gt;        myDocuments.eachFileMatch ~/.*\.docx/, { doc -&amp;gt;    &lt;br/&gt;            println "Opening $doc"&lt;br/&gt;            documents.Open doc.absolutePath&lt;br/&gt;            def activeDocument = word.ActiveDocument&lt;br/&gt;            assert activeDocument&lt;br/&gt;            try {&lt;br/&gt;                activeDocument.AcceptAllRevisions()&lt;br/&gt;                def html = new File(output, doc.name - ~/\.docx$/ + '.html')&lt;br/&gt;                // 7 is the magic number for Unicode text. See MSFT's docs for WdSaveFormatEnumeration. &lt;br/&gt;                // http://msdn.microsoft.com/en-us/library/bb238158%28office.12%29.aspx&lt;br/&gt;                // 17 is PDF; 16 is "default," thus Office 2007. &lt;br/&gt;                // wdFormatHTML = 8&lt;br/&gt;                def n = Scriptom.MISSING&lt;br/&gt;                activeDocument.SaveAs html.absolutePath, WdSaveFormat.wdFormatFilteredHTML, false, n, n, n, n, n, n, n, n, MsoEncoding.msoEncodingUTF8&lt;br/&gt;            } finally {&lt;br/&gt;                activeDocument.Close()&lt;br/&gt;            }&lt;br/&gt;        } // each&lt;br/&gt;    } finally {&lt;br/&gt;        // This is the Office automation API call, which Scriptom resolves for you.&lt;br/&gt;        word.Quit()    &lt;br/&gt;        // Apparently it now works: winword.exe disappears from my Task Manager, which is what I want.&lt;br/&gt;    }&lt;br/&gt;} // Close apartment.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;I'm not going to explain Scriptom here. Suffice it to say that the code above does roughly what VBA would do. Scriptom can use the constants defined at &lt;a href='http://msdn.microsoft.com/en-US/library/microsoft.office.interop.word.wdsaveformat.aspx'&gt;http://msdn.microsoft.com/en-US/library/microsoft.office.interop.word.wdsaveformat.aspx&lt;/a&gt; because someone was thoughtful enough to copy them to &lt;a href='http://groovy.codehaus.org/modules/scriptom/1.6.0/scriptom-office-2K3-tlb/apidocs/org/codehaus/groovy/scriptom/tlb/office/word/WdSaveFormat.html'&gt;http://groovy.codehaus.org/modules/scriptom/1.6.0/scriptom-office-2K3-tlb/apidocs/org/codehaus/groovy/scriptom/tlb/office/word/WdSaveFormat.html&lt;/a&gt;. Unfortunately, my attempt to specify the encoding for the output file failed, and I could have left off all the arguments to SaveAs() after the first two. &lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;Now, unfortunately, I've got a folder of bad HTML. How do I turn that into XHTML (actually, I wanted to run that through a further XSLT, to produce wiki markup)? Like this:&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;import org.ccil.cowan.tagsoup.Parser&lt;br/&gt;import org.xml.sax.*&lt;br/&gt;import javax.xml.transform.*&lt;br/&gt;import javax.xml.transform.sax.SAXSource&lt;br/&gt;import javax.xml.transform.stream.StreamResult&lt;br/&gt;import javax.xml.transform.stream.StreamSource&lt;br/&gt;&lt;br/&gt;output.eachFileMatch ~/.*\.html/, { html -&amp;gt;&lt;br/&gt;        def transformer = TransformerFactory.newInstance().newTransformer()&lt;br/&gt;        new File(html.parentFile, html.name - ~/html$/ + 'xhtml').withWriter 'UTF-8', { writer -&amp;gt;&lt;br/&gt;            html.withReader 'Windows-1252', { reader -&amp;gt;&lt;br/&gt;                transformer.transform(new SAXSource(new Parser(), new InputSource(reader)), new StreamResult(writer));&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;TransformerFactory#newInstance() simply returns the "identity transform," which is what I want: I don't want to change the structure of the XML at all. &lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-5951742961361602120?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/5951742961361602120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=5951742961361602120' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5951742961361602120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5951742961361602120'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2011/03/exporting-word-documents-to-html.html' title='Exporting Word Documents to HTML'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-2064336573040168895</id><published>2011-03-26T15:00:00.001-04:00</published><updated>2011-03-26T15:00:50.433-04:00</updated><title type='text'>Interval Coalesce with XSLT</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;I was intrigued by Vadim Tropashko's &lt;em&gt;SQL Design Patterns&lt;/em&gt;. I'm not a SQL guy, so like lots of developers, I think of databases as places to put data, from which to retrieve it, and which occasionally need tuning. I don't think of the execution engine as something that virtually can create additional information for me. I've written maybe two pivot queries in my life, for example. Someone who's adept with Mathematica, or Excel for that matter, can do things in a minute that would take a Java developer days, since a SQL rowset is a very impoverished data structure in Java.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Anyway, I saw an immediate use for Tropashko's "interval coalesce" algorithm. The problem is, given a collection of intervals, to coalesce those that overlap, and thus produce a small collection of non-overlapping intervals. Well, "immediate" is misleading; I waited a year to do this. Anyway, I fired up Oracle XE and made up some data. Then I entered the query on p. 37…only to find out that there's a typo in it. Several hours later, I figured out where it was:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;SELECT fst.x, lst.y -- Find two endpoints.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;FROM intervals fst, intervals lst WHERE fst.x &amp;lt; lst.y&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;AND NOT EXISTS ( -- There's no interval beginning between these endpoints...&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;  SELECT * FROM intervals i&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;  WHERE i.x &amp;gt; fst.x AND i.x &amp;lt; lst.y&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;  AND NOT EXISTS ( -- ...for which there's no covering interval.&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;    SELECT * FROM intervals cov &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;    WHERE i.x &amp;gt; cov.x AND i.x &amp;lt;= cov.y&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;  )&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;) AND NOT EXISTS (&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;  SELECT * FROM intervals cov&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;  WHERE cov.x &amp;lt; fst.x AND fst.x &amp;lt;= cov.y&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;  OR cov.x &amp;lt;= lst.y AND lst.y &amp;lt; cov.y&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;pre&gt;&lt;code&gt;)&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;You might notice a discrepancy from the query in the book...and I've reported the erratum! &lt;br /&gt;&lt;/p&gt;&lt;p&gt;Tropashko goes on to show a more elegant and efficient way to produce this result, but at this point I wanted to get back to my own practical problem, which was to coalesce intervals defined in an XML document. Since the query above involves three self-joins, I could expect n^4 performance if I simply turned Tropashko's SQL into XPath. However, an XML document is inherently ordered (there's no ANSI SQL equivalent to following-sibling::*[1]). If I sorted the intervals according to their left end, i.e. wrote an XSLT to preprocess the input, then I could simply iterate through the intervals one time, gradually coalescing them where possible. The code falls into a pattern familiar to functional programmers. In other words, I apply a template to the first interval in the document, and that template calls a second template that uses "accumulators" for the current coalesced interval. This pattern is also used in Jeni Tennison's (http://www.jenitennison.com/xslt/index.html) &amp;lt;span style="font-style: italic;"&amp;gt;XSLT on the Edge&amp;lt;/span&amp;gt; as a way of grouping adjacent elements. I wanted to avoid XPath stunts like Tropashko's SQL, because I will have to apply this transform to documents with 10s of 1000s of data points. I very deliberate select only following-sibling::*[1].&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Since blogger's editor widget hacks up my XSLT in horrifying ways, and I can't attach text files, I had to format this XSTL with non-breaking spaces and whatnot. Let me know if you want the real thing. Let's say the input looks like this (it &lt;em&gt;must&lt;/em&gt; be sorted, perhaps by another XSLT, by starting time only):&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#602020'&gt;?xml version='1.0' ?&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;intervals&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;interval &lt;/span&gt;&lt;span style='color:#d00020'&gt;x=&lt;/span&gt;&lt;span style='color:#000090'&gt;"10"&lt;/span&gt;&lt;span style='color:#d00020'&gt; y=&lt;/span&gt;&lt;span style='color:#000090'&gt;"14.1155607415243584888223696987094362306"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;interval &lt;/span&gt;&lt;span style='color:#d00020'&gt;x=&lt;/span&gt;&lt;span style='color:#000090'&gt;"10"&lt;/span&gt;&lt;span style='color:#d00020'&gt; y=&lt;/span&gt;&lt;span style='color:#000090'&gt;"27.2574271976039591982711142331954001295"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;interval &lt;/span&gt;&lt;span style='color:#d00020'&gt;x=&lt;/span&gt;&lt;span style='color:#000090'&gt;"30"&lt;/span&gt;&lt;span style='color:#d00020'&gt; y=&lt;/span&gt;&lt;span style='color:#000090'&gt;"33.7147910106524433624672477551503835313"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;interval &lt;/span&gt;&lt;span style='color:#d00020'&gt;x=&lt;/span&gt;&lt;span style='color:#000090'&gt;"40"&lt;/span&gt;&lt;span style='color:#d00020'&gt; y=&lt;/span&gt;&lt;span style='color:#000090'&gt;"46.844920420920822280815378491005656766"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;interval &lt;/span&gt;&lt;span style='color:#d00020'&gt;x=&lt;/span&gt;&lt;span style='color:#000090'&gt;"50"&lt;/span&gt;&lt;span style='color:#d00020'&gt; y=&lt;/span&gt;&lt;span style='color:#000090'&gt;"61.30421963829719394371538034317434986025"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;				&lt;/span&gt;&lt;/p&gt;&lt;p&gt;…&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Then the XSLT to coalesce these intervals will look about like this:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#602020'&gt;?xml version="1.0"?&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:stylesheet &lt;/span&gt;&lt;span style='color:#d00020'&gt;version=&lt;/span&gt;&lt;span style='color:#000090'&gt;"2.0"&lt;/span&gt;&lt;span style='color:#d00020'&gt;&lt;br /&gt;						&lt;br/&gt;    xmlns:xsl=&lt;/span&gt;&lt;span style='color:#000090'&gt;"http://www.w3.org/1999/XSL/Transform"&lt;/span&gt;&lt;span style='color:#d00020'&gt;&lt;br /&gt;						&lt;br/&gt;    xmlns:xs=&lt;/span&gt;&lt;span style='color:#000090'&gt;"http://www.w3.org/2001/XMLSchema"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:output &lt;/span&gt;&lt;span style='color:#d00020'&gt;indent=&lt;/span&gt;&lt;span style='color:#000090'&gt;"yes"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#aaaba0'&gt;!-- I could have named this template "start-recursion", but it's &lt;br/&gt;        convenient to be able to set the context node. --&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:template &lt;/span&gt;&lt;span style='color:#d00020'&gt;match=&lt;/span&gt;&lt;span style='color:#000090'&gt;"interval"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:call-template &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"coalesce-intervals"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:with-param &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"next-interval"&lt;/span&gt;&lt;span style='color:#d00020'&gt; select=&lt;/span&gt;&lt;span style='color:#000090'&gt;"."&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:with-param &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"from"&lt;/span&gt;&lt;span style='color:#d00020'&gt; select=&lt;/span&gt;&lt;span style='color:#000090'&gt;"number(@x)"&lt;/span&gt;&lt;span style='color:#d00020'&gt; tunnel=&lt;/span&gt;&lt;span style='color:#000090'&gt;"yes"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:with-param &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"to"&lt;/span&gt;&lt;span style='color:#d00020'&gt; select=&lt;/span&gt;&lt;span style='color:#000090'&gt;"number(@y)"&lt;/span&gt;&lt;span style='color:#d00020'&gt; tunnel=&lt;/span&gt;&lt;span style='color:#000090'&gt;"yes"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:call-template&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:template&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:template &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"coalesce-intervals"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:param &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"next-interval"&lt;/span&gt;&lt;span style='color:#d00020'&gt; as=&lt;/span&gt;&lt;span style='color:#000090'&gt;"element(interval)?"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:param &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"from"&lt;/span&gt;&lt;span style='color:#d00020'&gt; as=&lt;/span&gt;&lt;span style='color:#000090'&gt;"xs:double"&lt;/span&gt;&lt;span style='color:#d00020'&gt; tunnel=&lt;/span&gt;&lt;span style='color:#000090'&gt;"yes"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:param &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"to"&lt;/span&gt;&lt;span style='color:#d00020'&gt; as=&lt;/span&gt;&lt;span style='color:#000090'&gt;"xs:double"&lt;/span&gt;&lt;span style='color:#d00020'&gt; tunnel=&lt;/span&gt;&lt;span style='color:#000090'&gt;"yes"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:choose&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#aaaba0'&gt;!-- Stop the recursion. --&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:when &lt;/span&gt;&lt;span style='color:#d00020'&gt;test=&lt;/span&gt;&lt;span style='color:#000090'&gt;"not($next-interval)"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;interval &lt;/span&gt;&lt;span style='color:#d00020'&gt;x=&lt;/span&gt;&lt;span style='color:#000090'&gt;"{$from}"&lt;/span&gt;&lt;span style='color:#d00020'&gt; y=&lt;/span&gt;&lt;span style='color:#000090'&gt;"{$to}"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:when&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#aaaba0'&gt;!-- The current coalesced interval overlaps this one, so move on. --&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:when &lt;/span&gt;&lt;span style='color:#d00020'&gt;test=&lt;/span&gt;&lt;span style='color:#000090'&gt;"$to gt number($next-interval/@x)"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:call-template &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"coalesce-intervals"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:with-param &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"next-interval"&lt;/span&gt;&lt;span style='color:#d00020'&gt; select=&lt;/span&gt;&lt;span style='color:#000090'&gt;"$next-interval/following-sibling::interval[1]"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#aaaba0'&gt;!-- Extend the current interval if possible (hence max). --&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:with-param &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"to"&lt;/span&gt;&lt;span style='color:#d00020'&gt; select=&lt;/span&gt;&lt;span style='color:#000090'&gt;"max((number($next-interval/@y), $to))"&lt;/span&gt;&lt;span style='color:#d00020'&gt; tunnel=&lt;/span&gt;&lt;span style='color:#000090'&gt;"yes"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:call-template&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:when&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:otherwise&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#aaaba0'&gt;!-- No more to coalesce. Output the "accumulator" and start again. --&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;interval &lt;/span&gt;&lt;span style='color:#d00020'&gt;x=&lt;/span&gt;&lt;span style='color:#000090'&gt;"{$from}"&lt;/span&gt;&lt;span style='color:#d00020'&gt; y=&lt;/span&gt;&lt;span style='color:#000090'&gt;"{$to}"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:apply-templates &lt;/span&gt;&lt;span style='color:#d00020'&gt;select=&lt;/span&gt;&lt;span style='color:#000090'&gt;"$next-interval"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:otherwise&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:choose&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:template&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:template &lt;/span&gt;&lt;span style='color:#d00020'&gt;match=&lt;/span&gt;&lt;span style='color:#000090'&gt;"/intervals"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;intervals&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:apply-templates &lt;/span&gt;&lt;span style='color:#d00020'&gt;select=&lt;/span&gt;&lt;span style='color:#000090'&gt;"interval[1]"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;/intervals&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;br /&gt;					&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:template&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:stylesheet&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;				&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-2064336573040168895?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/2064336573040168895/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=2064336573040168895' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2064336573040168895'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2064336573040168895'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2011/03/interval-coalesce-with-xslt_26.html' title='Interval Coalesce with XSLT'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-1297872438709889143</id><published>2009-08-21T08:50:00.002-04:00</published><updated>2009-08-21T09:20:43.048-04:00</updated><title type='text'>Converting Oracle TIMESTAMP WITH TIME ZONE to Current TZ</title><content type='html'>&lt;span style="font-family: trebuchet ms;"&gt;I see answers to this question all over the Web, usually involving some arithmetic with abbreviations such as "EDT", the use of hacky Oracle extension functions such as NEW_TIME, etc. Forget about that. Here's how you do it: use CAST(). If this seems pedantic, it's not. I recently had to deal with an Oracle database in which times were stored as epoch times, i.e. milliseconds since January 1, 1970 UTC (e.g. new java.util.Date().getTime()). So you can get the beginning of the epoch in Oracle SQL like so:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;SELECT TIMESTAMP '1970-01-01 00:00:00 +00:00' FROM DUAL;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: trebuchet ms;"&gt;That'll get you a timestamp in terms of Greenwich Mean Time (GMT or UTC):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: trebuchet ms;"&gt;SELECT TO_CHAR(TIMESTAMP '1970-01-01 00:00:00 +00:00', 'TZR') FROM DUAL;&lt;br /&gt;&lt;br /&gt;If I've got "epoch time" in ms, I can easily convert it to a timestamp relative to UTC:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;SELECT TIMESTAMP '1970-01-01 00:00:00 +00:00' + NUMTODSINTERVAL(epoch_time / 1000, 'SECOND') FROM my_table;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;How do I then display, and especially export these values relative to my current time zone? Casting to TIMESTAMP WITH LOCAL TIME ZONE doesn't quite work; by default, the time zone isn't displayed for this column, and the 'TZR' format doesn't work on it (I get an Oracle error, which confused me. So CAST() them to TIMESTAMP WITH LOCAL TIME ZONE, then CAST() to TIMESTAMP WITH TIME ZONE. Oracle does all the work, and I don't have to hard-code any information in the query about my time zone, or rely on EXTRACT(). So:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;SELECT CAST(CAST(TIMESTAMP '1970-01-01 00:00:00 +00:00' AS TIMESTAMP WITH LOCAL TIME ZONE) AS TIMESTAMP WITH TIME ZONE) FROM DUAL;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;&lt;span style="font-family: trebuchet ms;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-1297872438709889143?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/1297872438709889143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=1297872438709889143' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1297872438709889143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1297872438709889143'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2009/08/converting-oracle-timestamp-with-time.html' title='Converting Oracle TIMESTAMP WITH TIME ZONE to Current TZ'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-5196172319936420295</id><published>2009-07-22T00:36:00.005-04:00</published><updated>2009-08-01T18:43:59.930-04:00</updated><title type='text'>An XSLT to Extract Text from Word</title><content type='html'>&lt;p&gt;Well, this turns out to be even easier. If you've got the text:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt;&amp;lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&lt;br /&gt;       xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"&lt;br /&gt;       version="1.0"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xsl:output method="text" encoding="UTF-8" /&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xsl:template match="*"&amp;gt;&lt;br /&gt;    &amp;lt;!-- Simply recurse over the children. --&amp;gt;&lt;br /&gt;    &amp;lt;xsl:apply-templates /&amp;gt;&lt;br /&gt;  &amp;lt;/xsl:template&amp;gt;&lt;br /&gt; &lt;br /&gt;  &amp;lt;!-- Any piece of text from the .docx is enclosed in a w:t element. --&amp;gt;&lt;br /&gt;  &amp;lt;xsl:template match="w:t"&amp;gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="."/&amp;gt;&lt;br /&gt;    &amp;lt;!-- Look for the xml:space attribute. A namespace is not necessary. --&amp;gt;&lt;br /&gt;    &amp;lt;!-- XPath 1.0 doesn't have the starts-with() and ends-with() functions, unfortunately. --&amp;gt;&lt;br /&gt;    &amp;lt;xsl:if test="@space = 'preserve'"&amp;gt;    &lt;br /&gt;   &amp;lt;xsl:text&amp;gt; &amp;lt;/xsl:text&amp;gt;&lt;br /&gt;    &amp;lt;/xsl:if&amp;gt;&lt;br /&gt;  &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;  &lt;br /&gt;  &amp;lt;xsl:template match="w:p"&amp;gt;&lt;br /&gt;    &amp;lt;xsl:apply-templates /&amp;gt;&lt;br /&gt;    &amp;lt;xsl:text&amp;gt;&amp;amp;#xa;&amp;lt;/xsl:text&amp;gt;&lt;br /&gt;  &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;  &lt;br /&gt; &amp;lt;/xsl:stylesheet&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;To get the text, here's some Groovy:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def createDocumentXml(f) {&lt;br /&gt;    def zip = new ZipFile(f)&lt;br /&gt;    def entry = zip.getEntry('word/document.xml')&lt;br /&gt;    assert entry&lt;br /&gt;    def xml =  new File(f.absolutePath - '.docx' + '.xml')&lt;br /&gt;    // I want to copy the XML to a file, for purposes of visual inspection.&lt;br /&gt;    // In production code I'd simply return the stream.&lt;br /&gt;    zip.getInputStream(entry).withStream { i -&amp;gt;&lt;br /&gt;        xml.withOutputStream { o -&amp;gt;&lt;br /&gt;            o &amp;lt;&amp;lt; i&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return xml&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;To apply the XSLT to the document, see the attachments. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-5196172319936420295?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/5196172319936420295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=5196172319936420295' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5196172319936420295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5196172319936420295'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2009/07/xslt-to-extract-text-from-word.html' title='An XSLT to Extract Text from Word'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-4566753909229074119</id><published>2009-05-02T14:14:00.007-04:00</published><updated>2009-05-02T15:01:54.289-04:00</updated><title type='text'>Extracting the Text of an HTML Document</title><content type='html'>This is something I often have to do within an XSLT: there's some base-64 encoded HTML in a text node, and I'd like to extract the body text. Saxon offers saxon:parse() and saxon:base64Binary-to-string() that might be useful here. If you use the &lt;a href="http://home.ccil.org/%7Ecowan/XML/tagsoup/"&gt;TagSoup &lt;/a&gt;parser to turn possibly nasty HTML into XHTML, then you can extract the text from the "thing" element with&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;&lt;span style="color: rgb(0, 0, 153);"&gt;xsl:variable&lt;/span&gt; name="html" select="&lt;span style="color: rgb(0, 102, 0);"&gt;saxon:parse(saxon:base64-to-string(xs:base64(thing)))&lt;/span&gt;"/&amp;gt;&lt;br /&gt;&amp;lt;&lt;span style="color: rgb(0, 0, 153);"&gt;xsl:value-of&lt;/span&gt; select="&lt;span style="color: rgb(0, 102, 0);"&gt;$html/xhtml:body//xhtml:*[local-name() != 'script']/text()&lt;/span&gt;" separator=""/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Saxon's parse() function will fail if you don't direct Saxon (from the command line, or programmatically) to use the TagSoup parser. OTOH, you could resort to Groovy. Put TagSoup on your CLASSPATH, and:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;import &lt;/span&gt;org.ccil.cowan.tagsoup.*&lt;br /&gt;&lt;br /&gt;parser = &lt;span style="font-weight: bold; color: rgb(0, 0, 153);"&gt;new &lt;/span&gt;Parser()&lt;br /&gt;// TagSoup offers loads of interesting options...check 'em out!&lt;br /&gt;f = new File('C:\\Documents and Settings\\tnassar\\Desktop\\Efficient.html')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As we're reminded &lt;a href="http://groovy.codehaus.org/Reading+XML+using+Groovy%27s+XmlSlurper"&gt;here&lt;/a&gt;, we can probably do without the namespace declarations. I quote:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size:85%;"&gt;name or "*:name" matches an element named "name" irrespective of the namespace it's in (i.e. this is the default mode of operation)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:85%;"&gt;":name" matches an element named "name" only id the element is not in a namespace&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:85%;"&gt;"prefix:name" matches an element names "name" only if it is in the namespace identified by the prefix "prefix" (and the prefix to namespace mapping was defined by a previous call to declareNamespace)&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;Anyway, force of habit:&lt;br /&gt;&lt;pre&gt;html = &lt;span style="font-weight: bold; color: rgb(0, 0, 102);"&gt;new &lt;/span&gt;XmlSlurper(parser).parse(f).declareNamespace(xhtml: 'http://www.w3.org/1999/xhtml')&lt;br /&gt;&lt;br /&gt;html.'xhtml:body'.depthFirst().findAll { it.name() != 'script' }*.text().join('\n')&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-4566753909229074119?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/4566753909229074119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=4566753909229074119' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/4566753909229074119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/4566753909229074119'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2009/05/extracting-text-of-html-document.html' title='Extracting the Text of an HTML Document'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-1324556103518000822</id><published>2009-05-02T13:44:00.005-04:00</published><updated>2009-05-02T14:14:49.434-04:00</updated><title type='text'>Extracting the Text from a Word Document w/ Groovy</title><content type='html'>As I'm doing a lot of data munging these days, and often have to talk to Java APIs. Since I'm not writing production code, don't have lots of RAM or lots of good tools at my disposal and therefore would just as soon use a text editor (&lt;a href="http://www.scintilla.org/index.html"&gt;SciTE&lt;/a&gt;'s my current preference...no, I can't get the Win32 port of emacs!), Groovy is definitely my best choice. Notwithstanding my preference for XSLT (over GPath) to handle XML, I can't deny that you can do some slick stuff w/ GPath. At another munger's request, I cooked this up in 5 minutes, and was almost shocked at how easy it was to get the text out of an Office 2007 docx file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(0, 0, 102);"&gt;import &lt;/span&gt;java.util.zip.*&lt;br /&gt;&lt;br /&gt;docx = &lt;span style="color: rgb(0, 0, 102);"&gt;new &lt;/span&gt;File('&lt;span style="color: rgb(0, 153, 0);"&gt;C:\\Documents and Settings\\tnassar\\My Documents\\Efficient.docx&lt;/span&gt;')&lt;br /&gt;zip = &lt;span style="color: rgb(0, 0, 102);"&gt;new &lt;/span&gt;ZipFile(docx)&lt;br /&gt;entry = zip.getEntry('word/document.xml')&lt;br /&gt;stream = zip.getInputStream(entry)&lt;br /&gt;&lt;br /&gt;// The namespace was gleaned from the decompressed XML.&lt;br /&gt;wordMl = &lt;span style="color: rgb(0, 0, 153);"&gt;new &lt;/span&gt;XmlSlurper().parse(stream).declareNamespace(w: '&lt;span style="color: rgb(0, 153, 0);"&gt;http://schemas.openxmlformats.org/wordprocessingml/2006/main&lt;/span&gt;')&lt;br /&gt;&lt;br /&gt;// The outermost XML element node is assigned to the variable wordMl, so&lt;br /&gt;// GPath expressions will start after that. To print out the concatenated&lt;br /&gt;// descendant text nodes of w:body, you use:&lt;br /&gt;&lt;br /&gt;text = wordMl.'&lt;span style="color: rgb(0, 153, 0);"&gt;w:body&lt;/span&gt;'.children().collect { it.text() }.join('')&lt;br /&gt;&lt;br /&gt;println text&lt;br /&gt;&lt;/pre&gt;It would be nice if Groovy offered "raw strings" like Python--r'C:\Documents and Settings\...'--or C#, which lets you prepend a '@' to have backslashes treated literally--esp. when it comes to Windows pathnames, but whatever.&lt;br /&gt;&lt;br /&gt;This will not work well for complex document formats (I can imagine that tables and such would be a disaster), but for me it was just enough.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-1324556103518000822?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/1324556103518000822/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=1324556103518000822' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1324556103518000822'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1324556103518000822'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2009/05/extracting-text-from-word-document-w.html' title='Extracting the Text from a Word Document w/ Groovy'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-2514486902671152274</id><published>2008-10-25T14:01:00.001-04:00</published><updated>2008-10-25T14:01:08.667-04:00</updated><title type='text'>Efficient “Disjoint Sets” Implementation in XSLT</title><content type='html'>&lt;span xmlns=''&gt;&lt;p&gt;I actually had to revisit this problem with slightly different input, and once again I struggled. If I represent an undirected graph like this:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#602020'&gt;?xml version="1.0"?&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;graph&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;nodes&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;node &lt;/span&gt;&lt;span style='color:#d00020'&gt;id=&lt;/span&gt;&lt;span style='color:#000090'&gt;"1"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;node &lt;/span&gt;&lt;span style='color:#d00020'&gt;id=&lt;/span&gt;&lt;span style='color:#000090'&gt;"2"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;node &lt;/span&gt;&lt;span style='color:#d00020'&gt;id=&lt;/span&gt;&lt;span style='color:#000090'&gt;"3"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;node &lt;/span&gt;&lt;span style='color:#d00020'&gt;id=&lt;/span&gt;&lt;span style='color:#000090'&gt;"4"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;node &lt;/span&gt;&lt;span style='color:#d00020'&gt;id=&lt;/span&gt;&lt;span style='color:#000090'&gt;"5"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;/nodes&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;links&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;link &lt;/span&gt;&lt;span style='color:#d00020'&gt;end1=&lt;/span&gt;&lt;span style='color:#000090'&gt;"1"&lt;/span&gt;&lt;span style='color:#d00020'&gt; end2=&lt;/span&gt;&lt;span style='color:#000090'&gt;"2"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;link &lt;/span&gt;&lt;span style='color:#d00020'&gt;end1=&lt;/span&gt;&lt;span style='color:#000090'&gt;"1"&lt;/span&gt;&lt;span style='color:#d00020'&gt; end2=&lt;/span&gt;&lt;span style='color:#000090'&gt;"3"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;link &lt;/span&gt;&lt;span style='color:#d00020'&gt;end1=&lt;/span&gt;&lt;span style='color:#000090'&gt;"1"&lt;/span&gt;&lt;span style='color:#d00020'&gt; end2=&lt;/span&gt;&lt;span style='color:#000090'&gt;"4"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;link &lt;/span&gt;&lt;span style='color:#d00020'&gt;end1=&lt;/span&gt;&lt;span style='color:#000090'&gt;"1"&lt;/span&gt;&lt;span style='color:#d00020'&gt; end2=&lt;/span&gt;&lt;span style='color:#000090'&gt;"5"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;/links&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;/graph&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;				&lt;/span&gt;&lt;/p&gt;&lt;p&gt;…then it's a little harder to go from one node to its connected nodes, because the information I need is elsewhere in the XML document (i.e. not in a child node of the &lt;span style='font-family:Times New Roman; font-size:12pt'&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;node&lt;/span&gt;&lt;/span&gt;&amp;gt; element) . To index any node element, I'd have to navigate up the tree, and back down to the links. What's worse, I'd have to scan all the links for every node. So the solution involves some indirection. First I declare these &lt;em&gt;two&lt;/em&gt; mappings:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:key &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"left"&lt;/span&gt;&lt;span style='color:#d00020'&gt; match=&lt;/span&gt;&lt;span style='color:#000090'&gt;"/graph/links/link"&lt;/span&gt;&lt;span style='color:#d00020'&gt; use=&lt;/span&gt;&lt;span style='color:#000090'&gt;"@end1"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:key &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"right"&lt;/span&gt;&lt;span style='color:#d00020'&gt; match=&lt;/span&gt;&lt;span style='color:#000090'&gt;"/graph/links/link"&lt;/span&gt;&lt;span style='color:#d00020'&gt; use=&lt;/span&gt;&lt;span style='color:#000090'&gt;"@end2"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;&lt;span style='font-size:12pt'&gt;Now I map each node (its ID, actually; remember that the key has to be of an atomic type) to its connected nodes by going through these mappings (such a recursive definition is &lt;em&gt;not&lt;/em&gt; possible in XSLT 1.0, I believe). Note that for links that were indexed by @end1, I'm interested in the value of @end2, and &lt;em&gt;v.v.&lt;/em&gt;&lt;br /&gt;				&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;&lt;br/&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:key &lt;/span&gt;&lt;span style='color:#d00020'&gt;name=&lt;/span&gt;&lt;span style='color:#000090'&gt;"connected"&lt;/span&gt;&lt;span style='color:#d00020'&gt; match=&lt;/span&gt;&lt;span style='color:#000090'&gt;"node"&lt;/span&gt;&lt;span style='color:#d00020'&gt; use=&lt;/span&gt;&lt;span style='color:#000090'&gt;"key('left', @id)/@end2, key('right', @id)/@end1"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;				&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;Finally, I can use this mapping:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;&lt;span style='font-family:Times New Roman; font-size:12pt'&gt;&lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:template &lt;/span&gt;&lt;span style='color:#d00020'&gt;match=&lt;/span&gt;&lt;span style='color:#000090'&gt;"/"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;connectivity&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;            &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:for-each &lt;/span&gt;&lt;span style='color:#d00020'&gt;select=&lt;/span&gt;&lt;span style='color:#000090'&gt;"graph/nodes/node"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;                &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;node &lt;/span&gt;&lt;span style='color:#d00020'&gt;id=&lt;/span&gt;&lt;span style='color:#000090'&gt;"{@id}"&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;                    &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;connected-to&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;                        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;xsl:value-of &lt;/span&gt;&lt;span style='color:#d00020'&gt;select=&lt;/span&gt;&lt;span style='color:#000090'&gt;"key('connected', @id)/@id"&lt;/span&gt;&lt;span style='color:#d00020'&gt;/&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;                    &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;/connected-to&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;                &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;/node&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;            &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:for-each&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;        &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ea8f0f'&gt;/connectivity&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span style='color:blue'&gt;&amp;lt;&lt;/span&gt;&lt;span style='color:#ac306d'&gt;/xsl:template&lt;/span&gt;&lt;span style='color:blue'&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;				&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt; &lt;/p&gt;&lt;p&gt;I believe that these are all the data structures I need. Now I can implement the "strongly connected components" algorithm as I did below, but using set operators on the nodes.  &lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-2514486902671152274?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/2514486902671152274/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=2514486902671152274' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2514486902671152274'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2514486902671152274'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/10/efficient-disjoint-sets-implementation.html' title='Efficient “Disjoint Sets” Implementation in XSLT'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-1753507115502577273</id><published>2008-10-03T17:21:00.008-04:00</published><updated>2008-10-15T21:59:30.703-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xslt'/><title type='text'>Graph Algorithms in XSLT</title><content type='html'>&lt;p&gt;Notwithstanding that XSLT can be considered a functional programming language, you probably wouldn't choose to implement algorithms in it. Nonetheless, I recently had to split a very large XML document into smaller pieces (using xsl:result-document), and I wanted to redundancies between the outputs. So what I essentially wanted to do was separate elements in the input into "strongly-connected components." XSLT 2.0 provides lots of operators on sequences and sets, and allows you to define property functions, but what it does not lend itself to is the creation of new data structures. To be more precise, the data structures you'd need for depth-first search or Tarjan's &lt;a href="http://en.wikipedia.org/wiki/Disjoint-set_data_structure"&gt;disjoint sets&lt;/a&gt; would require you to &lt;strong&gt;copy&lt;/strong&gt; elements into new structures. Easy, if you're working in a language that has pointer or reference equality; not possible in XLST. However, if you assume that the XSLT processor will use hash tables to represent sets, and you put that together with &amp;lt;xsl:key&amp;gt;, you can try a different approach.&lt;/p&gt;&lt;p&gt;Here's the input I want to process:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;  &amp;lt;data&amp;gt;&lt;br /&gt;    &amp;lt;documents&amp;gt;&lt;br /&gt;    &amp;lt;document id="a"&amp;gt;&lt;br /&gt;      &amp;lt;related-entities&amp;gt;1 2 3&amp;lt;/related-entities&amp;gt;&lt;br /&gt;    &amp;lt;/document&amp;gt;&lt;br /&gt;    &amp;lt;document id="b"&amp;gt;&lt;br /&gt;     &amp;lt;related-entities&amp;gt;2 3 4&amp;lt;/related-entities&amp;gt;&lt;br /&gt;    &amp;lt;/document&amp;gt;&lt;br /&gt;    &amp;lt;document id="c"&amp;gt;&lt;br /&gt;      &amp;lt;related-entities&amp;gt;5 6 7&amp;lt;/related-entities&amp;gt;&lt;br /&gt;    &amp;lt;/document&amp;gt;&lt;br /&gt;    &amp;lt;document id="d"&amp;gt;&lt;br /&gt;      &amp;lt;related-entities&amp;gt;7 8 9&amp;lt;/related-entities&amp;gt;&lt;br /&gt;    &amp;lt;/document&amp;gt;&lt;br /&gt;    &amp;lt;document id="e"&amp;gt;&lt;br /&gt;      &amp;lt;related-entities&amp;gt;10&amp;lt;/related-entities&amp;gt;&lt;br /&gt;    &amp;lt;/document&amp;gt;&lt;br /&gt;  &amp;lt;/documents&amp;gt;&lt;br /&gt;  &amp;lt;entities&amp;gt;&lt;br /&gt;    &amp;lt;entity id="1"/&amp;gt;&lt;br /&gt;    &amp;lt;entity id="2"/&amp;gt;&lt;br /&gt;    &amp;lt;entity id="3"/&amp;gt;&lt;br /&gt;    &amp;lt;entity id="4"/&amp;gt;&lt;br /&gt;    &amp;lt;entity id="5"/&amp;gt;&lt;br /&gt;    &amp;lt;entity id="6"/&amp;gt;&lt;br /&gt;    &amp;lt;entity id="7"/&amp;gt;&lt;br /&gt;    &amp;lt;entity id="8"/&amp;gt;&lt;br /&gt;    &amp;lt;entity id="9"/&amp;gt;&lt;br /&gt;    &amp;lt;entity id="10"/&amp;gt;&lt;br /&gt;  &amp;lt;/entities&amp;gt;&lt;br /&gt;&amp;lt;/data&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;I'm going to create a hashmap (actually, a hashmultimap) from entity IDs to documents, and &lt;em&gt;vice versa&lt;/em&gt;. Note that the keys have to be of an atomic value type, and the values have to be elements. You can't map, say, integers to strings. Alright: for any document I can get the list of entity IDs, and use them as keys pointing back to the document element. So if any two documents point to the same entity, those two documents (&lt;em&gt;and&lt;/em&gt;, obviously, the entity as well) must belong in the same strongly-connected component. For a sequence of entity IDs I can create a set of document elements; I then have to determine if &lt;em&gt;this&lt;/em&gt; set intersects with the set I've already accumulated. If yes, I union both sets and recurse; otherwise I spit out the subgraph, pop the stack, and resume with the next, as-yet-unselected document.&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&amp;lt;xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="subgraphs"&amp;gt;&lt;br /&gt;  &amp;lt;xsl:key name="linked-documents" match="/data/documents/document" use="tokenize(related-entities, ' ')"/&amp;gt;&lt;br /&gt;  &amp;lt;xsl:key name="linked-entities" match="/data/entities/entity" use="key('linked-documents', @id)/@id"/&amp;gt;&lt;br /&gt;  &amp;lt;xsl:template match="document"&amp;gt;&lt;br /&gt;    &amp;lt;document id="{@id}"/&amp;gt;&lt;br /&gt;  &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;  &amp;lt;xsl:template name="create-subgraph"&amp;gt;&lt;br /&gt;    &amp;lt;xsl:param name="subgraph" as="element(document)*"/&amp;gt;&lt;br /&gt;    &amp;lt;xsl:param name="selection" as="element(document)*" /&amp;gt;&lt;br /&gt;    &amp;lt;xsl:message select="string-join($subgraph/@id, ',')" /&amp;gt;&lt;br /&gt;    &amp;lt;xsl:message select="string-join($selection/@id, ',')" /&amp;gt;&lt;br /&gt;    &amp;lt;xsl:choose&amp;gt;&lt;br /&gt;      &amp;lt;!-- If there are no more elements to select from, the subgraph is complete (even if empty). --&amp;gt;&lt;br /&gt;      &amp;lt;xsl:when test="not($selection)"&amp;gt;&lt;br /&gt;        &amp;lt;subgraph&amp;gt;&lt;br /&gt;          &amp;lt;xsl:apply-templates select="$subgraph"/&amp;gt;&lt;br /&gt;        &amp;lt;/subgraph&amp;gt;&lt;br /&gt;      &amp;lt;/xsl:when&amp;gt;&lt;br /&gt;      &amp;lt;xsl:otherwise&amp;gt;&lt;br /&gt;        &amp;lt;xsl:variable name="linked-entities" as="element(entity)*" select="for $d in $subgraph return key('linked-entities', $d/@id)"/&amp;gt;&lt;br /&gt;        &amp;lt;xsl:message select="string-join($linked-entities/@id, ',')" /&amp;gt;&lt;br /&gt;        &amp;lt;xsl:variable name="subselection" select="$selection[key('linked-entities', @id) intersect $linked-entities]"/&amp;gt;&lt;br /&gt;        &amp;lt;xsl:message select="string-join($subselection/@id, ',')" /&amp;gt;&lt;br /&gt;        &amp;lt;xsl:choose&amp;gt;&lt;br /&gt;          &amp;lt;xsl:when test="$subselection"&amp;gt;&lt;br /&gt;            &amp;lt;xsl:call-template name="create-subgraph"&amp;gt;&lt;br /&gt;              &amp;lt;xsl:with-param name="selection" select="$selection except $subselection"/&amp;gt;&lt;br /&gt;              &amp;lt;xsl:with-param name="subgraph" select="$subgraph|$subselection"/&amp;gt;&lt;br /&gt;            &amp;lt;/xsl:call-template&amp;gt;&lt;br /&gt;          &amp;lt;/xsl:when&amp;gt;&lt;br /&gt;          &amp;lt;xsl:otherwise&amp;gt;&lt;br /&gt;            &amp;lt;subgraph&amp;gt;&lt;br /&gt;              &amp;lt;xsl:apply-templates select="$subgraph"/&amp;gt;&lt;br /&gt;            &amp;lt;/subgraph&amp;gt;&lt;br /&gt;            &amp;lt;xsl:variable name="new-selection" select="$selection except $subselection"/&amp;gt;&lt;br /&gt;            &amp;lt;xsl:call-template name="create-subgraph"&amp;gt;&lt;br /&gt;              &amp;lt;xsl:with-param name="selection" select="$new-selection[position() gt 1]"/&amp;gt;&lt;br /&gt;              &amp;lt;xsl:with-param name="subgraph" select="$new-selection[1]"/&amp;gt;&lt;br /&gt;            &amp;lt;/xsl:call-template&amp;gt;&lt;br /&gt;          &amp;lt;/xsl:otherwise&amp;gt;&lt;br /&gt;        &amp;lt;/xsl:choose&amp;gt;&lt;br /&gt;      &amp;lt;/xsl:otherwise&amp;gt;&lt;br /&gt;    &amp;lt;/xsl:choose&amp;gt;&lt;br /&gt;  &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xsl:template match="/"&amp;gt;&lt;br /&gt;    &amp;lt;subgraphs&amp;gt;&lt;br /&gt;      &amp;lt;xsl:call-template name="create-subgraph"&amp;gt;&lt;br /&gt;        &amp;lt;!-- Make the first document element an SCC. --&amp;gt;&lt;br /&gt;          &amp;lt;xsl:with-param name="subgraph" select="/data/documents/document[1]"/&amp;gt;&lt;br /&gt;          &amp;lt;xsl:with-param name="selection" select="/data/documents/document[position() gt 1]"/&amp;gt;&lt;br /&gt;        &amp;lt;/xsl:call-template&amp;gt;&lt;br /&gt;      &amp;lt;/subgraphs&amp;gt;&lt;br /&gt;  &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;In the end, sure enough, I get:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;subgraphs xmlns:fn="subgraphs" xmlns:xs="http://www.w3.org/2001/XMLSchema"&amp;gt;&lt;br /&gt;  &amp;lt;subgraph&amp;gt;&lt;br /&gt;    &amp;lt;document id="a"/&amp;gt;&lt;br /&gt;    &amp;lt;document id="b"/&amp;gt;&lt;br /&gt;  &amp;lt;/subgraph&amp;gt;&lt;br /&gt;  &amp;lt;subgraph&amp;gt;&lt;br /&gt;    &amp;lt;document id="c"/&amp;gt;&lt;br /&gt;    &amp;lt;document id="d"/&amp;gt;&lt;br /&gt;  &amp;lt;/subgraph&amp;gt;&lt;br /&gt;  &amp;lt;subgraph&amp;gt;&lt;br /&gt;    &amp;lt;document id="e"/&amp;gt;&lt;br /&gt;  &amp;lt;/subgraph&amp;gt;&lt;br /&gt;&amp;lt;/subgraphs&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yes, defining one mapping in terms of another &lt;em&gt;is&lt;/em&gt; allowed in XSLT 2.0. The problem I had at work was actually more complicated: the entities were in entirely separate XML documents. The trick there, which cost me some sweat, was to define &lt;em&gt;one&lt;/em&gt; mapping, which pointed from a document immediately to other documents. However, I'll put off that explanation until someone asks for it. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-1753507115502577273?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/1753507115502577273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=1753507115502577273' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1753507115502577273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1753507115502577273'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/10/graph-algorithms-in-xslt.html' title='Graph Algorithms in XSLT'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-85467882472022455</id><published>2008-07-18T21:48:00.010-04:00</published><updated>2008-07-22T23:21:55.348-04:00</updated><title type='text'>How to Convert Julian Dates to Gregorian</title><content type='html'>I'm writing Java / Jython / Groovy these days, not F#, at a new job, and haven't had time to maintain this blog, but I did want to post one little snippet that I was surprised not to find elsewhere. I've had to ingest 10s of 1000s of records from a customer's Microsoft Access database, and the dates are represented in Julian format. Not relative to the Julian calendar; I mean &lt;a href="http://en.wikipedia.org/wiki/Julian_date"&gt;the number of days since a particular Day 0&lt;/a&gt;. For Microsoft Access, Day 0 is December 30, 1899. If Microsoft Access interests you, read up on it &lt;a href="http://support.microsoft.com/kb/210276"&gt;here&lt;/a&gt;.&lt;br /&gt;     &lt;br /&gt;Anyway, there are quite a few Web pages out there describing how to do it in Excel or T-SQL, but none describing how to do it in Java. Actually, it's ridiculously easy, which doesn't say much for me. Here's the solution in Jython:&lt;pre&gt;&lt;br /&gt;from java.util import *&lt;br /&gt;&lt;br /&gt;def convertJulian(julian):&lt;br /&gt;    calendar = Calendar.getInstance()&lt;br /&gt;    # Why in God's name are months in Java 0-based?&lt;br /&gt;    # This is December 30, 1899.&lt;br /&gt;    calendar.set(1899, 11, 30)&lt;br /&gt;    calendar.add(Calendar.DATE, julian)&lt;br /&gt;    return calendar.time&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;D'oh! Set Day 0, add the days, and you're good to go. Now in Java:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;private static Date convertJulian(final int julian) {&lt;br /&gt;    Calendar calendar = Calendar.getInstance();&lt;br /&gt;    calendar.set(1899, 11, 30);&lt;br /&gt;    calendar.add(Calendar.DATE, julian);&lt;br /&gt;    return calendar.getTime();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, as XSLT (you'll need an XSLT 2.0-compliant processor for this):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &amp;lt;xsl:function name="local:convert-access-date" as="xs:date"&amp;gt;&lt;br /&gt;  &amp;lt;xsl:param name="access-date" as="xs:integer"/&amp;gt;&lt;br /&gt;  &amp;lt;xsl:variable name="days-since-zero" as="xs:dayTimeDuration"&lt;br /&gt;    select="xs:dayTimeDuration(concat('P', $access-date, 'D'))"/&amp;gt;&lt;br /&gt;  &amp;lt;xsl:sequence select="$zero + $days-since-zero"/&amp;gt;&lt;br /&gt; &amp;lt;/xsl:function&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-85467882472022455?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/85467882472022455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=85467882472022455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/85467882472022455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/85467882472022455'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/07/how-to-convert-julian-dates-to.html' title='How to Convert Julian Dates to Gregorian'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-5217513286228120491</id><published>2008-05-24T14:23:00.011-04:00</published><updated>2008-09-17T09:48:51.463-04:00</updated><title type='text'>John Cage for Children</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_yClxQhTLbco/SDhdLEJJsuI/AAAAAAAAACw/uBqwKhIknBc/s1600-h/meredithMonk.jpg"&gt;&lt;img style="margin: 5pt 5pt 10px 10px; float: left; cursor: pointer;" src="http://4.bp.blogspot.com/_yClxQhTLbco/SDhdLEJJsuI/AAAAAAAAACw/uBqwKhIknBc/s400/meredithMonk.jpg" alt="" id="BLOGGER_PHOTO_ID_5204011814098678498" border="0" /&gt;&lt;/a&gt;Imagine my surprise when I came home from work a few days ago to find this album blasting from my Meadowlark speakers. I did buy them to listen to art music, not Wiggles DVDs, but I didn't expect my toddler to rummage through my CDs and pick this one to play. Kids today grow up so fast!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.diacenter.org/km/cdcoversm.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://www.diacenter.org/km/cdcoversm.jpg" alt="" border="0" /&gt;&lt;/a&gt;Here's the album I &lt;i&gt;want&lt;/i&gt; him to listen to though, because I want to train him to be normal. Komar and Melamid, those jokesters, have assembled musical material that people say they like, result in "a musical work that will be unavoidably and uncontrollably 'liked' by 72 ± 12% of listeners ((standard deviation; Kolmogorov-Smirnov statistic)." I have to admit that &lt;i&gt;I&lt;/i&gt; prefer the composition that only 200 people in the world are supposed to like; it sounds to me like John Cage sounds to those other people. You can listen to recordings of both &lt;a href="http://www.ubu.com/sound/komar.html"&gt;here&lt;/a&gt;. The unpleasant recording had to be fun to make; the pleasant one sounds like a bored Alicia Keys cover band playing at an airport lounge.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-5217513286228120491?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/5217513286228120491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=5217513286228120491' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5217513286228120491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5217513286228120491'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/05/john-cage-for-children.html' title='John Cage for Children'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_yClxQhTLbco/SDhdLEJJsuI/AAAAAAAAACw/uBqwKhIknBc/s72-c/meredithMonk.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-2454496617718551500</id><published>2008-05-04T20:08:00.009-04:00</published><updated>2008-05-05T09:39:39.340-04:00</updated><title type='text'>Counting Change Again?</title><content type='html'>&lt;a href="http://projecteuler.net/index.php?section=problems&amp;amp;id=76"&gt;Problem 76 at Project Euler &lt;/a&gt;is really just the "change counting" problem: how many ways can you count out 100 cents if you have coins in denominations from 1 to 99? However, when I plug those values into my previous implementations of the change counting algorithm, it runs forever...well, maybe not forever, but for hours and hours, while using 100% of my RAM. OK, so that's not going to work. At first I thought that the problem was simply that too many of the answers were in memory (i.e. sequences of coins), so I changed the implementation, simply to count possible answers:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;let rec countChange =&lt;br /&gt;    match coins, amount with&lt;br /&gt;    ¦ _, 0 -&amp;gt; 1L&lt;br /&gt;    ¦ [], _ -&amp;gt; 0L&lt;br /&gt;    ¦ h::t, _ -&amp;gt; [ 0..h..amount ]&lt;br /&gt;                 ¦&amp;gt; List.map (fun amt' -&gt; countChange t (amount - amt'))&lt;br /&gt;                 ¦&amp;gt; Seq.fold1 (+)&lt;br /&gt;&lt;/pre&gt;Let's review this. How many ways are there to make 0 in change, whatever the available coins? There's 1 way. How many ways are there to make some other quantity in change if you have no coins? 0. Finally, how many ways are there to make C in change if you have a a list of coins h::t? Partition the problem thus: use the coin h to pay out a partition of C (obviously you can use coin h up to C/h times), then make up the remaining partition of C with the rest of the list. If, to solve each subproblem, you're dividing C by the denomination of a coin, then the size of the problem space is exponential in the number of denominations: (C/denomination&lt;sub&gt;1&lt;/sub&gt;) * (C/denomination&lt;sub&gt;2&lt;/sub&gt;) * ... * (C/denomination&lt;sub&gt;n&lt;/sub&gt;) = O(C&lt;sup&gt;n&lt;/sup&gt;). You can't exactly tell from this example, but the rate of growth here is clearly superpolynomial:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; countChange [ 4; 5 ] 20;;&lt;br /&gt;val it : int64 = 2L&lt;br /&gt;&amp;gt; countChange [ 3; 4; 5 ] 20;;&lt;br /&gt;val it : int64 = 6L&lt;br /&gt;&amp;gt; countChange [ 3; 4; 5; 6 ] 20;;&lt;br /&gt;val it : int64 = 11L&lt;br /&gt;&amp;gt; countChange [2;  3; 4; 5; 6 ] 20;;&lt;br /&gt;val it : int64 = 47L&lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;/pre&gt;Anyway, it should be clear why there's no point in waiting for the solution to countChange [ 1..99 ] 100. However, I can memoize the change counting algorithm, since it's deterministic: for a given amount, and a given set of coins, the answer is always the same (commerce would be hopeless otherwise). This turns out to be fairly easy, notwithstanding the two arguments to the function. Here's my answer:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;open System.Collections.Generic;;&lt;br /&gt;&lt;br /&gt;let memoize f =&lt;br /&gt;    let cache = Dictionary&lt;_,&gt;()&lt;br /&gt;    fun x y -&gt;&lt;br /&gt;        if cache.ContainsKey((x, y)) then cache.[(x, y)]&lt;br /&gt;        // Remember that f will always consult the memo&lt;br /&gt;        // for subproblems.&lt;br /&gt;        else let result = f x y&lt;br /&gt;             cache.[(x, y)] &lt;- result              &lt;br /&gt;             result  &lt;br /&gt;&lt;br /&gt;let rec countChange =   &lt;br /&gt;  // Turn this into a function that returns a function.   &lt;br /&gt;  // The *returned* function will be evaluated over the arguments.   &lt;br /&gt;  let countChange' coins amount =      &lt;br /&gt;    match coins, amount with     &lt;br /&gt;    ¦ _, 0 -&amp;gt; 1L     &lt;br /&gt;    ¦ [], _ -&amp;gt; 0L     &lt;br /&gt;    ¦ h::t, _ -&amp;gt; [ 0..h..amount ]                   &lt;br /&gt;                 ¦&gt; List.map (fun amt' -&gt; countChange t (amount - amt'))&lt;br /&gt;                 ¦&gt; Seq.fold1 (+)&lt;br /&gt;  memoize countChange'&lt;br /&gt;&lt;/pre&gt;The answer shows up in a few seconds. Since F# lists are immutable, they can easily be compared for reference equality, rather than in O(n&lt;sup&gt;2&lt;/sup&gt;) time. That is, the initial list [ 99..(-1)..1 ] is a 99 prepended to the list [ 98..(-1)..1 ], and so on. We partition the problem into subproblems corresponding to the head and tail of the list, but no additional lists are created beyond the argument to the function (there's only one list beginning with 50, ever, and it's the tail of the list beginning with 51). Microsoft.FSharp.Collections.List&lt;_&gt;.CompareTo() sees literally the same lists again and again when called from memoize(). Hence the lickety-split performance.&lt;br /&gt;&lt;br /&gt;Perhaps this could all be done imperatively, though I don't how OOP is at all applicable to this sort of classic optimization problem (what could justify a Coin class here)? If you haven't read Daniel Friedman's "&lt;a href="http://www.cs.indiana.edu/hyplan/dfried/mex.pdf"&gt;The Role of the Study of Programming Languages in the Education of a Programmer&lt;/a&gt;," here's your chance. As my buddy George Paci puts it, "The title is clearly written by a professor; a marketing guy would have called it something like, 'Holy Crap! It's Amazing What You Can Do with Program Transformations!'"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-2454496617718551500?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/2454496617718551500/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=2454496617718551500' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2454496617718551500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2454496617718551500'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/05/counting-change-again.html' title='Counting Change Again?'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-2322701292861366598</id><published>2008-04-30T21:02:00.002-04:00</published><updated>2008-04-30T21:06:45.554-04:00</updated><title type='text'>Agent Less Than Zero</title><content type='html'>I just started a new job, and I'm writing a lot of Java all of a sudden. Hey, maybe I'll learn Scala and piss off my new colleagues just as I was pissing people off with F# a few weeks ago! Or maybe I'll put off blogging about F# for a few weeks until I get settled in. I have been using F# to solve Project Euler problems, but I feel guilty blogging about those, since I'd have to give away answers. &lt;br /&gt;&lt;br /&gt;In the meantime, I have to agree with &lt;a href="http://sports.aol.com/fanhouse/2008/04/16/barkley-on-the-wizards-dumbest-team-in-the-history-of-civiliza/"&gt;Charles Barkley&lt;/a&gt;, who said, "I think the Washington Wizards have got to be the dumbest team in the history of civilization." I think that Caron Butler is a smart player, mind you, and he *should* have had the ball in his hands at the end of tonight's game.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-2322701292861366598?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/2322701292861366598/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=2322701292861366598' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2322701292861366598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2322701292861366598'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/04/agent-less-than-zero.html' title='Agent Less Than Zero'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-5048292304687173318</id><published>2008-04-02T09:21:00.005-04:00</published><updated>2008-04-05T18:54:35.330-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F# functional programming'/><title type='text'>Memoization, etc.</title><content type='html'>I've been sweating over some problems at &lt;a href="http://projecteuler.net/"&gt;Project Euler&lt;/a&gt;, and I've verified that I'm no mathematician. Some &lt;a href="http://www.haskell.org/haskellwiki/Euler_problems"&gt;enterprising Haskell programmers&lt;/a&gt; got there way before I did, and if I were inclined to cheat, I'd simply translate their solutions into F#. However, in the effort or out of the necessity to remain pure, they don't use for-loops over arrays, or their own memoization, which I'd almost certainly do for any production code that had to calculate the Fibonacci series...well, actually, I don't do that kind of work. Anyway...&lt;br /&gt;&lt;br /&gt;I did find the Haskell samples instructive, though I don't want to get sidetracked from my project of learning F#. To this end I've once again resorted to Don Syme's thoughts on memoization: &lt;a href="http://caml.inria.fr/pub/ml-archives/caml-list/2006/10/ec2a2d964cedd607584e055b240838ba.en.html"&gt;here&lt;/a&gt; on the CAML list, and &lt;a href="http://blogs.msdn.com/dsyme/archive/2007/05/31/a-sample-of-the-memoization-pattern-in-f.aspx"&gt;here&lt;/a&gt; more recently, where he avails himself of some popular .NET collections. So a memoized Fibonacci function in Dr. Syme's older formulation looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; #light;;&lt;br /&gt;&lt;br /&gt;&gt; let cache f =&lt;br /&gt;&lt;br /&gt;-   let table = ref [] in&lt;br /&gt;&lt;br /&gt;-   fun n -&gt;&lt;br /&gt;&lt;br /&gt;-      try&lt;br /&gt;&lt;br /&gt;-        List.assoc n !table&lt;br /&gt;&lt;br /&gt;-      with Not_found -&gt;&lt;br /&gt;&lt;br /&gt;-        let f_n = f n in&lt;br /&gt;&lt;br /&gt;-           table := (n, f_n) :: !table;&lt;br /&gt;&lt;br /&gt;-           f_n&lt;br /&gt;&lt;br /&gt;-&lt;br /&gt;&lt;br /&gt;-&lt;br /&gt;&lt;br /&gt;- let rec fib_mem =&lt;br /&gt;&lt;br /&gt;-    cache (function&lt;br /&gt;&lt;br /&gt;-              // You'd better return a bigint, not an int!&lt;br /&gt;&lt;br /&gt;-              &amp;brvbar; 0 -&gt; 0I&lt;br /&gt;&lt;br /&gt;-              &amp;brvbar; 1 -&gt; 1I&lt;br /&gt;&lt;br /&gt;-              &amp;brvbar; n -&gt; fib_mem (n - 1) + fib_mem (n - 2))&lt;br /&gt;&lt;br /&gt;-&lt;br /&gt;&lt;br /&gt;-&lt;br /&gt;&lt;br /&gt;- ;;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;val cache : ('a -&gt; 'b) -&gt; ('a -&gt; 'b)&lt;br /&gt;&lt;br /&gt;val fib_mem : (int -&gt; bigint)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&gt; fib_mem(1000);;&lt;br /&gt;&lt;br /&gt;val it : bigint&lt;br /&gt;&lt;br /&gt;= 434665576869374564356885276750406258025646605173717804024817290895365554179490&lt;br /&gt;&lt;br /&gt;51890403879840079255169295922593080322634775209689623239873322471161642996440906&lt;br /&gt;&lt;br /&gt;533187938298969649928516003704476137795166849228875I&lt;br /&gt;&lt;br /&gt;val ns : seq&lt;int&gt;&lt;br /&gt;&lt;/pre&gt;Several problems at Project Euler ask you to do something with the first 10 or last 10 digits of some big integer; for anyone who was wondering how to do that with a number such as the one above, here's one way:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; fib_mem(1000) |&gt; Seq.unfold (fun n -&gt; if n &gt; 0I&lt;br /&gt;-                                         then&lt;br /&gt;-                                          let quotient,remainder = BigInt.divmod n 10I&lt;br /&gt;-                                          Some (remainder, quotient)&lt;br /&gt;-                                        else&lt;br /&gt;-                                          None);;&lt;br /&gt;val it : seq&lt;bigint&gt; = seq [5I; 7I; 8I; 8I; ...]&lt;br /&gt;&gt;&lt;br /&gt;&lt;/pre&gt;You'll notice that the least significant digits appear first, which is of course by design, since dividing by 10 is probably cheap. The last thing I'd want to do is reverse this sequence, i.e.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-               |&gt; List.of_seq&lt;br /&gt;-               |&gt; List.rev;;&lt;br /&gt;&lt;/pre&gt;I could clean this up further by applying BigInt.to_Int32 to the generated elements, etc. To sum the first 10 digits (note that this is *not* the Project Euler problem; I'm not about to give away any answers) I could go further, like so:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-               |&gt; Seq.take 10&lt;br /&gt;-               |&gt; Seq.fold1 (+);;&lt;br /&gt;&lt;/pre&gt;You get the idea.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-5048292304687173318?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/5048292304687173318/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=5048292304687173318' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5048292304687173318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5048292304687173318'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/04/memoization-etc.html' title='Memoization, etc.'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-4472143666887250210</id><published>2008-03-11T16:53:00.007-04:00</published><updated>2008-03-12T09:23:06.939-04:00</updated><title type='text'>Munging with Active Patterns</title><content type='html'>Have you checked out &lt;a href="http://blogs.msdn.com/dsyme/attachment/2044281.ashx"&gt;active patterns &lt;/a&gt;yet? C'mon! Here's what the code below might have looked like if I'd used them:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; open System;;&lt;br /&gt;&lt;br /&gt;&amp;gt; open System.Text.RegularExpressions;;&lt;br /&gt;&lt;br /&gt;&amp;gt; let re = new Regex("^(MON&amp;brvbar;TUE&amp;brvbar;WED&amp;brvbar;THU&amp;brvbar;FRI&amp;brvbar;SAT&amp;brvbar;SUN)", RegexOptions.Compiled);;&lt;br /&gt;&lt;br /&gt;val re : Regex&lt;br /&gt;&amp;gt; let (¦DataRow(¦_¦) (s : string) =&lt;br /&gt;-     if re.IsMatch s&lt;br /&gt;-         &lt;span style="color:#009900;"&gt;// Here I'd want to break the line down.&lt;/span&gt;&lt;br /&gt;-     then Some(1, 2, "Hello!")&lt;br /&gt;-     else None&lt;br /&gt;&lt;br /&gt;val ( &amp;brvbar;DataRow&amp;brvbar;_&amp;brvbar; ) : string -&gt; (int * int * string) option&lt;br /&gt;&lt;/pre&gt;As I said earlier, Seq.choose will through out None, and I could then handle different kinds of data rows from the input:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; match "TUE" with&lt;br /&gt;- ¦ DataRow(x, y, s) -&gt; Console.WriteLine(s)&lt;br /&gt;- &lt;span style="color:#009900;"&gt;// Additional cases here...&lt;/span&gt;&lt;br /&gt;- ¦ _ -&gt; Console.WriteLine("Got bupkes!");;&lt;br /&gt;Hello!&lt;br /&gt;val it : unit = ()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-4472143666887250210?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/4472143666887250210/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=4472143666887250210' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/4472143666887250210'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/4472143666887250210'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/03/munging-with-active-patterns.html' title='Munging with Active Patterns'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-3051207096476926257</id><published>2008-03-10T09:54:00.015-04:00</published><updated>2008-03-11T14:56:24.688-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Munging Data in F#, Again</title><content type='html'>I couldn't help myself. I was recently asked to take a coding test to prove my fitness for a potential new job, and I figured I could do it more quickly in F#, interactively. I wouldn't have to fire up Visual Studio, I wouldn't have to create an executable project with &lt;a href="http://nunit.org/index.php"&gt;NUnit&lt;/a&gt; tests and debuggability and all that ceremony. But really, my dabblings in functional programming have taught me that decomposition into classes is &lt;em&gt;not &lt;/em&gt;always the natural way to decompose a problem. Why do I need any classes at all to do this exercise?&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; #light;;&lt;br /&gt;&amp;gt; open System.Text.RegularExpressions;;&lt;br /&gt;&amp;gt; let r = new Regex("^(MON&amp;brvbar;TUE&amp;brvbar;WED&amp;brvbar;THU&amp;brvbar;FRI&amp;brvbar;SAT&amp;brvbar;SUN)", RegexOptions.Compiled);;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The line must start with one of these abbreviations. Hey, Blogger keeps taking out the vertical bars between the abbreviations; if they're missing, don't get distracted. Then I want to turn the file into a sequence of strings, i.e. IEnumerable&amp;lt;string&amp;gt;. A "use" as opposed to a "let" binding ensures that the Close() or Dispose() is finally called. See &lt;a href="http://blogs.msdn.com/dsyme/archive/2007/09/22/some-details-on-f-computation-expressions-aka-monadic-or-workflow-syntax.aspx"&gt;Don Syme's blog&lt;/a&gt; for examples (this example, actually, which I simply copied). It's more or less like a C# function that returns IEnumerable&lt;_&gt; by means of "yield return."&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; let reader =&lt;br /&gt;- { use reader = new StreamReader(File.OpenRead(@"C:\...\input.txt"))&lt;br /&gt;-   while not reader.EndOfStream do&lt;br /&gt;-   yield reader.ReadLine() };;&lt;br /&gt;// Filter out the lines that aren’t data rows.&lt;br /&gt;&gt; let filtered = reader ¦&gt; Seq.filter (fun line -&gt; r.IsMatch line);;&lt;br /&gt;&gt; open System;;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Now decompose the line into the parts we’re interested in, based on the (assumed) fixed format. If you want to get your mind blown by "active patterns," dig my man &lt;a href="http://blogs.msdn.com/dsyme/archive/2007/04/06/detailed-release-notes-for-1-9-1-8.aspx"&gt;Don Syme&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let (¦Record¦) (line : string) =&lt;br /&gt;- let transientSold = Int32.Parse(line.Substring(67, 7))&lt;br /&gt;- let definite = Int32.Parse(line.Substring(25, 3))&lt;br /&gt;- let tentative = Int32.Parse(line.Substring(19, 3))&lt;br /&gt;- let date = line.Substring(5, 11)&lt;br /&gt;- // Return the tuple of interesting values.&lt;br /&gt;- date, transientSold, definite, tentative;;&lt;br /&gt;// This should really be embedded in a function!&lt;br /&gt;&gt; open System.Xml;;&lt;br /&gt;&gt; let xml = XmlWriter.Create("output.xml");;&lt;br /&gt;&gt; xml.WriteStartDocument();;&lt;br /&gt;&gt; xml.WriteStartElement("Sample");;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Now pass the filtered lines through a function that decomposes the line into fields we care about, and output an XML element per. Seq.iter applies a function with side-effects but no return value to every element in a sequence.&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; filtered &amp;brvbar;&amp;gt; Seq.iter (fun line -&gt;&lt;br /&gt;- match line with&lt;br /&gt;- &amp;brvbar; Record(date, ts, d, t) -&gt; xml.WriteStartElement("Thing")&lt;br /&gt;-   xml.WriteElementString("Date", date)&lt;br /&gt;-   xml.WriteElementString("TransientSold", XmlConvert.ToString(ts))&lt;br /&gt;-   xml.WriteElementString("CommitmentsDefinite", XmlConvert.ToString(d))&lt;br /&gt;-   xml.WriteElementString("CommitmentsTentative", XmlConvert.ToString(t))&lt;br /&gt;-   xml.WriteEndElement()&lt;br /&gt;- );;&lt;br /&gt;val it : unit = ()&lt;br /&gt;// Likewise, this should have been put into the function I inlined above.&lt;br /&gt;&gt; xml.WriteEndElement();;&lt;br /&gt;val it : unit = ()&lt;br /&gt;&gt; xml.Close();;&lt;br /&gt;val it : unit = ()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;I could have made some different choices here. First of all, opening an XmlWriter in one function, or directly from the command line, then closing it the same way, is rather ugly. If a function is the unit of encapsulation in functional programming, then one function should really own the writer via a use-binding. So I could do something like this:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;filtered &amp;brvbar;&amp;gt; (fun lines -&gt; &lt;br /&gt;                                use xmlWriter = XmlWriter.Create("output.xml")&lt;br /&gt;                                xmlWriter .WriteStartDocument()&lt;br /&gt;                                xmlWriter .WriteStartElement("Sample")&lt;br /&gt;                                // Yes, it's a loop!&lt;br /&gt;                                for line in lines do&lt;br /&gt;                                    // Create each element...&lt;br /&gt;                            )&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The following may be too cute:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;filtered  &amp;brvbar;&amp;gt; Seq.fold (fun () -&gt; let xmlWriter = ...&lt;br /&gt;                                                 xmlWriter.WriteStartDocument()&lt;br /&gt;                                                 // additional initialization&lt;br /&gt;                                                 xmlWriter&lt;br /&gt;                                      )&lt;br /&gt;                                      (fun writer line -&gt; &lt;br /&gt;                                                 // Add an element to the XmlWriter.&lt;br /&gt;                                                 writer&lt;br /&gt;                                      )&lt;br /&gt;                  &amp;brvbar;&amp;gt; (fun writer -&gt; writer.Close())&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;A significant change would have been to use an active pattern to both look for lines of interest and decompose them into interesting tuples. In this case I either keep the line or throw it out, but there might be several data row formats that I care about. In this event I could use active patterns to discriminate between them, and embed any regular expressions in the pattern function. Furthermore, I could use Seq.choose instead of Seq.filter, because the former passes over None. If I get a chance tonight, I'll write that code out. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-3051207096476926257?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/3051207096476926257/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=3051207096476926257' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3051207096476926257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3051207096476926257'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/03/munging-data-in-f-again.html' title='Munging Data in F#, Again'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-1138715927604237795</id><published>2008-02-02T17:03:00.000-05:00</published><updated>2008-02-10T14:37:56.998-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F# Linq'/><title type='text'>Munging some XML with F# &amp; Linq</title><content type='html'>I recently wanted to change the format of a test script, and canonicalize a flattened listing of all the regression test models. So I set myself the goal of doing it in F#, which would force me to use some of the language features I'd only read about, and possibly prod me to use some LINQ as well. I could have done this transformation manually in about an hour, I admit. And I learned that if I really want to transform a lot of XML to XML, I'd rather do it via XML, i.e. XSLT. However, Microsoft doesn't offer XSLT 2.0 compliancy, so I have less to gain from learning to do it that way (meaning, I'm stuck with the .NET XsltCompiledTransform implementation at work). What I would have needed is the tokenize function (to split up pathnames on the '\\' character), XSLT functions that could return Boolean values, and the ability to select groups. Anyway, that's what I did below.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#light&lt;br /&gt;&lt;br /&gt;open System&lt;br /&gt;open System.Xml&lt;br /&gt;open System.Linq&lt;br /&gt;open System.Xml.Linq&lt;br /&gt;open Microsoft.FSharp.Linq&lt;br /&gt;open Microsoft.FSharp.Xml.Linq&lt;br /&gt;open Microsoft.FSharp.Linq.SequenceOps&lt;br /&gt;open Microsoft.FSharp.Xml.Linq.SequenceOps&lt;br /&gt;&lt;br /&gt;let doc = XDocument.Load @"knownSolutions.xml"&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;// Simply tagged aliases for these various tuples. I want to create &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;// a tree of these, then finally convert them back to XML.      &lt;/span&gt;&lt;br /&gt;type Node =&lt;br /&gt;| File of string * XElement&lt;br /&gt;| Directory of string * Node list&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;// the temporary data structure: I'll create a list of these from&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;// the initial XML, then recursively group them into directories. &lt;/span&gt;&lt;br /&gt;type Path = string list * XElement&lt;br /&gt;&lt;br /&gt;let make_path (e : XElement) =&lt;br /&gt;   e.Element(xname "File") |&gt; element_to_string |&gt; String.split [ '\\' ], e&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;// I want to split up the path names, while carrying the original&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;// element around until I'm ready to use it. &lt;/span&gt;&lt;br /&gt;let paths = doc.Root.Elements(xname "Model")&lt;br /&gt;               |&gt; Seq.map make_path&lt;br /&gt;               |&gt; Seq.to_list // There's no Seq.partition, so...&lt;br /&gt;              &lt;br /&gt;let is_file (p, _) =&lt;br /&gt;   (List.length p) = 1&lt;br /&gt;  &lt;br /&gt;let strip (p, e) =&lt;br /&gt;   &lt;span style="color: rgb(0, 153, 0);"&gt;// Strip the next part of the path, and return this tuple.&lt;/span&gt;&lt;br /&gt;   List.tl p, e&lt;br /&gt;&lt;br /&gt;let separate (l : Path list) =&lt;br /&gt;   List.partition is_file l  &lt;br /&gt;  &lt;br /&gt;let rec collect (l : Path list) =&lt;br /&gt;   match l with&lt;br /&gt;   | [] -&gt; []&lt;br /&gt;   | _ -&gt; let files, directories = separate l&lt;br /&gt;          let x = directories&lt;br /&gt;                  |&gt; Seq.groupBy (fun (l, _) -&gt; List.hd l)&lt;br /&gt;                  |&gt; Seq.map make_directories&lt;br /&gt;                  |&gt; Seq.to_list&lt;br /&gt;          (make_files files) @ x&lt;br /&gt;and make_directories (l, r) =&lt;br /&gt;   Directory ( l, r |&gt; Seq.map strip |&gt; Seq.to_list |&gt; collect )&lt;br /&gt;and make_files paths =&lt;br /&gt;   paths |&gt; List.map (fun (l, e) -&gt; File (List.hd l, e))&lt;br /&gt;  &lt;br /&gt;let collected = paths |&gt; collect&lt;br /&gt;&lt;br /&gt;let make_xml l =&lt;br /&gt;   &lt;span style="color: rgb(0, 153, 0);"&gt;// Yes, you need the "let rec" here, too. That took me a while to &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;    // figure out. Otherwise these nested functions can't call each other.&lt;/span&gt;&lt;br /&gt;   let rec make_file name (xml : XElement) =&lt;br /&gt;       &lt;span style="color: rgb(0, 153, 0);"&gt;// If you want to mix XElement and XAttribute objects, you'll have &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;        // to upcast them to XObject. F#'s type inference won't try to find the&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;        // lowest common denominator between two classes in a heterogeneous &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;        // collection, as far as I can tell. &lt;/span&gt;&lt;br /&gt;       XElement(xname "File", xargs [XAttribute(xname "Name", name); XAttribute(xname "ObjectiveValue", xml.Element(xname "Solution") |&gt; element_to_string)])&lt;br /&gt;   and make_directory name nodes =&lt;br /&gt;       let element = XElement(xname "Directory", xargs (nodes |&gt; List.map make_xml'))&lt;br /&gt;       element.SetAttributeValue(xname "Name", name)&lt;br /&gt;       element&lt;br /&gt;   and make_xml' e =&lt;br /&gt;       match e with&lt;br /&gt;       | File (name, element) -&gt; make_file name element&lt;br /&gt;       | Directory (name, element) -&gt; make_directory name element&lt;br /&gt;   match l with&lt;br /&gt;   | [] -&gt; []&lt;br /&gt;   | _ -&gt; List.map make_xml' l&lt;br /&gt;  &lt;br /&gt;let xml = XElement(xname "KnownSolutions", make_xml collected)&lt;br /&gt;let s = xml.ToString()&lt;br /&gt;Console.WriteLine(s)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-1138715927604237795?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/1138715927604237795/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=1138715927604237795' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1138715927604237795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1138715927604237795'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/02/munging-some-xml-with-f-linq.html' title='Munging some XML with F# &amp; Linq'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-7013772676199970830</id><published>2008-02-02T12:26:00.000-05:00</published><updated>2008-02-08T23:12:05.541-05:00</updated><title type='text'>What's an F# Tuple?</title><content type='html'>&lt;p&gt;&lt;br /&gt;It's hardly a difficult question, but it's taken me a while to arrive at an answer. &lt;a href="http://www.aisto.com/roeder/dotnet/"&gt;Reflector &lt;/a&gt;has been invaluable to me, and I can't recommend it strongly enough to anyone who wants or needs to understand what the F# compiler does. One often hears that functional programming more readily lets one find the proper level of abstraction, but sometimes one has to look at the IL, perhaps as a bridge to C# (the "canonical" &lt;br /&gt;.NET language), to improve.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Let's say I define these tuple types:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;type StringInt = string * int&lt;br /&gt;type StringDouble = string * double&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;If I disassemble the resulting DLL, I find...nothing at all! There's no trace in the IL of these tuple types, or anything else:&lt;pre&gt;public static void main()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;Let me add some functions that operate on this code:&lt;br /&gt;&lt;pre&gt;let get_int (t : StringInt) =&lt;br /&gt;    let _, n = t in n&lt;br /&gt;&lt;br /&gt;let get_float (t : StringFloat) =&lt;br /&gt;    let _, f = t in f&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;Something interesting now shows up in Reflector's tree view of the &lt;br /&gt;assembly: &lt;a href="http://2.bp.blogspot.com/_yClxQhTLbco/R6S7s5sYZYI/AAAAAAAAACQ/09mSgGvqDGo/s1600-h/reflectorTree.png"&gt;&lt;img id="BLOGGER_PHOTO_ID_5162457452949038466" style="margin: 10px 10px 10px 0px; float: none; clear: both; display: block; position: static; text-align: center;" alt="" src="http://2.bp.blogspot.com/_yClxQhTLbco/R6S7s5sYZYI/AAAAAAAAACQ/09mSgGvqDGo/s320/reflectorTree.png" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;main() then looks like this (screenshots made using &lt;a href="http://blogs.geekdojo.net/brian/articles/Cropper.aspx"&gt;Cropper&lt;/a&gt;):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_yClxQhTLbco/R6iBF5sYZaI/AAAAAAAAACg/ntqlAdXsatg/s1600-h/disassembly.png"&gt;&lt;img style="margin: 10px auto; display: block; float: none; cursor: pointer; clear: both; vertical-align: middle; position: static; text-align: center;" src="http://2.bp.blogspot.com/_yClxQhTLbco/R6iBF5sYZaI/AAAAAAAAACg/ntqlAdXsatg/s400/disassembly.png" alt="" id="Img1" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;I can't claim that all of this is comprehensible to me, but one thing is obvious: the compiled IL has no trace of the named tuple types. They're like structs insofar as they're equal when they have equal structure. Tuple types with the same structure are really the same type. They are not implemented as structs (presumably to avoid having to copy them to the stack on every recursion), but they're implemented as classes. &lt;i&gt;Sealed&lt;/i&gt; classes, mind you:&lt;/p&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_yClxQhTLbco/R6iN-JsYZbI/AAAAAAAAACo/cv4myjNCEkY/s1600-h/tupleCs.PNG"&gt;&lt;img style="margin: 10px auto; display: block; float: none; cursor: pointer; clear: both; vertical-align: middle; position: static; text-align: center;" src="http://3.bp.blogspot.com/_yClxQhTLbco/R6iN-JsYZbI/AAAAAAAAACo/cv4myjNCEkY/s400/tupleCs.PNG" alt="" id="Img2" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;The disassembled get_int() function looks like this; I don't know why a new instance is being allocated from the heap:&lt;a href="http://1.bp.blogspot.com/_yClxQhTLbco/R6S3apsYZWI/AAAAAAAAACA/u88dWe_jdSk/s1600-h/get_int.PNG"&gt;&lt;img id="Img3" style="margin: 10px 0px; clear: both; display: block; float: none; vertical-align: middle; position: static; text-align: center;" alt="" src="http://1.bp.blogspot.com/_yClxQhTLbco/R6S3apsYZWI/AAAAAAAAACA/u88dWe_jdSk/s320/get_int.PNG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Interestingly, if I change my code thus, I can no longer call get_int() and  get_float() with the resulting values, because the "discrimated union" Tuples &lt;i&gt;is&lt;/i&gt; implemented as a class, rather like a C-style union with type tag and some smarter operations (the disassembled IL is much too  verbose to reproduce here, but I recommend trying it):&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;type Tuples =&lt;br /&gt;| One of StringInt&lt;br /&gt;| Two of StringFloat&lt;br /&gt;&lt;br /&gt;let si = One ("Carlo", 6)&lt;br /&gt;let sf = Two ("Leo", 1.5)&lt;br /&gt;&lt;br /&gt;Console.WriteLine (si)&lt;br /&gt;Console.WriteLine (sf)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Note that I don't have to specify the tuple type; the compiler infers if from the tag "One" or "Two". What I've learned from Don Syme's &lt;i&gt;Expert F#&lt;/i&gt; is that the tag most recently put in scope is the one that the compiler tries to use. Then the disassembled code looks like this, meaning that you can no longer get at the tuple except through the tag:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://2.bp.blogspot.com/_yClxQhTLbco/R6S6e5sYZXI/AAAAAAAAACI/oxWMbDXKOpc/s1600-h/myUnion.PNG"&gt;&lt;img id="Img4" style="margin: 10px 0px; float: none; clear: both; display: block; vertical-align: middle; position: static; text-align: center;" alt="" src="http://2.bp.blogspot.com/_yClxQhTLbco/R6S6e5sYZXI/AAAAAAAAACI/oxWMbDXKOpc/s320/myUnion.PNG" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-7013772676199970830?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/7013772676199970830/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=7013772676199970830' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/7013772676199970830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/7013772676199970830'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/02/whats-f-tuple.html' title='What&apos;s an F# Tuple?'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_yClxQhTLbco/R6S7s5sYZYI/AAAAAAAAACQ/09mSgGvqDGo/s72-c/reflectorTree.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-8088728877161677162</id><published>2008-01-29T09:48:00.000-05:00</published><updated>2008-01-29T10:06:34.958-05:00</updated><title type='text'>Cute Techie Kids!</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_yClxQhTLbco/R59A7psYZTI/AAAAAAAAABo/Q1xpqavYdaw/s1600-h/Leo,+sleeping+with+bottle.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_yClxQhTLbco/R59A7psYZTI/AAAAAAAAABo/Q1xpqavYdaw/s320/Leo,+sleeping+with+bottle.jpg" alt="" id="BLOGGER_PHOTO_ID_5160915091538339122" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I don't really have any excuse for posting this photo of Leo (19 mos. old) sleeping with a bottle dangling from his lips. You wanna make something of it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-8088728877161677162?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/8088728877161677162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=8088728877161677162' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8088728877161677162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8088728877161677162'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/01/cute-techie-kids.html' title='Cute Techie Kids!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_yClxQhTLbco/R59A7psYZTI/AAAAAAAAABo/Q1xpqavYdaw/s72-c/Leo,+sleeping+with+bottle.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-6191782115114112085</id><published>2008-01-29T08:40:00.000-05:00</published><updated>2008-01-29T08:43:16.859-05:00</updated><title type='text'>I Couldn't Be Happier!</title><content type='html'>From &lt;a href="http://www.slate.com/id/2182930/"&gt;slate.com&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;A study suggests &lt;strong&gt;extreme happiness may be bad for you.&lt;/strong&gt; &lt;a href="http://www.eurekalert.org/pub_releases/2008-01/uoia-dwb012408.php" target="_blank"&gt;Findings&lt;/a&gt;: 1) "The highest levels of income, education and political participation were reported not by the most satisfied individuals, but by moderately satisfied individuals." 2) Extremely happy people "earned significantly less money" and earned lower school grades than moderately happy people. 3) They "may not live as long," either. &lt;a href="http://www.reuters.com/article/lifestyleMolt/idUSTON87744820080128" target="_blank"&gt;Theories&lt;/a&gt;: 1) Happiness makes you complacent and kills your drive. 2) It makes you slow to adapt. 3) It makes you too optimistic and insufficiently vigilant about your health. 4) It may overstimulate your cardiovascular system. &lt;strong&gt;Researchers' conclusions:&lt;/strong&gt; 1) "Happiness may need to be moderated for success." 2) "Extremely high levels of happiness might not be a desirable goal." &lt;strong&gt;Human Nature's conclusions:&lt;/strong&gt; 1) Success may need to be moderated for happiness. 2) Extremely high levels of success might not be a desirable goal.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-6191782115114112085?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/6191782115114112085/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=6191782115114112085' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/6191782115114112085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/6191782115114112085'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/01/i-couldnt-be-happier.html' title='I Couldn&apos;t Be Happier!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-2246991049379880399</id><published>2008-01-20T11:03:00.001-05:00</published><updated>2008-01-25T11:45:24.317-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='F# SICP Huffman'/><title type='text'>Huffman Coding in F#</title><content type='html'>&lt;pre&gt;#light &lt;span style="color: rgb(0, 102, 0);"&gt;// I.e. lightweight syntax&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;namespace&lt;/span&gt; Nassar&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;// An F# module is a "static class." All the declarations&lt;br /&gt;// that follow are static members of this class.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;module&lt;/span&gt; Huffman&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;type&lt;/span&gt; Incidence = HashMultiMap&amp;lt;char, int&amp;gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;type&lt;/span&gt; CharSet = Set&amp;lt;char&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; addToCount (d : Incidence) (c : char) =&lt;br /&gt;   &lt;span style="color: rgb(0, 102, 0);"&gt;// For some reason, the "do" is necessary here.&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 153);"&gt;do match&lt;/span&gt; d.TryFind c &lt;span style="color: rgb(0, 0, 153);"&gt;with&lt;/span&gt;&lt;br /&gt;       Some(n) &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; d.Replace(c, n + 1)&lt;br /&gt;       _ &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; d.Add(c, 1)&lt;br /&gt;   d &lt;span style="color: rgb(0, 102, 0);"&gt;// Return the dictionary.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; count (s : string) =&lt;br /&gt;   &lt;span style="color: rgb(0, 102, 0);"&gt;// I only just noticed that System.String implements&lt;br /&gt;   // IEnumerable. The F# compiler correctly inferred from&lt;br /&gt;   // the type of Incidence that s has to be a sequence of&lt;br /&gt;   // char.&lt;/span&gt;&lt;br /&gt;   s |&amp;gt; Seq.fold addToCount (Incidence.Create())&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;type&lt;/span&gt; HuffmanNode =&lt;br /&gt;  Leaf &lt;span style="color: rgb(0, 0, 153);"&gt;of&lt;/span&gt; HuffmanLeaf&lt;br /&gt;  Tree &lt;span style="color: rgb(0, 0, 153);"&gt;of&lt;/span&gt; HuffmanTree&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;and&lt;/span&gt; HuffmanLeaf = {Count: int;&lt;br /&gt;                  Char: char;}&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;and&lt;/span&gt; HuffmanTree = {Count: int;&lt;br /&gt;                  Chars: CharSet;&lt;br /&gt;                  Left: HuffmanNode;&lt;br /&gt;                  Right: HuffmanNode;}&lt;br /&gt;              &lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; get_count node =&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 153);"&gt;match&lt;/span&gt; node &lt;span style="color: rgb(0, 0, 153);"&gt;with&lt;/span&gt;&lt;br /&gt;    Leaf l &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; l.Count&lt;br /&gt;    Tree t &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; t.Count&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; has_char node c =&lt;br /&gt;   match node with&lt;br /&gt;    Leaf l &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; l.Char = c&lt;br /&gt;    Tree t &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; t.Chars.Contains c&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; get_set node =&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 153);"&gt;match&lt;/span&gt; node &lt;span style="color: rgb(0, 0, 153);"&gt;with&lt;/span&gt;&lt;br /&gt;    Leaf leaf -&amp;gt; CharSet.Singleton leaf.Char&lt;br /&gt;    Tree tree -&amp;gt; tree.Chars&lt;br /&gt;              &lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; merge_nodes left right =&lt;br /&gt;   Tree { Count = get_count left + get_count right;&lt;br /&gt;          Chars = (get_set left) + (get_set right);&lt;br /&gt;          Left = left;&lt;br /&gt;          Right = right }&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; node_priority l r =&lt;br /&gt;   get_count l - get_count r&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; rec reduce_list l =&lt;br /&gt;   &lt;span style="color: rgb(0, 102, 0);"&gt;// What I should really do is put all the new leaf nodes&lt;br /&gt;   // into a priority queue, and pull off two at a time as&lt;br /&gt;   // long as the length of the queue is &amp;gt; 1. Then&lt;br /&gt;   // make_tree wouldn't have to convert the sequence&lt;br /&gt;   // it creates from the dictionary into a list.&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 153);"&gt;match&lt;/span&gt; l &lt;span style="color: rgb(0, 0, 153);"&gt;with&lt;/span&gt;&lt;br /&gt;    l::r::tail &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; (merge_nodes l r)::tail&lt;br /&gt;                   |&amp;gt; List.sort node_priority&lt;br /&gt;                   &lt;span style="color: rgb(0, 102, 0);"&gt;// ...and recurse.&lt;/span&gt;&lt;br /&gt;                   |&amp;gt; reduce_list&lt;br /&gt;    [tree] &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; tree&lt;br /&gt;    [] &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 0, 153);"&gt;failwith&lt;/span&gt; "Can't operate on empty list!"&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; rec encode h s =&lt;br /&gt;   &lt;span style="color: rgb(0, 102, 0);"&gt;// Encode a character by branching to the leaf node&lt;br /&gt;   // with that character.&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; rec encode' node c =&lt;br /&gt;       &lt;span style="color: rgb(0, 0, 153);"&gt;match&lt;/span&gt; node &lt;span style="color: rgb(0, 0, 153);"&gt;with&lt;/span&gt;&lt;br /&gt;        Leaf l &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; []&lt;br /&gt;       &lt;span style="color: rgb(0, 102, 0);"&gt;// TODO Add an accumulator to make this tail-recursive,&lt;br /&gt;       // rather than prepending the new element.&lt;/span&gt;&lt;br /&gt;        Tree t &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 0, 153);"&gt;if&lt;/span&gt; has_char t.Left c &lt;span style="color: rgb(0, 0, 153);"&gt;then&lt;/span&gt; 0::(encode' t.Left c) &lt;span style="color: rgb(0, 0, 153);"&gt;else&lt;/span&gt; 1::(encode' t.Right c)&lt;br /&gt;   s &amp;gt; Seq.map (fun c -&amp;gt; encode' h c) &amp;gt; Seq.concat&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;// Decode the int list l using the tree h.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; &lt;span style="color: rgb(0, 0, 153);"&gt;rec&lt;/span&gt; decode h l =&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; &lt;span style="color: rgb(0, 0, 153);"&gt;rec&lt;/span&gt; decode' node l =&lt;br /&gt;       &lt;span style="color: rgb(0, 0, 153);"&gt;match&lt;/span&gt; node, l &lt;span style="color: rgb(0, 0, 153);"&gt;with&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;        // If we've arrived at a leaf, this must be the character&lt;br /&gt;       // we're looking for. Add it to the sequence, and resume&lt;br /&gt;       // from the top of the tree.&lt;br /&gt;       // TODO Add an accumulator to make this tail-recursive.&lt;/span&gt;&lt;br /&gt;        Leaf leaf, _ &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; leaf.Char :: (decode h l)&lt;br /&gt;&lt;span style="color: rgb(0, 102, 0);"&gt;        // A 0 in the stream means a left branch during&lt;br /&gt;       // the encoding, and v.v.&lt;/span&gt;&lt;br /&gt;        Tree tree, n::tail &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(0, 0, 153);"&gt;if&lt;/span&gt; n = 0 &lt;span style="color: rgb(0, 0, 153);"&gt;then&lt;/span&gt; decode' tree.Left tail &lt;span style="color: rgb(0, 0, 153);"&gt;else&lt;/span&gt; decode' tree.Right tail&lt;br /&gt;       &lt;span style="color: rgb(0, 102, 0);"&gt;// Something was wrong in the encoding.&lt;/span&gt;&lt;br /&gt;        _, _ -&amp;gt; &lt;span style="color: rgb(0, 0, 153);"&gt;failwith&lt;/span&gt; "Something has gone very wrong."&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 153);"&gt;match&lt;/span&gt; l &lt;span style="color: rgb(0, 0, 153);"&gt;with&lt;/span&gt;&lt;br /&gt;    [] &lt;span style="color: rgb(0, 0, 153);"&gt;-&amp;gt;&lt;/span&gt; []&lt;br /&gt;   &lt;span style="color: rgb(0, 102, 0);"&gt;// Start navigating down the tree.&lt;/span&gt;&lt;br /&gt;    _ -&amp;gt; decode' h l&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; make_tree s =&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; d = count s&lt;br /&gt;   &lt;span style="color: rgb(0, 102, 0);"&gt;// IDictionary&amp;lt;_, _&amp;gt; implements&lt;br /&gt;   // ICollection&amp;lt;KeyValuePair&amp;lt;_,_&amp;gt;&amp;gt;, so enumerate the&lt;br /&gt;   // pairs and convert each into a leaf node.&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 153);"&gt;let&lt;/span&gt; leaves = d |&amp;gt; Seq.map (&lt;span style="color: rgb(0, 0, 153);"&gt;fun&lt;/span&gt; pair -&amp;gt;&lt;br /&gt;                                   Leaf { Count = pair.Value;&lt;br /&gt;                                          Char = pair.Key }; )&lt;br /&gt;                  |&amp;gt; Seq.to_list&lt;br /&gt;                  |&amp;gt; List.sort node_priority&lt;br /&gt;   reduce_list leaves&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-2246991049379880399?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/2246991049379880399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=2246991049379880399' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2246991049379880399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2246991049379880399'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/01/huffman-coding-almost-done.html' title='Huffman Coding in F#'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-653266710085662211</id><published>2008-01-16T15:29:00.001-05:00</published><updated>2008-01-16T23:03:48.592-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SICP'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><category scheme='http://www.blogger.com/atom/ns#' term='Huffman'/><title type='text'>Huffman Coding, SICP Exercise 2.69</title><content type='html'>As a convenience, I defined Incidence as &lt;span style="font-family:courier new;"&gt;System.Collections.Generic.Dictionary&lt;char,&gt;&lt;/char,&gt;&lt;/span&gt;. F#'s Map type&lt;br /&gt;is actually a functor, and each insertion or removal returns a new instance. What I'd really like is something like the &lt;span style="font-family:arial;"&gt;&lt;span style="font-family: courier new;"&gt;FindOrAdd()&lt;/span&gt; &lt;/span&gt;method available in the  &lt;a href="http://www.itu.dk/research/c5/"&gt;C5 &lt;/a&gt;dictionary implementations, but I can live without it. Anyway, I intend to fold a sequence of characters into a dictionary, for which purpose I'll first define the function that will do the folding:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let count (d : Incidence) (c : char) =&lt;br /&gt;-     let n = ref 0&lt;br /&gt;-     // I need a "ref" variable to serve as an "out" parameter.&lt;br /&gt;-     if d.TryGetValue(c, n) then&lt;br /&gt;-         // Assign to a mutable property...&lt;br /&gt;-         d.[c] &lt;- !n + 1 &lt;br /&gt;-     else &lt;br /&gt;-         d.Add(c, 1) &lt;br /&gt;-     d;;  &lt;br /&gt;val count : Incidence -&gt; char -&gt; Incidence&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I'm getting good at this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let d = "Tony Nassar, Software Engineer" |&gt; Seq.fold count (new Incidence());;&lt;br /&gt;&lt;br /&gt;val d : Incidence&lt;br /&gt;&lt;br /&gt;&gt; d;;&lt;br /&gt;val it : Incidence&lt;br /&gt;= dict&lt;br /&gt;    [('T', 1); ('o', 2); ('n', 3); ('y', 1); (' ', 3); ('N', 1); ('a', 3);&lt;br /&gt;     ('s', 2); ('r', 3); (',', 1); ('S', 1); ('f', 1); ('t', 1); ('w', 1);&lt;br /&gt;     ('e', 3); ('E', 1); ('g', 1); ('i', 1)]&lt;br /&gt;&gt;&lt;br /&gt;&lt;/pre&gt;I did not know until today that System.String implements IEnumerable! And, fortunately, F#'s type inference lets me pipe that sequence of characters into my &lt;span style="font-family:courier new;"&gt;count()&lt;/span&gt; function. There are two steps that I need to take now: I need to create a sequence sorted by frequency (actually, a mapping from frequency to character, where the frequencies don't have to be unique) and turn it into a tree. This took me a while. Unlike C#, F# only allows mutually recursive type definitions if they're linked with "and." Here I'm declaring HuffmanNode as a "discriminated union," where Leaf and Tree are implemented as tags (you can verify this by using Reflector to examine the compiled DLL), and a leaf consists of the character to be encoded and its frequency (the latter is necessary during the construction of the tree), and a tree (or any non-leaf node) is...well, it's kinda obvious. If this were C, and someone were using a union and a type code, I'd say "Yuck!" But since it's functional programming, and this is a very handy way to build up a tree, it's suddenly cool again.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; type HuffmanNode =&lt;br /&gt;- | Leaf of HuffmanLeaf&lt;br /&gt;- | Tree of HuffmanTree&lt;br /&gt;- and HuffmanLeaf = { Count : int; Char : char }&lt;br /&gt;- and HuffmanTree = { Count : int; Chars : CharSet; Left : HuffmanNode; Right : HuffmanNode };;&lt;br /&gt;&lt;br /&gt;type HuffmanNode =&lt;br /&gt;  | Leaf of HuffmanLeaf&lt;br /&gt;  | Tree of HuffmanTree&lt;br /&gt;and HuffmanLeaf = {Count: int;&lt;br /&gt;                   Char: char;}&lt;br /&gt;and HuffmanTree = {Count: int;&lt;br /&gt;                   Chars: CharSet;&lt;br /&gt;                   Left: HuffmanNode;&lt;br /&gt;                   Right: HuffmanNode;}&lt;br /&gt;&lt;br /&gt;&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-653266710085662211?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/653266710085662211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=653266710085662211' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/653266710085662211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/653266710085662211'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/01/huffman-coding-sicp-exercise-269_16.html' title='Huffman Coding, SICP Exercise 2.69'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-6925319474016896712</id><published>2008-01-15T22:57:00.000-05:00</published><updated>2008-02-06T07:51:12.905-05:00</updated><title type='text'>Eight Queens: I couldn't let it go!</title><content type='html'>If you're not used to functional programming, and I'm not, you may respond with irritation at an FPer's "How few lines of code can I use to solve this problem?" showpiece. A good example would be the textbook problem of transposing a matrix (for which the answer is posted on &lt;a href="http://blogs.msdn.com/jomo_fisher/archive/2007/11/17/tight-code-a-puzzle-in-f.aspx"&gt;Jomo Fischer's blog&lt;/a&gt;). If you really need to transpose a lot of matrices, you're probably going to be working in an environment where such operations are optimized, e.g. Mathematica. In other words, you're not going to do it yourself. So if anyone is irritated with the following code, let me say that I had to ponder this (on and off, not continuously!) for a few months before I arrived at the "right" solution, and I do feel that it's right. However, trying to implement it in F# gave me some valuable insights into how it ought to be done in other languages. Here's my solution:&lt;br /&gt;&lt;pre&gt;&amp;gt; let queens size =&lt;br /&gt;-     let range = [1..size]&lt;br /&gt;-     range |&gt; Seq.fold (fun answers column -&gt;&lt;br /&gt;-         let positions = [for row in range -&gt; (column, row)]&lt;br /&gt;-         [for p in positions for a in answers when all_safe p a -&gt; a@[p]]) [[]]&lt;br /&gt;- ;;&lt;br /&gt;&lt;br /&gt;val queens : int -&amp;gt; (int * int) list list&lt;br /&gt;&lt;br /&gt;&amp;gt; queens 5;;&lt;br /&gt;val it : (int * int) list list&lt;br /&gt;= [[(1, 4); (2, 2); (3, 5); (4, 3); (5, 1)];&lt;br /&gt; [(1, 3); (2, 5); (3, 2); (4, 4); (5, 1)];&lt;br /&gt; [(1, 5); (2, 3); (3, 1); (4, 4); (5, 2)];&lt;br /&gt; [(1, 4); (2, 1); (3, 3); (4, 5); (5, 2)];&lt;br /&gt; [(1, 5); (2, 2); (3, 4); (4, 1); (5, 3)];&lt;br /&gt; [(1, 1); (2, 4); (3, 2); (4, 5); (5, 3)];&lt;br /&gt; [(1, 2); (2, 5); (3, 3); (4, 1); (5, 4)];&lt;br /&gt; [(1, 1); (2, 3); (3, 5); (4, 2); (5, 4)];&lt;br /&gt; [(1, 3); (2, 1); (3, 4); (4, 2); (5, 5)];&lt;br /&gt; [(1, 2); (2, 4); (3, 1); (4, 3); (5, 5)]]&lt;br /&gt;&amp;gt; queens 4;;&lt;br /&gt;val it : (int * int) list list&lt;br /&gt;= [[(1, 3); (2, 1); (3, 4); (4, 2)]; [(1, 2); (2, 4); (3, 1); (4, 3)]]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;First, I did not use a for loop. Not because that would just be too imperative, but because I realized that the range (e.g. from 1..8, for an 8 x 8 chessboard) was actually an (immutable) object that I needed to use in several ways. To generate all the positions on a chessboard, I would then use a "sequence comprehension" such as { for column_number in range for row_number in range -&gt; (column_number, row_number) }. I've begun to see this as more natural than nested for loops. &lt;span style="font-style: italic;"&gt;I'm&lt;/span&gt; not doing with side effects in a loop; I'm asking for a single object (a collection of board positions) to be created for me. So the comprehension, a declarative construct, is more natural: "Here's what I want; give it to me. I'm not interested in putting it together piece by piece; that's your job." In comparison, the idiom 1, create a list, 2, go through several loops, appending on each innermost iteration, 3, finally do something with the collection (which is probably mutable, even though it really shouldn't be), obscures the intention, and is error-prone. &lt;br /&gt;&lt;br /&gt;Then I iterate over that range (now treating it as the sequence of column numbers), and for every column I take the sequence of answers so far (i.e., each such answer is a sequence of one position each from the columns I've seen so far) and extend each one with each position in the current column (i.e. the current element of the range) that's safe. Below, I defined the functions safe() and all_safe(). I can easily generate board positions for the current column by means of another comprehension (let positions = etc.). Then I take the Cartesian product of the answers so far and this new column, filter all these new candidates through all_safe, and pass this new collection on to the next iteration, via fold(). For obvious reasons, Seq.fold starts with the collection of known 0-length answers, which is of course [[]].&lt;br /&gt;&lt;br /&gt;If I were (re)doing it in Scheme, I'd rip off &lt;a href="http://www.koders.com/scheme/fid171794757BE08923FBA59197431C72C7AD5EA99A.aspx?s=comprehension+dybvig#L3"&gt;Kent Dybvig's comprehension macro&lt;/a&gt;, first off. I'd also rip off &lt;a href="http://srfi.schemers.org/srfi-1/srfi-1.html"&gt;an implementation of fold&lt;/a&gt;, if I didn't already have one. I'd generate a range 1..n just as I do above. Then, on every iteration (I believe it &lt;i&gt;is&lt;/i&gt; an iteration, if I'm folding from the left) I create the Cartesian product of the answers so far and the current column (which I just generated), &lt;span style="font-family: courier new;"&gt;(filter) &lt;/span&gt;these pairs through&lt;span style="font-family: courier new;"&gt; (all-safe)&lt;/span&gt;, and continue. Something like that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-6925319474016896712?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/6925319474016896712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=6925319474016896712' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/6925319474016896712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/6925319474016896712'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/01/eight-queens-i-couldnt-let-it-go.html' title='Eight Queens: I couldn&apos;t let it go!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-3159806299905829213</id><published>2008-01-15T21:58:00.000-05:00</published><updated>2008-01-15T22:57:43.310-05:00</updated><title type='text'>Eight Queens, and an End</title><content type='html'>Once again I was led astray by having done too much imperative programming. I &lt;span style="font-style: italic;"&gt;hate&lt;/span&gt; nesting loops to create collections, but I didn't go all the way. If the columns and rows of the chessboard are represented as a range—the same range—then all the positions are produced by taking the Cartesian product of that range with itself. Then I can iterate across the columns, and, for each one, take the Cartesian product of the rows in that column and the answers so far, and &lt;span style="font-style: italic;"&gt;fold&lt;/span&gt; the new answers into a collection that was empty to start with. Ah, &lt;span style="font-style: italic;"&gt;fold&lt;/span&gt;, delight of all functional programmers. To wit:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let queens size =&lt;br /&gt;-     // the row numbers *and* the column numbers&lt;br /&gt;-     let range = [1..size]&lt;br /&gt;-     let queens' columns =&lt;br /&gt;-         columns |&gt; Seq.fold (fun answers column -&gt;&lt;br /&gt;-             let positions = [for row in range -&gt; (column, row)]&lt;br /&gt;-             [for p in positions for a in answers when all_safe p a -&gt; a@[p]])&lt;br /&gt;-             [[]]&lt;br /&gt;-     queens' range;;&lt;br /&gt;&lt;br /&gt;val queens : int -&gt; (int * int) list list&lt;br /&gt;&lt;br /&gt;&gt; queens 4;;&lt;br /&gt;val it : (int * int) list list&lt;br /&gt;= [[(1, 3); (2, 1); (3, 4); (4, 2)]; [(1, 2); (2, 4); (3, 1); (4, 3)]]&lt;br /&gt;&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-3159806299905829213?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/3159806299905829213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=3159806299905829213' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3159806299905829213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3159806299905829213'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2008/01/eight-queens-and-end.html' title='Eight Queens, and an End'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-7071219854217684952</id><published>2007-12-08T13:49:00.000-05:00</published><updated>2008-01-15T22:26:18.159-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='SICP'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>Eight Queens in F#</title><content type='html'>Man, I'm really going back to school here. The last time I did this problem was when I was &lt;a href="http://www.answers.com/topic/eight-queens-puzzle"&gt;learning Prolog&lt;/a&gt;. Let's say you're trying to place a queen in the first column. Prolog's &lt;span style="font-family:courier new;"&gt;select &lt;/span&gt;function will pick one of the rows, and create a branch to explore the remaining rows. If row 3 was selected, say, you don't have to write any code to concatenate [1..2] and [4..8], in order to pass that smaller set of unoccupied rows to the next iteration / recursion. Very handy! No loops, no list operations, etc.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; #light;;&lt;br /&gt;&gt; type Position = int * int;;&lt;br /&gt;&lt;br /&gt;type Position = int * int&lt;br /&gt;&lt;br /&gt;&gt; let safe p q =&lt;br /&gt;-     fst p &lt;&gt; fst q &amp;amp;&amp;amp; snd p &lt;&gt; snd q &amp;amp;&amp;amp; fst p + snd p &lt;&gt; fst q + snd q &amp;amp;&amp;amp; fst p - snd p &lt;&gt; fst q - snd q;;&lt;br /&gt;&lt;br /&gt;val safe : int * int -&gt; int * int -&gt; bool&lt;br /&gt;&lt;br /&gt;&gt; let all_safe p positions =&lt;br /&gt;-     Seq.for_all (safe p) positions;;&lt;br /&gt;&lt;br /&gt;val all_safe : int * int -&gt; #seq&lt;int&gt; -&gt; bool&lt;br /&gt;&lt;br /&gt;&gt; all_safe (1, 2) [(1, 2)];;&lt;br /&gt;val it : bool = false&lt;br /&gt;&gt; all_safe (1, 2) [(3, 3)];;&lt;br /&gt;val it : bool = true&lt;br /&gt;&lt;br /&gt;&gt; let range = [1..8];;&lt;br /&gt;&lt;br /&gt;val range : int list&lt;br /&gt;&lt;br /&gt;&gt; range;;&lt;br /&gt;val it : int list = [1; 2; 3; 4; 5; 6; 7; 8]&lt;br /&gt;&gt; let queens size =&lt;br /&gt;-     let range = [1..size]&lt;br /&gt;-     let rec queens' columns answers =&lt;br /&gt;-         match columns with&lt;br /&gt;-         | [] -&gt; answers&lt;br /&gt;-         | column::tail -&gt; queens' tail [for answer in answers for position in [for row in range -&gt; (column, row)]  when all_safe position answer -&gt; position::answer]&lt;br /&gt;-     queens' range [[]];;&lt;br /&gt;&lt;br /&gt;val queens : int -&gt; (int * int) list list&lt;br /&gt;&lt;br /&gt;&gt; queens 4;;&lt;br /&gt;val it : (int * int) list list&lt;br /&gt;= [[(4, 3); (3, 1); (2, 4); (1, 2)]; [(4, 2); (3, 4); (2, 1); (1, 3)]]&lt;br /&gt;&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Implemented recursively, it's:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let queens size =&lt;br /&gt;-     let range = [1..size]&lt;br /&gt;-     let rec queens' columns =&lt;br /&gt;-         match columns with&lt;br /&gt;-         | column::tail -&gt; [for answer in (queens' tail) for position in [for row in range -&gt; (column, row)]  when all_safe position answer -&gt; position::answer]&lt;br /&gt;-         | _ -&gt; [[]]&lt;br /&gt;-     queens' range;;&lt;br /&gt;&lt;br /&gt;val queens : int -&gt; (int * int) list list&lt;br /&gt;&lt;br /&gt;&gt; queens 4&lt;br /&gt;- ;;&lt;br /&gt;val it : (int * int) list list&lt;br /&gt;= [[(1, 3); (2, 1); (3, 4); (4, 2)]; [(1, 2); (2, 4); (3, 1); (4, 3)]]&lt;br /&gt;&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-7071219854217684952?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/7071219854217684952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=7071219854217684952' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/7071219854217684952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/7071219854217684952'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/12/eight-queens-in-f.html' title='Eight Queens in F#'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-1936503061053683986</id><published>2007-12-07T20:17:00.001-05:00</published><updated>2007-12-10T17:30:14.472-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional programming'/><category scheme='http://www.blogger.com/atom/ns#' term='F#'/><title type='text'>F#, finally!</title><content type='html'>I've been champing at the bit to actually write some F#, but haven't been willing to risk sneaking it into production at the office, and unable to put in extra time at home to learn it. I have read Robert Pickering's &lt;a href="http://www.bookpool.com/sm/1590597575"&gt;Foundations of F#&lt;/a&gt; while commuting, but that's not verifiable experience in functional programming. As to why I would care whether I learn functional programming or not, let me plug the &lt;a href="http://www.lisperati.com/fringedc.html"&gt;FringeDC &lt;/a&gt;interest group, founded by the estimable Dr. Conrad Barski. Not only have I found the members to be charming and personable, but they also seem much more incisive about what programming is than most developers I meet. They're not, for the most part, doing enterprise-scale stuff, where decisions about programming languages, database vendors, and so on  would probably be taken out of their hands. Unless they're &lt;a href="http://www.paulgraham.com/"&gt;Paul Graham&lt;/a&gt;, who got rich writing LISP. Good for him! Anyway, lots of smart people write Java, or even VB. Nonetheless, the kicks in the butt that I've gotten at FringeDC, as well as from graduate classes at George Mason U., have led me to feel that what would do me the most good, and what I would most enjoy, is something more fundamental, theoretical, than spending every weekend preparing for MCAD exams. Different strokes.&lt;br /&gt;&lt;br /&gt;Alright, enough about that. Languages that offer a REPL seem really suited to scripting. Yes, my friends at FringeDC would probably do everything in Emacs Scheme, but...I just don't know how anymore. Which I'm not proud of. Visual Studio has actually spoiled me to the point where I wouldn't know how to run some XML through an XSLT from the command line. I would actually have to take time out to figure it out. OK, really enough. Here's my task: I've got some XML describing the expected return values from some linear programming models, and it's tacked on top of some older regression testing scripts that just list the files to be solved by particular solvers. The XML file is called &lt;span style="font-family:courier new;"&gt;knownSolutions.xml&lt;/span&gt;, and the scripts are called &lt;span style="font-family:courier new;"&gt;solveBatch.cplex.txt&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;solveBatch.lpSolve.txt&lt;/span&gt;. Here's what I want to do: load the XML file, and, when a particular solver is not supposed to solve a model, add a corresponding attribute to the element. I've changed the schema:&lt;br /&gt;&lt;pre&gt;&amp;lt;xs:attribute name="SkipSolvers" type="solverTypeList" use="optional"&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;xs:simpleType name="&lt;span style="font-weight: bold;"&gt;solverType&lt;/span&gt;"&amp;gt;&lt;br /&gt;&amp;lt;xs:restriction base="xs:string"&amp;gt;&lt;br /&gt;&amp;lt;xs:enumeration value="lpSolve" /&amp;gt;&lt;br /&gt;&amp;lt;xs:enumeration value="Cplex" /&amp;gt;&lt;br /&gt;&amp;lt;/xs:restriction&amp;gt;&lt;br /&gt;&amp;lt;/xs:simpleType&amp;gt;&lt;br /&gt;&amp;lt;xs:simpleType name="&lt;span style="font-weight: bold;"&gt;solverTypeList&lt;/span&gt;"&amp;gt;&lt;br /&gt;&amp;lt;xs:list itemType="solverType" /&amp;gt;&lt;br /&gt;&amp;lt;/xs:simpleType&amp;gt;&lt;br /&gt;&lt;/pre&gt;So if we have the XML:&lt;br /&gt;&lt;pre&gt;&amp;lt;KnownSolutions&amp;gt;&lt;br /&gt;&amp;lt;Model skipsolvers="lpSolve"&amp;gt;&lt;br /&gt; &amp;lt;File&gt;TestModels\JasonCh3Net.xml&amp;lt;/File&amp;gt;&lt;br /&gt; &amp;lt;ObjectiveValue&gt;196200&amp;lt;/ObjectiveValue&amp;gt;&lt;br /&gt;&amp;lt;/Model&amp;gt;&lt;br /&gt;&amp;lt;/KnownSolutions&amp;gt;&lt;br /&gt;&lt;/pre&gt;...then I'd like to add attributes like so:&lt;br /&gt;&lt;pre&gt;&amp;lt;Model SkipSolvers="lpSolve" .../&amp;lt;&lt;/pre&gt;&lt;br /&gt;So I need to load the XML from the F# console:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; open System.Xml;; # Import the assembly and the corresponding namespace.&lt;br /&gt;   # The F# console waits for two semicolons before it interprets&lt;br /&gt;   # your statement.&lt;br /&gt;&lt;br /&gt;&amp;gt; let document = new XmlDocument();; # Infer the variable's type.&lt;br /&gt;&lt;br /&gt;val document : XmlDocument&lt;br /&gt;&lt;br /&gt;&amp;gt; document.Load @"knownSolutions.xml";;&lt;br /&gt;val it : unit = () # The Load() method returns void, in other words.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now at this point I've got to make some decisions about, you know, algorithms and data structures. I've got a tree—XML—and I've got a list of files that I can't assume are sorted. So why don't I read them in, filter out the empty lines, sort them, and then iterate across the XML's elements looking for files that &lt;i&gt;aren't&lt;/i&gt; in the list for Cplex. Since I'm going to be looking things up in the list, I'll make a set out of it first. I should point out that, in contrast to imperative programming, we do &lt;i&gt;not&lt;/i&gt; want to open the file and loop over the lines. We want to treat the file as a sequence of lines, no different than &lt;span style="font-family:courier new;"&gt;IEnumerable&amp;lt;string&amp;gt;&lt;/span&gt;. I say "we," but I mean "I": I just don't want to write nested loops anymore. But how do I deal with the EOF? I use a "generator" that has a special way of "terminating" the sequence of lines it reads. This is the "pipes and filters" idiom familier to UNIX folks. F#'s Seq class provides a &lt;a href="http://blogs.technet.com/apg/archive/2006/11/04/dealing-with-terabytes-with-f.aspx"&gt;generate_using &lt;/a&gt;function that thoughtfully calls IDispose() on the object created at the beginning of the generation.&lt;pre&gt;&amp;gt; let opener () = File.OpenText("something.txt");;&lt;br /&gt;&lt;br /&gt;val opener : unit -&amp;gt; StreamReader&lt;br /&gt;&amp;gt; &gt; opener;;&lt;br /&gt;val it : (unit -&amp;gt; StreamReader) = &amp;lt;fun:clo@0_3&amp;gt;&lt;br /&gt;&lt;/pre&gt;What that means is that opener is a function with no arguments (if I'd left off the parentheses before the equals sign, F# would assign the return value of &lt;span style="font-family:courier new;"&gt;OpenText() &lt;/span&gt;to &lt;span style="font-family:courier new;"&gt;opener&lt;/span&gt;, which is not what I want), that knows how to return a StreamReader.&lt;span style="font-family:courier new;"&gt; generate_using &lt;/span&gt;takes that residual value (I don't think it's called a return value, &lt;i&gt;per se&lt;/i&gt;) and passes it  repeatedly to the actual generator, ergo:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let liner (reader : StreamReader) =&lt;br /&gt;- if reader.EndOfStream then None&lt;br /&gt;- else Some(reader.ReadLine());;&lt;br /&gt;&lt;br /&gt;val liner : StreamReader -&amp;gt; string option&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;option&lt;/i&gt; means "either a string, or nothing." Don't think of it as null; &lt;a href="http://research.microsoft.com/fsharp/manual/import-interop.aspx"&gt;F# doesn't really have null.&lt;/a&gt; None will mark the end of the generated sequence. generate_using keeps sending the stream as an argument to the "closure" liner, and liner decides whether it's got something worth returning; if not, it signals to the next thing in line, "I'm done." I don't need to point out how parallelizable this "pipes and filters" idiom is; whatever is waiting for a line of text only needs one line to do its job. Anyway, now I'll filter out the empty strings, and put all of them in a hashed set for fast lookup (I could also just remove the empty string from the hashed set after populating it). I'll create the filter separately, rather than inline:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let filter s = (s &amp;lt;&amp;gt; "");;&lt;br /&gt;&lt;br /&gt;val filter : string -&amp;gt; bool&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The static &lt;span style="font-family:courier new;"&gt;Seq.filter&lt;/span&gt; method requires a function that takes a sequence element and returns a Boolean value, in this case false if the string is empty. In order to create a set that's initialized to include all the values in the generated sequence, I do:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let lpSolveSet = HashSet&amp;lt;string&amp;gt;.Create(&lt;br /&gt;- Seq.generate_using opener liner&lt;br /&gt;- |&amp;gt; Seq.filter (fun s -&amp;gt; (s &amp;lt;&amp;gt; ""))&lt;br /&gt;- );;&lt;br /&gt;&lt;br /&gt;val lpSolveSet : HashSet&amp;lt;string&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Whew! I don't know if F#'s type inference would have let me leave out the &amp;lt;string&amp;gt;. Putting it together, as I suppose I could have done in 5 minutes if I were a PowerShell guru:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; Seq.generate_using (fun () -&amp;gt; File.OpenText @"solveBatch.lp_solve.txt")&lt;br /&gt;- (fun reader -&amp;gt; if reader.EndOfStream then None else Some(reader.ReadLine()))&lt;br /&gt;- |&amp;gt; Seq.filter (fun s -&amp;gt; (s &amp;lt;&amp;gt; ""))&lt;br /&gt;- |&amp;gt; Seq.map (fun s -&amp;gt; lpSolveSet.Add(s));;&lt;br /&gt;val it : seq&amp;lt;unit&amp;gt; = seq [null; null; null; null; ...]&lt;br /&gt;&amp;gt; lpSolveSet;;&lt;br /&gt;val it : HashSet&amp;lt;string&amp;gt;&lt;br /&gt;= seq&lt;br /&gt;["JasonCh3Net.xml"; "JohnCh4Net.xml"; "OffsetNet.xml";&lt;br /&gt;"WithinClauseTestNet.xml"; ...]&lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In other words, generate a sequence of lines from a file , pass them through a filter that removes empty strings, and send the resulting sequence to the HashSet's constructor. I could also have piped the filtered sequence to a closure that would add each string to the set in turn. Whatever. Now...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; let models = document.SelectNodes "/KnownSolutions/Model";;&lt;br /&gt;&lt;br /&gt;val models : XmlNodeList&lt;br /&gt;&lt;br /&gt;&amp;gt; models;;&lt;br /&gt;val it : XmlNodeList&lt;br /&gt;= seq&lt;br /&gt;[seq [seq [seq []]; seq [seq []]]; seq [seq [seq []]; seq [seq []]];&lt;br /&gt;seq [seq [seq []]; seq [seq []]]; seq [seq [seq []]; seq [seq []]]; ...]&lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Oh, so it's a sequence! Now I'm rolling, so:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; Seq.hd models;;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  Seq.hd models;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  -------^^^^^^^&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;stdin(131,7): error: FS0001: The type XmlNodeList is not compatible with the type seq&amp;lt;'a&amp;gt;&lt;/span&gt;&lt;br /&gt;stopped due to error&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-family:courier new;"&gt;Seq.hd&lt;/span&gt; ("head") is like LISP's &lt;span style="font-family:courier new;"&gt;car&lt;/span&gt;, btw. Hm, it's not a sequence. Embarrassing. I scratched my head for a few minutes, then realized that I should use a list comprehension:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; { for o in models -&gt; o :?&gt; XmlElement };;&lt;br /&gt;val it : seq&lt;xmlelement&gt;&lt;br /&gt;= seq&lt;br /&gt;[seq [seq [seq []]; seq [seq []]]; seq [seq [seq []]; seq [seq []]];&lt;br /&gt;seq [seq [seq []]; seq [seq []]]; seq [seq [seq []]; seq [seq []]]; ...]&lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;/xmlelement&gt;&lt;/pre&gt;I apply the downcast operator to each element; now I have a strongly typed sequence, so F# can use type inference on the next element in the chain:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let attrAdder (e : XmlElement) =&lt;br /&gt;- if not (lpSolveSet.Contains(e.InnerText)) then begin&lt;br /&gt;-   let attr = document.CreateAttribute("SkipSolvers") in begin&lt;br /&gt;-     attr.Value &amp;lt;- "lpSolve";&lt;br /&gt;-     e.Attributes.Append(attr)&lt;br /&gt;-   end&lt;br /&gt;- end;;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;      e.Attributes.Append(attr)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;  -----------------^^^^^^^^^^^^^&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;stdin(87,17): error: Type constraint mismatch. The type&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;        XmlAttribute&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;is not compatibile with type&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;        unit.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;The type XmlAttribute is not compatible with the type unit&lt;/span&gt;&lt;br /&gt;stopped due to error&lt;br /&gt;&lt;/pre&gt;What? I scratched my head over this one. In the end, the error message makes perfect sense: because there's no "alternative," the "consequent" is not allowed to return a value, and XmlAttributeCollection.Append() does. So we have to pipe it into oblivion, hence:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; let attrAdder (e : XmlElement) =&lt;br /&gt;- if not (lpSolveSet.Contains(e.InnerText)) then begin&lt;br /&gt;-   let attr = document.CreateAttribute("SkipSolvers") in begin&lt;br /&gt;-     attr.Value &amp;amp;lt- "lpSolve";&lt;br /&gt;-     e.Attributes.Append(attr) &lt;span style="font-weight: bold;"&gt;|&amp;gt; ignore&lt;/span&gt;&lt;br /&gt;-   end&lt;br /&gt;- end;;&lt;br /&gt;&lt;br /&gt;val attrAdder : XmlElement -&gt; unit&lt;br /&gt;&lt;/pre&gt;So this is the little function object that will add the required attribute to the element if the lpSolve solver is not supposed to solve it. Then:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt; { for o in models -&gt; o :?&amp;gt; XmlElement } |&amp;gt; Seq.iter adder;;&lt;br /&gt;val it : unit = ()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And lo and behold, I get this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; {for node in document.SelectNodes "/KnownSolutions/Model" -&gt; node :?&amp;gt; XmlElement } |&amp;gt; Seq.iter adder;;&lt;br /&gt;System.ArgumentException: The named node is from a different document context.&lt;br /&gt;at System.Xml.XmlAttributeCollection.Append(XmlAttribute node)&lt;br /&gt;at FSI_0014.adder(XmlElement e)&lt;br /&gt;at FSI_0074.it@150.Invoke(XmlElement e@51_6)&lt;br /&gt;at Microsoft.FSharp.Collections.Seq.iter@868.Invoke(IEnumerator`1 e@90_1)&lt;br /&gt;at Microsoft.FSharp.Core.Operators.using[T,U](T ie, FastFunc`2 f)&lt;br /&gt;at Microsoft.FSharp.Collections.Seq.iter[T,U](FastFunc`2 f, U ie)&lt;br /&gt;at &amp;lt;StartupCode&amp;gt;.FSI_0074._main()&lt;br /&gt;stopped due to error&lt;br /&gt;&lt;/pre&gt;After about an hour, I realized that the function object that's adding the attributes is a &lt;a href="http://en.wikipedia.org/wiki/Closure_%28computer_science%29"&gt;closure&lt;/a&gt;: that means that it encapsulates a reference to a previous value of &lt;span style="font-family:courier new;"&gt;document&lt;/span&gt;. That's what "closure" means, silly! I create a new closure that refers to the new XML document, rerun, and I'm done, almost:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;gt; document.Save @"knownSolutionsImproved.xml"&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-1936503061053683986?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/1936503061053683986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=1936503061053683986' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1936503061053683986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1936503061053683986'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/12/f-finally.html' title='F#, finally!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-5361365256792038826</id><published>2007-11-30T16:38:00.000-05:00</published><updated>2007-11-30T16:59:48.244-05:00</updated><title type='text'>Now it's Chinese music...</title><content type='html'>Yep, I've listened to nothing but flamenco for several weeks. Then I wanted to see if YouTube had any clips of Sabicas, and saw that someone had posted a comment comparing him to Liu Fang. I don't quite see the comparison, except in terms of their virtuosity, but if you've been dying to hear a performance of "King Chu doffs his armor," &lt;a href="http://youtube.com/watch?v=TCAU797437g"&gt;here it is&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-5361365256792038826?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/5361365256792038826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=5361365256792038826' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5361365256792038826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5361365256792038826'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/11/now-its-chinese-music.html' title='Now it&apos;s Chinese music...'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-8493967167196931030</id><published>2007-11-17T16:54:00.000-05:00</published><updated>2008-02-08T09:14:26.042-05:00</updated><title type='text'>The FBI in Movies, the FBI in Reality</title><content type='html'>In &lt;em&gt;Broken&lt;/em&gt;, Richard Gid Powers suggests that the FBI has never actually had a mission, and many of it failures can be attributed to its flailing about in the effort to find one. J. Edgar Hoover's insistence on chasing a tiny number of radicals while completely ignoring the much more serious problem of organized crime was not simply due to his cynicism; it was also due to the confusion in the definition of a federal crime, and his unwillingness to embarrass himself trying and failig to solve an actually difficult problem. It was also the beginning of the FBI's self-promotion through popular entertainment ("The Untouchables" and all that). &lt;br /&gt;&lt;br /&gt;Now, I'm a sucker for crime shows, but there is a powerful whiff of bullshit in the depiction of the profiling of serial killers in such movies as &lt;em&gt;The Silence of the Lambs&lt;/em&gt; (not to mention something really repulsive, not to mention factually incorrect, in the depiction of them as evil geniuses in &lt;em&gt;SotL&lt;/em&gt;, the contemptible &lt;em&gt;Seven&lt;/em&gt;...I could go on), and that whiff emanates from John Douglas, the FBI's "eminent" profiler, the model for &lt;em&gt;SotL&lt;/em&gt;'s Jack Crawford. Malcolm Gladwell recently argued in &lt;a href="http://www.newyorker.com/reporting/2007/11/12/071112fa_fact_gladwell?currentPage=all"&gt;The New Yorker &lt;/a&gt;that what these profilers do is not so different from the "cold readings" that psychics such as John Edward do, namely, spew enough predictions that some of them have to stick. Even about such simple, empirically verifiable aspects of a killer's identity as, oh, his age, his skin color, his intelligence...Douglas is usually wrong. Even if he were right, how would his colleagues go about looking for an unusually intelligent, unmarried white man whom no evidence links to the crime? Profiling's basis in research is rather weak; imprisoned serial killers are *not* reliable sources, and the more intelligent ones have ample opportunity to construct &lt;em&gt;ex post facto&lt;/em&gt; justifications for details of the crime if that will keep some psychologist talking to them. Even if this were not so, "research" on imprisoned serial killers has not been conducted according to proper research protocols, and the findings would be impossible to replicate. Gladwell: "Not long ago, a group of psychologists at the University of Liverpool decided to test the FBI's assumptions [that a criminal typology can be deduced from crime-scene details]...When they looked at a sample of a hundred serial crimes, however, they couldn't find any support for the FBI's distinction [between 'organized' and 'disorganized' killings and therefore killers]. Crimes don't fall into one camp or the other. It turns out that they're almost always a mixture of &lt;em&gt;a few key organized traits&lt;/em&gt; [emphasis mine&amp;mdash;TN] and a random array of disorganized traits. Laurence Alison, one of the leaders of the Liverpool group and the author of &lt;em&gt;The Forensic Psychologist's Casebook&lt;/em&gt;,'The whole business is a lot more complicated than the FBI imagines.'"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-8493967167196931030?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/8493967167196931030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=8493967167196931030' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8493967167196931030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8493967167196931030'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/11/fbi-in-movies-fbi-in-reality.html' title='The FBI in Movies, the FBI in Reality'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-8408912499443750101</id><published>2007-11-17T16:43:00.000-05:00</published><updated>2007-11-17T16:45:37.412-05:00</updated><title type='text'>Unit-Testing XML, XSLT</title><content type='html'>OK, I posted my suggested design for an XML fixture to the &lt;a href="http://groups.google.com/group/MbUnitUser/browse_thread/thread/7af889f569bcceb6"&gt;MbUnit user's group&lt;/a&gt;. I ought to use this blog for self-promotion &lt;span style="font-style:italic;"&gt;sometimes.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-8408912499443750101?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/8408912499443750101/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=8408912499443750101' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8408912499443750101'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8408912499443750101'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/11/unit-testing-xml-xslt.html' title='Unit-Testing XML, XSLT'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-3583711806857733478</id><published>2007-11-05T10:50:00.000-05:00</published><updated>2007-11-09T11:24:11.710-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MBunit'/><category scheme='http://www.blogger.com/atom/ns#' term='NUnit'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>MbUnit vs. NUnit, the cage match</title><content type='html'>&lt;table width="100%"&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td align="center" width="33%"&gt;&lt;img src="http://1.bp.blogspot.com/_yClxQhTLbco/RzHzvSQAZoI/AAAAAAAAABI/6eQDCGi7sss/s320/mbunit.jpg"&gt;&lt;/td&gt;&lt;br /&gt;&lt;td align="center" width="33%"&gt;&lt;img src="http://2.bp.blogspot.com/_yClxQhTLbco/RzC8XyQAZnI/AAAAAAAAABA/RlD4AOiIt7g/s320/joey-crawford-t-240.jpg"&gt;&lt;/td&gt;&lt;br /&gt;&lt;td align="center" width="33%"&gt;&lt;img src="http://nunit.org/img/logo.gif"&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;I've happily used &lt;a href="http://nunit.org/"&gt;NUnit &lt;/a&gt;for 4 years now, and never questioned the design. As you may know, NUnit diverges from the &lt;a href="http://www.xprogramming.com/software.htm"&gt;xUnit&lt;/a&gt; model by using metadata rather than inheritance to indicate test fixtures, their setup, and their tests: "With NUnit 2.0, we introduced the use of attributes to identify both fixtures and test cases. Use of attributes in this way was a natural outcome of their presence in .NET and gave us a way of identifying tests that was completely independent of both inheritance and naming conventions" (see &lt;a href="file:///E:/Program%20Files/NUnit%202.4.4/doc/nunitAddins.html#testDecorators"&gt;here&lt;/a&gt;).  There are times, however, when this new model doesn't quite serve. I have quite a few test fixtures that, as part of their setup, need to perform a similar set of operations: say, read a file, create some sort of data structures, run an expensive Solve() method, etc. I can do this by deriving my fixture classes from an abstract base class that spells out a "template pattern," namely the methods or properties that the real fixture classes must implement, which the base class, which actually has a public method with the &lt;span style="font-family:courier new;"&gt;[TestFixtureSetUp]&lt;/span&gt; or &lt;span style="font-family:courier new;"&gt;[SetUp]&lt;/span&gt;, which in turn encapsulates the pattern. In this case, the derived class would implement &lt;span style="font-family:courier new;"&gt;public string ModelPath { get { return @"C:\Documents and Settings\...\model.xml" } }&lt;/span&gt;, and the abstract class's fixture set up would call &lt;span style="font-family:courier new;"&gt;this.Load(this.ModelPath)&lt;/span&gt;. You get the idea. Anyway, I'm not totally happy with this. I don't like the coupling within a class hierarchy that the template pattern entails, I end up with too many, only slightly different &lt;span style="font-style: italic;"&gt;ad hoc&lt;/span&gt; abstract base classes that no one else understands, and there are still things I can't do this way.&lt;br /&gt;&lt;br /&gt;Now, I have to admit that I haven't yet explored NUnit's &lt;a href="http://nunit.org/index.php?p=extensibility&amp;amp;r=2.4.3"&gt;extensibility&lt;/a&gt;. My current task is to write a rather complex XSLT involving three different &lt;a href="http://www.jenitennison.com/xslt/grouping/muenchian.html"&gt;Muenchian groupings&lt;/a&gt;, and I really wanted to be able to do it incrementally, in TDD fashion. I do sort of like stepping through the transform in Visual Studio's debugger, as that's taught me a lot about XML, but I need repeatable tests. &lt;a href="http://xsltunit.org/"&gt;XsltUnit &lt;/a&gt;hasn't been updated in four years, nor has &lt;a href="http://xmlunit.sourceforge.net/"&gt;XmlUnit&lt;/a&gt;. The former might be suitable for a real XMLer, but I'm not one, and I don't want to displace my difficulties onto a tool. XmlUnit doesn't handle namespaces, anyway, whereas I can't avoid them. Poking around further, I stumbled on MbUnit,which does include XML diffing. That turned out not to be what I wanted, but I did learn some interesting things. MbUnit still provides the expected attributes, including the ever-handy &lt;span style="font-family:courier new;"&gt;[ExpectedException(...)]&lt;/span&gt;, but the test pattern is really described in terms of attributes. A test runner finds a test fixture's attributes, and has the appropriate invoker create the tests. This allows test methods to accept parameters, for example; the invoker simply uses reflection to call the method with an argument described by method's attributes.  You can find more information at &lt;a href="http://blog.dotnetwiki.org/UnderstandingMbUnitArchitecture.aspx"&gt;Peli's  Farm&lt;/a&gt;, and at the &lt;a href="http://www.codeproject.com/gen/design/autp5.asp"&gt;Advanced Unit Test&lt;/a&gt; project, but I wouldn't say that the documentation is stellar. Anyway, in order to begin to be more of a radical, and less of an early adopter, I downloaded the source code, went back and forth between it and my requirements, trying to figure out how to implement them.&lt;br /&gt;&lt;br /&gt;So this is what I ended up with:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;using System.Xml.XPath;&lt;br /&gt;using MbUnit.Framework;&lt;br /&gt;using Omega.NetworkModel.Tests;&lt;br /&gt;&lt;br /&gt;namespace Omega.NetworkModel.Tests&lt;br /&gt;{&lt;br /&gt;/* A fixture factory should load this document:&lt;br /&gt;&lt;br /&gt; &amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt; &amp;lt;Root&amp;gt;&lt;br /&gt;   &amp;lt;Element Integer="1"&amp;gt;&amp;lt;/Element&amp;gt;&lt;br /&gt; &amp;lt;/Root&amp;gt;&lt;br /&gt;&lt;br /&gt; */&lt;br /&gt;[MyXmlTestFixture("document.xml")]&lt;br /&gt;public class TestXmlWithoutTransformFixture&lt;br /&gt;{&lt;br /&gt; // A test invoker should somehow get its hands on the XPathNavigable,&lt;br /&gt; // compile this expression, and give me the strongly typed result.&lt;br /&gt; [MyXPathExpressionTest("/*")]&lt;br /&gt; public void DoSomething(XPathNodeIterator iterator)&lt;br /&gt; {&lt;br /&gt;  Assert.AreEqual(1, iterator.Count);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; // count() returns a double, it turns out. The invoker is invoking&lt;br /&gt; // this method via reflection, so if the XPath expression returns&lt;br /&gt; // something other than a double, there'll be an error.&lt;br /&gt; [MyXPathExpressionTest("count(//*)")]&lt;br /&gt; public void CountElements(double count)&lt;br /&gt; {&lt;br /&gt;  Assert.AreEqual(2, count);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; // I was surprised that the compiler does *not* expect XML-escaped stuff.&lt;br /&gt; [MyXPathExpressionTest("count(//*) &gt; 0")]&lt;br /&gt; public void TryBoolean(bool hasElements)&lt;br /&gt; {&lt;br /&gt;  Assert.IsTrue(hasElements);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// The runner should load this document, and transform it. The&lt;br /&gt;// transformed result is then the basis of the tests.&lt;br /&gt;[MyXmlTestFixture("document.xml")]&lt;br /&gt;/*&lt;br /&gt; &amp;lt;?xml version="1.0" encoding="UTF-8" ?&amp;gt;&lt;br /&gt;    &amp;lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&amp;gt;&lt;br /&gt;&lt;br /&gt;     &amp;lt;xsl:template match="node()|@*"&amp;gt;&lt;br /&gt;     &amp;lt;xsl:copy&amp;gt;&lt;br /&gt;     &amp;lt;xsl:apply-templates select="@*"/&amp;gt;&lt;br /&gt;      &amp;lt;!-- The default template simply copies the text value. --&amp;gt;&lt;br /&gt;      &amp;lt;xsl:apply-templates/&amp;gt;&lt;br /&gt;        &amp;lt;/xsl:copy&amp;gt;&lt;br /&gt;      &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;xsl:template match="/"&amp;gt;&lt;br /&gt;       &amp;lt;xsl:apply-templates select="*|@*" /&amp;gt;&lt;br /&gt;     &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;   &amp;lt;/xsl:stylesheet&amp;gt;&lt;br /&gt; */&lt;br /&gt;[MyXsltTransform("test.xslt")]&lt;br /&gt;public class TestXmlAfterIdentityTransformFixture&lt;br /&gt;{&lt;br /&gt; [MyXPathExpressionTest("/*")]&lt;br /&gt; public void DoSomething(XPathNodeIterator iterator)&lt;br /&gt; {&lt;br /&gt;  Assert.AreEqual(iterator.Current.NodeType, XPathNodeType.Root);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Transform, and then establish an evaluation context (i.e., namespace mappings!).&lt;br /&gt;[MyXmlTestFixture("document.xml")]&lt;br /&gt;[MyXsltTransform("test.xslt")]&lt;br /&gt;[MyXsltNamespaceMappingDecorator("tnt", "")]&lt;br /&gt;public class TestXmlAfterIdentityTransformAndNamespaceFixture&lt;br /&gt;{&lt;br /&gt; [MyXPathExpressionTest("/tnt:*")]&lt;br /&gt; public void DoSomething(XPathNodeIterator iterator)&lt;br /&gt; {&lt;br /&gt;  Assert.AreEqual(iterator.Current.NodeType, XPathNodeType.Root);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-3583711806857733478?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/3583711806857733478/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=3583711806857733478' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3583711806857733478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3583711806857733478'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/11/mbunit-vs-nunit-cage-match.html' title='MbUnit vs. NUnit, the cage match'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_yClxQhTLbco/RzHzvSQAZoI/AAAAAAAAABI/6eQDCGi7sss/s72-c/mbunit.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-8645128299670127202</id><published>2007-11-01T09:13:00.000-04:00</published><updated>2007-11-01T10:09:04.878-04:00</updated><title type='text'>Open Spaces &amp; Productivity</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_yClxQhTLbco/RyndbiQAZmI/AAAAAAAAAA4/Ohx6QqMQIH0/s1600-h/ForGoogle.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_yClxQhTLbco/RyndbiQAZmI/AAAAAAAAAA4/Ohx6QqMQIH0/s320/ForGoogle.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5127873115858167394" /&gt;&lt;/a&gt;&lt;br /&gt;I write from a position of utter ignorance here, as for the most of the last 5 years I've had quite unpleasant working situations. Nonetheless, I've often been very productive in my windowless basements, sometimes because of their unpleasantness: I had to work harder in order to eliminate them from my awareness. I wasn't always allowed an MP3 player and headphones, either, so it's not like I had any sort of private space. I worked for 15 months at the desk pictured at the right, and I was extremely productive. That doesn't mean that I didn't also hate it. The effort that I had to exert in order to shut out distractions eventually wore me out. Be all that as it may, I really just want to admit that I have no proper basis of comparison to the agile image of an open space of unimpeded communication. I'm not sure I want to share a keyboard with anyone for most of the day, but maybe my irritation will diminish now that I've moved out of the aforementioned dungeon.&lt;br /&gt;&lt;br /&gt;At any rate, it is possible to study these things, and Michael Brill does just that. Brill is president of BOSTI Associates, "workplace planning and design consultant in Buffalo, N.Y., and founder of the School of Architecture at the University of Buffalo" (&lt;a href="http://www.shrm.org/hrmagazine/articles/0902/0902covstory_offices.asp"&gt;click here&lt;/a&gt;). This article contrasts his viewpoint to that of Franklin Becker, director of the International Workplace Studies Program at Cornell University. I don't have time to summarize right now (I just finished my coffee, so my "knife-sharpening" 20 minutes are over), but they both agree that cubicles are bad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-8645128299670127202?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/8645128299670127202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=8645128299670127202' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8645128299670127202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8645128299670127202'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/11/open-spaces-productivity.html' title='Open Spaces &amp; Productivity'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_yClxQhTLbco/RyndbiQAZmI/AAAAAAAAAA4/Ohx6QqMQIH0/s72-c/ForGoogle.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-3462031062749036440</id><published>2007-10-26T12:49:00.000-04:00</published><updated>2007-10-26T13:04:30.496-04:00</updated><title type='text'>I'm a Libertarian Now!</title><content type='html'>It was probably inevitable that I would become a libertarian. When I was in graduate school, I was a Gramscian Marxist and a Freudian, as was only proper. Now I write software. Now I'm linking to George Mason University economics professor Tyler Cowen's &lt;a href="http://www.marginalrevolution.com/"&gt;Marginal Revolution&lt;/a&gt; blog. Nice that the guy also writes about art: &lt;a href="http://www.amazon.com/exec/obidos/ASIN/047206889X/marginalrevol-20"&gt;Markets and Cultural Voices: Liberty vs. Power in the Lives of Mexican Amate Painters&lt;/a&gt;. To be honest, most of the libertarians I knew before I got back into software were angry middle-aged engineers, and it was hard not to interpret their convictions as of a piece with their middle-aged guy anger.&lt;br /&gt;&lt;br /&gt;Now that I'm rockin' the GMU CS department, maybe I'll meet Cowan.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-3462031062749036440?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/3462031062749036440/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=3462031062749036440' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3462031062749036440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3462031062749036440'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/10/im-libertarian-now.html' title='I&apos;m a Libertarian Now!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-3369325598960357172</id><published>2007-10-21T09:56:00.000-04:00</published><updated>2007-10-21T11:52:08.614-04:00</updated><title type='text'>Greeted as Liberators</title><content type='html'>&lt;a href="http://www.amazon.com/Prince-Marshes-Other-Occupational-Hazards/dp/0156032791/ref=pd_bbs_sr_2/103-9963688-6584618?ie=UTF8&amp;s=books&amp;qid=1192979324&amp;sr=8-2"&gt;Rory Stewart&lt;/a&gt;, who served as governor of Maysan province under the CPA, reviews several biographies of one of his forebears, Gertrude Bell: &lt;a href="http://www.nybooks.com/articles/20691"&gt;"The Queen of the Quagmire"&lt;/a&gt;. Both the review and his book &lt;i&gt;The Prince of the Marshes&lt;/i&gt; show a kind of modesty that might be impossible for any American writing about our occupation of Iraq. For Americans, it seems to be all about us. This flaw doesn't invalidate the writing of George Packer, for example, whose &lt;a href="http://www.newyorker.com/reporting/2007/03/26/070326fa_fact_packer"&gt;essay&lt;/a&gt; on our contemptible neglect of Iraqis who've chosen to help the occupiers by serving as translators I highly recommend. Yet Packer was a liberal hawk only a few years ago, and it's very difficult for him, writing now, not to attribute the obvious failure of our occupation (and of his arguments at the time) to the people carrying it out, rather than to a more fundamental cause. In other words, if the CPA had not put the obtuse Paul Bremer in charge, it might have worked.&lt;br /&gt;&lt;br /&gt;Gertrude Bell is one of the architects of modern Iraq, if that's something she'd really want to claim credit for at this point. In contrast to the liberal hawk's regret that we didn't put the smartest people in charge, not to mention Edward Said's imputation of a fundamental bad faith, Stewart credits Bell and her colleagues with quite impressive knowledge of Arabic, of tribal power structures, of how to ride a camel, etc.: "Some suggest that the US failure in Iraq is due simply to lack of planning...They should consider Bell and her colleagues, such as Colonel Leachman or Bertram Thomas, a political officer on the Euphrates. All three were fluent and highly experienced Arabists, won medals from the Royal Geographical Society for their Arabian journeys, and were greatly admired for their political work. Thomas was driven from his office in Shatra by a tribal mob. Colonel Leachman, who was famed for being able to kill a tribesman dead in his own tent without a hand lifted against him, was shot in the back in Fallujah." Bell herself has to take the blame for agreeing to tack the Kurdish areas onto this new entity, at subsequent great cost to the Kurds.&lt;br /&gt;&lt;br /&gt;It's too easy to hate the smirking fatuity of Donald Rumsfeld. But it's entirely beside the point. There is no objective measure of the depth of our understanding of Iraq. Who's to say that their self-understanding is any better than our understanding of them? The obstacle to building a new Iraq for the Iraqis is not that we don't understand them. The obstacle is that they're just not that into us. That's what it's like to be an occupier.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-3369325598960357172?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/3369325598960357172/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=3369325598960357172' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3369325598960357172'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3369325598960357172'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/10/greeted-as-liberators.html' title='Greeted as Liberators'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-5610871287919841954</id><published>2007-10-13T13:27:00.000-04:00</published><updated>2007-10-13T13:39:00.917-04:00</updated><title type='text'>Photo of cute toddler!</title><content type='html'>&lt;a href="http://1.bp.blogspot.com/_yClxQhTLbco/RxEAVSd022I/AAAAAAAAAAc/0jrzTjiRCvU/s1600-h/Picture+002.jpg"&gt;&lt;img style="float:left; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_yClxQhTLbco/RxEAVSd022I/AAAAAAAAAAc/0jrzTjiRCvU/s320/Picture+002.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5120874617031154530" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-5610871287919841954?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/5610871287919841954/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=5610871287919841954' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5610871287919841954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/5610871287919841954'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/10/photo-of-cute-toddler.html' title='Photo of cute toddler!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_yClxQhTLbco/RxEAVSd022I/AAAAAAAAAAc/0jrzTjiRCvU/s72-c/Picture+002.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-1809304530108205597</id><published>2007-10-12T19:32:00.000-04:00</published><updated>2007-10-12T21:18:38.101-04:00</updated><title type='text'>There's this music called flamenco...</title><content type='html'>...and on a whim I recently purchased a 12-volume set called &lt;span style="font-style:italic;"&gt;The History of Flamenco&lt;/span&gt;. I had just read Fernanda Eberstadt's &lt;span style="font-style:italic;"&gt;Little Money Street&lt;/span&gt;, and I felt I had to hear some. Or a lot. I've always loved the blues, and flamenco is like it in some ways: it's the music of a marginal group, much of its history is mysterious, purity of tone means little while emotional force means everything (in a wonderful phrase, a great flamenco singer is said to have "the voice that wounds"), and there's a violence in it that doesn't always seem metaphorical. On the other hand, flamenco has a much larger range of forms, and however much I may love Charlie Patton, I can't think of a blues guitar virtuoso who comes close to Sabicas. Anyway, I was just surfing for some information on La Paquera, one of whose tracks is included in the above anthology, and stumbled across &lt;a href="http://www.youtube.com/watch?v=5RCXyEjznjY"&gt;this video clip&lt;/a&gt; of the same woman, now perhaps an &lt;span style="font-style:italic;"&gt;abuela&lt;/span&gt;, attacking "Dolor de madre mia" with even more force than she had when she was young. I have no idea if this song is actually sad, although the title would suggest that. I was at work, but the hair on my arm stood up on hearing this, not just because of the emotion, which I can't identify, but because of the indomitable force of the music that bursts out of these old folks. Hunt around for some more clips from Carlos Saura's flamenco films. YouTube also has a lot of odd clips from old movies, in which some &lt;a href="http://www.youtube.com/watch?v=JEoUyjbyDJY&amp;NR=1"&gt;cantante in a suit strolls onto the set and lets loose&lt;/a&gt; (it doesn't hurt to have a young Paco de Lucia on guitar). For me it's like seeing Howlin' Wolf walk into a Doris Day movie and sing "Commit a Crime."&lt;br /&gt;&lt;br /&gt;When I saw the first clip, I thought, "I could listen to this all day." Since then, a month ago, I have been. Here, I've done some of the work for you: &lt;a href="http://www.youtube.com/watch?v=fjGtghxtgjk"&gt;Paco de Lucia with El Camaron de la Isla&lt;/a&gt;, &lt;a href="http://www.youtube.com/watch?v=XusAgsVUbgI"&gt;Juanito Valderrama wandering into some weird dance routine&lt;/a&gt;, &lt;a href="http://www.youtube.com/watch?v=LnBdxy2kv6I"&gt;some younger singers and the great Tomatito tearing it up&lt;/a&gt; (also from Saura's film), and finally &lt;a href="http://www.youtube.com/watch?v=7-XUA9CSsk8"&gt;these two old men&lt;/a&gt; singing the slowest form, the &lt;span style="font-style:italic;"&gt;martinete&lt;/span&gt;. Their intonation isn't perfect. Listen, Maria Callas's intonation wasn't perfect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-1809304530108205597?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/1809304530108205597/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=1809304530108205597' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1809304530108205597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1809304530108205597'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/10/theres-this-music-called-flamenco.html' title='There&apos;s this music called flamenco...'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-4067403246909506425</id><published>2007-10-12T19:07:00.000-04:00</published><updated>2007-10-13T13:38:22.891-04:00</updated><title type='text'>Ancient Technology</title><content type='html'>I'm usually skeptical when I watch some History Channel cheapie about ancient trans-Atlantic journeys or whatever, but the evidence in this case is incontrovertible: &lt;a href="http://www.antikythera-mechanism.gr/"&gt;the ancient Greeks knew how to build clocks&lt;/a&gt;, with gears and all. Only two examples exist, but they're too sophisticated to have been the only ones of their kind. How could this knowledge have been lost? &lt;a href="http://www.newyorker.com/reporting/2007/05/14/070514fa_fact_seabrook?printable=true"&gt;John Seabrook&lt;/a&gt; suggests a number of reasons: not many people would have possessed such knowledge, and if social upheavals separated them from the opportunity to use the knowledge, the clocks would have ended up as scrap, and melted down; both Greek and Roman culture respected individual bravery too much to commit itself to technologizing warfare; technology may have provided delight rather than profit. Moreover, the knowledge almost surely ended up with the Arabs, whence it returned to Europe in the 14th century, when clocks with gears suddenly (re)appear. It's easy to suspect that the West refuses to admit just how advanced Muslim civilization was, but something more peculiar is going on here: even classicists showed little interest in the Antikythera Mechanism until recently. It's as though we think that only &lt;span style="font-style:italic;"&gt;we&lt;/span&gt; are capable of technology per se. The Greeks had some impressive mechanisms, but didn't put them to uses we respect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-4067403246909506425?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/4067403246909506425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=4067403246909506425' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/4067403246909506425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/4067403246909506425'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/10/ancient-technology.html' title='Ancient Technology'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-8696313950203221211</id><published>2007-10-12T18:15:00.000-04:00</published><updated>2007-10-12T18:29:56.141-04:00</updated><title type='text'>Country music is actually pretty good!</title><content type='html'>&lt;div&gt;&lt;span class="328494719-11062007"&gt;Merle Haggard, at &lt;a title="http://archive.salon.com/people/bc/2000/11/14/haggard/index2.html" href="http://archive.salon.com/people/bc/2000/11/14/haggard/index2.html"&gt;http://archive.salon.com/people/bc/2000/11/14/haggard/index2.html&lt;/a&gt;: "Look at the past 25  years -- we went downhill, and if people don't realize it, they don't have their  fucking eyes on," says Haggard. "In 1960, when I came out of prison as an  ex-convict, I had more freedom under parolee supervision than there's available  to an average citizen in America right now. I mean, there was nobody going to  throw you down on the side of the road spread-eagled, and look up your butt for  a fucking marijuana cigarette. God almighty, what have we done to each  other?"&lt;/span&gt;&lt;/div&gt; &lt;blockquote dir="ltr" style="margin-right: 0px;"&gt; &lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-8696313950203221211?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/8696313950203221211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=8696313950203221211' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8696313950203221211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/8696313950203221211'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/10/country-music-is-actually-pretty-good.html' title='Country music is actually pretty good!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-3303559640325887287</id><published>2007-10-10T08:12:00.000-04:00</published><updated>2007-10-12T09:47:44.817-04:00</updated><title type='text'>I give up!</title><content type='html'>I started this blog about a year ago because I thought it was necessary to my self-promotion. In addition to going to work every day, and doing a good job, and attending conferences, and reading books about CS generally, whatever language I'm using (XSLT, F#, whatever), whatever platform I'm on (.NET for most of the last 4 years), etc., I need to blog about the cool thing I'm doing, and get some C# MVP finally to add me to his blogroll. Real life got in the way, though, as it will, esp. in the form of a new baby. I've had just enough time to do what needed to be done, and none left over to describe to the recruiters at Google who, one imagines, are reading all these blogs, to explain how well I did it. So I'm going to start using this blog the way most people do, i.e. to quote big chunks of stuff I read elsewhere, give shout-outs to my friends, post cute toddler photos, etc.&lt;br /&gt;&lt;br /&gt;Anyway, the big chunk that I want to regurgitate undigested is from &lt;span style="font-style: italic;"&gt;The New Yorker&lt;/span&gt;, May 14, 2007. James Surowiecki, whose &lt;span style="font-style: italic;"&gt;The Wisdom of Crowds&lt;/span&gt; might have appealed to the same folks I see reading &lt;span style="font-style: italic;"&gt;Radicals for Capitalism&lt;/span&gt; on the bus, argues that it might be better for the whole if a certain part, namely companies who own patents, makes less money. I suppose that's an instance of the radicalism of capitalism; emergent capitalist economies might require a certain amount of theft, just as ours did. I'm not an ideologue of open-source software; I actually don't care about Linux one way or another, just as I don't care about Microsoft. Anyway, "History suggests that after a certain point tougher intellectual property rules yield diminishing returns. Josh Lerner, a professor at Harvard Business School, looked at a hundred and fifty years of patenting, and found that strengthening patent laws had little effect on the number of innovations within a country. And, in the US, stronger patent protections for things like software have had little or no effect on the amount of innovation in the field. The benefits of stronger IP protection are even less convincing when it comes to copyright law: there's little evidence that writers and artists are made more productive or creative by the prospect of earning profits for seventy years after they die, and the historical record suggests only a tenuous connection between stronger IP laws and creative output."&lt;br /&gt;&lt;br /&gt;One would expect that at some point China will have to have strong laws to prevent Chinese entrepreneurs from stealing from each other. You're certainly not going to read some ponderous clash-of-civilizations crap on my blog. Maybe China is culturally incapable of respecting the rule of law, in the way that's bred into the Anglo-Saxon bone. Maybe not. "The great irony is that the US economy in its early years was built in large part on a lax attitude toward intellectual property rights and enforcement [ditto for the Dutch and English economies in the two centuries before that]. As the historian Doron Ben-Atar shows in his book &lt;span style="font-style: italic;"&gt;Trade Secrets&lt;/span&gt;, the Founders believed that a strict attitude toward patents and copyright would limit domestic innovation and make it harder for the US to expand its industrial base. American law did not protect the rights of foreign investors or writers, and Secretary of the Treasury Alexander Hamilton, in his famous &lt;span style="font-style: italic;"&gt;Report on the Manufactures&lt;/span&gt;, of 1791, actively advocated the theft of technology and the luring of skilled workers from foreign countries. Among the beneficiaries...was the American textile industry,which flourished thanks to pirated technology."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-3303559640325887287?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/3303559640325887287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=3303559640325887287' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3303559640325887287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3303559640325887287'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/10/i-give-up.html' title='I give up!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-161839018475459441</id><published>2007-03-14T10:35:00.000-04:00</published><updated>2007-10-12T18:53:25.829-04:00</updated><title type='text'>I really hate Wes Anderson</title><content type='html'>From James Wolcott's &lt;a href="http://www.vanityfair.com/politics/blogs/wolcott/2007/03/you_know_its_an.html"&gt;blog&lt;/a&gt;: "When I was watching Wes Anderson's beyond-precious and unjustifiably proud-of-itself The Life Aquatic, there was a scene where a three-legged dog is whimpering on deck and Jeff Goldblum, playing the suave villain, asks Bill Murray the dog's name. Murray tells him and (off camera) we hear a loud smack as Goldblum hits the dog to stop its whimpering. I can't recall if Murray changed expression or not, since that wasn't a high priority for him here. The minor cruelty doled out to an animal for a cheap laugh was compounded when the dog was left behind at the end splashing in the water as the cast of overpaid actors vying to outhip each other departs."&lt;br /&gt;&lt;br /&gt;Wolcott puts it better than I could. Jason Schwartzmann gave &lt;span style="font-style: italic;"&gt;Rushmore&lt;/span&gt; some real passion, and the film deserved the praise it got, but my jaw clenched while watching &lt;span style="font-style: italic;"&gt;The Royal Tenenbaums&lt;span style="font-style: italic;"&gt;. &lt;/span&gt;&lt;/span&gt;Not only was I both bored and irritated, Anjelica Huston looked bored and irritated, the usually ill-used Danny Glover looked bored and irritated, Gwyneth Paltrow looked bored and irritated (though that may just be Gwyneth)...only Gene Hackman didn't seem to be saying, "Dig how adorable I am in my ineffectuality." I know that Wes Anderson has already anticipated my belligerent response to his WASP privilege (check out the &lt;a href="http://www.slate.com/id/2174828/"&gt;bowtie&lt;/a&gt;) by his irony, but that just makes me hate him more. Which, of course, lets him come out on top!&lt;br /&gt;&lt;br /&gt;Would the aimless but amusing banter of his films be possible without the equally problematic Quentin Tarantino, who similarly dares you to disdain his shallowness? I saw &lt;span style="font-style: italic;"&gt;Pulp Fiction&lt;/span&gt; on a college campus, surrounded by undergraduates (I was about 34 at the time), and I might have been the only person in the theater not to laugh when Marvin (the nigger, in case you've forgotten) gets his brains blown out! This gives Quentin the opportunity to appear in his own film and try to say the word "nigger" in good conscience, though he's so sweaty and uncomfortable doing it that ofay college students were not moved to emulate him, as far as I know. I realize that, having admitted that I was not enchanted by &lt;span style="font-style: italic;"&gt;Pulp Fiction&lt;/span&gt;, I will never considered cool, but that's what his film is for: to separate the cool from the uncool (cf. &lt;a href="http://www.prospect-magazine.co.uk/article_details.php?id=4738"&gt;this&lt;/a&gt;). What I'm getting at is that Wes Anderson is sort of the upper-class Tarantino, complex in an infuriatingly shallow if not selfish way, and deliberate in his provocation, which is intended to make you lose your cool and leave him on top.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-161839018475459441?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/161839018475459441/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=161839018475459441' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/161839018475459441'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/161839018475459441'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/03/i-really-hate-wes-anderson.html' title='I really hate Wes Anderson'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-7665413541456275094</id><published>2007-03-09T09:48:00.000-05:00</published><updated>2007-03-09T12:22:42.433-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Subversion'/><category scheme='http://www.blogger.com/atom/ns#' term='NAnt'/><title type='text'>NAnt's Filtered Trigger</title><content type='html'>I have to say that I found the documentation on this feature somewhat baffling, but that might just be me. We use Subversion here, and I'd like the build &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; to run if the only files committed since the last build are the ones that the last build created, e.g. the &lt;code&gt;version.txt&lt;/code&gt; file, the generated &lt;code&gt;CommonAssemblyInfo.cs&lt;/code&gt; into which the incremented build number is written, the backups of CruiseControl.NET's various .config and .xsl files, etc. Feel free to write me if my example doesn't help you out. The problem that cost me close to a day is that relative paths within the &lt;sourcecontrol&gt; block don't seem to be resolved as I expected. See my comments...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;cruisecontrol&amp;gt;  &lt;br /&gt;  &amp;lt;project name="Our Project 3.0"&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;workingDirectory&amp;gt;E:\Builds\Our Project 3.0&amp;lt;/workingDirectory&amp;gt;&lt;br /&gt;    &amp;lt;artifactDirectory&amp;gt;E:\Builds\Our Project 3.0\artifacts&amp;lt;/artifactDirectory&amp;gt;&lt;br /&gt;    &amp;lt;!-- &lt;br /&gt;         CruiseControl.NET doesn't handle spaces in paths consistently, so let's&lt;br /&gt;         keep ourselves out of trouble. &lt;br /&gt;    --&amp;gt;&lt;br /&gt;    &amp;lt;webURL&amp;gt;http://localhost/ccnet/OurProject3.0&amp;lt;/webURL&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;!-- Check every 10 minutes. --&amp;gt;&lt;br /&gt;    &amp;lt;triggers&amp;gt;&lt;br /&gt;        &amp;lt;intervalTrigger seconds="600" /&amp;gt;&lt;br /&gt;    &amp;lt;/triggers&amp;gt;&lt;br /&gt;    &lt;br /&gt;    &amp;lt;sourcecontrol type="filtered"&amp;gt;&lt;br /&gt;      &amp;lt;sourceControlProvider type="svn"&amp;gt;&lt;br /&gt;        &amp;lt;trunkUrl&amp;gt;svn://aMachine/omega/trunk/OurProject&amp;lt;/trunkUrl&amp;gt;&lt;br /&gt;        &amp;lt;workingDirectory&amp;gt;E:\Builds\Our Project 3.0&amp;lt;/workingDirectory&amp;gt;&lt;br /&gt;        &amp;lt;username&amp;gt;tnassar&amp;lt;/username&amp;gt;&lt;br /&gt;        &amp;lt;password&amp;gt;&amp;lt;/password&amp;gt;&lt;br /&gt;      &amp;lt;/sourceControlProvider&amp;gt;&lt;br /&gt;    &amp;lt;inclusionFilters&amp;gt;&lt;br /&gt;      &amp;lt;pathFilter&amp;gt;&lt;br /&gt;        &amp;lt;!-- Note that this is the full pathname, and apparently has to be. --&amp;gt;&lt;br /&gt;       &amp;lt;pattern&amp;gt;/trunk/Our Project/**/*.*&amp;lt;/pattern&amp;gt;&lt;br /&gt;      &amp;lt;/pathFilter&amp;gt;&lt;br /&gt;    &amp;lt;/inclusionFilters&amp;gt;&lt;br /&gt;    &amp;lt;exclusionFilters&amp;gt;&lt;br /&gt;      &amp;lt;!-- Changes to these files should *not* cause a new build. --&amp;gt;&lt;br /&gt;      &amp;lt;pathFilter&amp;gt;&lt;br /&gt;        &amp;lt;pattern&amp;gt;/trunk/Our Project/version.txt&amp;lt;/pattern&amp;gt;&lt;br /&gt;      &amp;lt;/pathFilter&amp;gt;&lt;br /&gt;      &amp;lt;pathFilter&amp;gt;&lt;br /&gt;        &amp;lt;!-- These are serialization assemblies generated during the build process,&lt;br /&gt;             using xsd.exe.&lt;br /&gt;        --&amp;gt;&lt;br /&gt;        &amp;lt;pattern&amp;gt;/trunk/Our Project/Serialization/Bin/*.dll&amp;lt;/pattern&amp;gt;&lt;br /&gt;      &amp;lt;/pathFilter&amp;gt;&lt;br /&gt;      &amp;lt;pathFilter&amp;gt;&lt;br /&gt;        &amp;lt;!-- The build number is boosted by a NAnt task. --&amp;gt;&lt;br /&gt;     &amp;lt;pattern&amp;gt;/trunk/Omega LP/Applications/CommonAssemblyInfo.cs&amp;lt;/pattern&amp;gt;&lt;br /&gt;      &amp;lt;/pathFilter&amp;gt;&lt;br /&gt;      &amp;lt;pathFilter&amp;gt;&lt;br /&gt;        &amp;lt;pattern&amp;gt;/trunk/Our Project/Build/**/*&amp;lt;/pattern&amp;gt;&lt;br /&gt;      &amp;lt;/pathFilter&amp;gt;&lt;br /&gt;    &amp;lt;/exclusionFilters&amp;gt;&lt;br /&gt;    &amp;lt;/sourcecontrol&amp;gt;&lt;br /&gt;    &amp;lt;tasks&amp;gt;&lt;br /&gt;       &amp;lt;!-- And so on... --&amp;gt;&lt;br /&gt;    &amp;lt;/tasks&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-7665413541456275094?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/7665413541456275094/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=7665413541456275094' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/7665413541456275094'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/7665413541456275094'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/03/nants-filtered-trigger.html' title='NAnt&apos;s Filtered Trigger'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-3988877858288281640</id><published>2007-02-05T17:08:00.000-05:00</published><updated>2007-02-05T17:13:09.935-05:00</updated><title type='text'>Generating LOC from NAnt, Part D'oh!</title><content type='html'>At some point I'd like to make all the code available, and perhaps even contribute it NAntContrib, but at the moment it's really butt-ugly. I'm glad that C# 2.0 has better support for sort-of-functional programming constructs, but this is unpleasantly verbose:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public static TreeSorter Sort(string[] paths)&lt;br /&gt;{&lt;br /&gt; if (paths.Length == 0)&lt;br /&gt;  return new TreeSorter("");&lt;br /&gt; List&amp;lt;string&amp;gt; pathsList = new List&amp;lt;string&amp;gt;(paths);&lt;br /&gt; pathsList.Sort();&lt;br /&gt; List&amp;lt;List&amp;lt;string&amp;gt;&amp;gt; splitPaths = pathsList.ConvertAll&amp;lt;List&amp;lt;string&amp;gt;&amp;gt;(delegate(string path)&lt;br /&gt;                                                                     {&lt;br /&gt;                                                                      return&lt;br /&gt;                                                                       new List&amp;lt;string&amp;gt;(&lt;br /&gt;                                                                        path.Split(&lt;br /&gt;                                                                         System.IO.Path.DirectorySeparatorChar));&lt;br /&gt;                                                                     });&lt;br /&gt;&lt;br /&gt; int index = 0;&lt;br /&gt; for (; index &amp;lt; splitPaths[0].Count; ++index)&lt;br /&gt; {&lt;br /&gt;  if (!splitPaths.TrueForAll(delegate(List&amp;lt;string&amp;gt; input)&lt;br /&gt;                              {&lt;br /&gt;                               return index &amp;lt; input.Count &amp;&amp;amp; input[index] == splitPaths[0][index];&lt;br /&gt;                              }))&lt;br /&gt;   break;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; IEnumerable&amp;lt;string&amp;gt; commonPath = splitPaths[0].GetRange(0, index);&lt;br /&gt; IList&amp;lt;IEnumerable&amp;lt;string&amp;gt;&amp;gt; truncatedPaths = splitPaths.ConvertAll&amp;lt;IEnumerable&amp;lt;string&amp;gt;&amp;gt;(delegate(List&amp;lt;string&amp;gt; input)&lt;br /&gt;                                                                                         {&lt;br /&gt;                                                                                          return&lt;br /&gt;                                                                                           input.GetRange(index,&lt;br /&gt;                                                                                                          input.Count -&lt;br /&gt;                                                                                                          index);&lt;br /&gt;                                                                                         });&lt;br /&gt; return new TreeSorter(commonPath, truncatedPaths);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-3988877858288281640?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/3988877858288281640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=3988877858288281640' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3988877858288281640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/3988877858288281640'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/02/generating-loc-from-nant_05.html' title='Generating LOC from NAnt, Part D&apos;oh!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-2299367157404176323</id><published>2007-02-03T11:04:00.000-05:00</published><updated>2007-02-03T14:31:41.697-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NAntContrib'/><category scheme='http://www.blogger.com/atom/ns#' term='NAnt'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>Generating LOC from NAnt, Part 1</title><content type='html'>Last March I my team took over a failing project for our most important client. The domain to which the software applied is very interesting, and very difficult, and I can't deny that the previous contractor had employed some smart people...at &lt;span style="font-style: italic;"&gt;some&lt;/span&gt; point in the project's history! The code was a mess. Simply passing FxCop over it revealed that about 5% of it was dead code. After a few months of working with it, we realized that another significant part of it would only be called if certain events fired, and they never did. This is often not so easy to prove, unfortunately. Since I use ReSharper, I found it easier simply to delete unused methods, but the overuse of dynamic binding (!) in the code base meant that I could not be sure, without regression tests (which of course didn't exist until I wrote them) that certain classes were never instantiated. And so on.&lt;br /&gt;&lt;br /&gt;Anyway, we began by simply counting the lines of text in the entire code base. This measure decreased daily, and the project manager finally suggested that we do this more systematically so that the client could cite these figures when trying to justify his additional expenditures on this already "working" code to his superiors. I want to make clear that we are &lt;span style="font-style: italic;"&gt;not &lt;/span&gt;somehow charging for KLOC. Notwithstanding that we're doing this work for a gov't agency, we're not working under some burdensome methodology. The distinction between physical and logical LOC is not so important to us. We are supposed to be making the code more maintainable; a steady decrease in the &lt;span style="font-style: italic;"&gt;relative &lt;/span&gt;LOC counts as success. I imposed the requirement on myself that the LOC metric be generated from CruiseControl.NET via NAnt. None of the tools that I could use in this way were entirely satisfactory, however. &lt;a href="http://www.ndepend.com/"&gt;NDepend &lt;/a&gt;measured the LOC in files that didn't interest me (e.g., MyForm.Designer.cs); &lt;a href="http://www.geronesoft.com/"&gt;GeroneSoft&lt;/a&gt;'s Code Counter likewise. Both utilites allow me to create include and exclude lists, but none of them as neatly as NAnt! I would really like to be able to edit the .build file, quickly create another &lt;span style="font-family:courier new;"&gt;&amp;lt;fileset&amp;gt;&lt;/span&gt;, say for ".cs files generated by the Windows Forms Designer," measure the LOC in that set, and arrange for those results to be published by CruiseControl.NET.&lt;br /&gt;&lt;br /&gt;NAntContrib does supply a &lt;a style="font-family: courier new;" href="http://nantcontrib.sourceforge.net/release/latest/help/tasks/codestats.html"&gt;&amp;lt;codestats&amp;gt;&lt;/a&gt; task, which accepts a fileset as input. Then, unfortunately, it outputs the statistics as a flat list, which is &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; what I wanted. What I need is to turn that list into a tree again. This turns out to be unspeakably difficult without XSLT 2.0. Don't recommend Muenchian groupings, please! My initial thought that it would be &lt;span style="font-style: italic;"&gt;neat&lt;/span&gt; to write this transform cost me at least a day. Then I set about writing a custom NAnt task in C#, but once again I was sidetracked by C#'s relatively poor support for list comprehensions. Moreover, one is discouraged by "performance considerations" from repeatedly splitting and rejoining strings, slicing lists, etc. That's what happens to you when you write too much C, as opposed to learning LISP at a good school. After about two wasted days, on the bus going home, I pulled out a pad of paper and wrote out some Python. I tried it out when I got home, and it worked right out of the box. Then, I admit with some embarrassment, I retrofitted the unit tests, and implemented the special functions such as &lt;code&gt;__len__()&lt;/code&gt;, for the sake of the tests.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;import os&lt;br /&gt;import itertools&lt;br /&gt;import unittest&lt;br /&gt;&lt;br /&gt;class Node:&lt;br /&gt; def __init__(self, path = [], children = []):&lt;br /&gt;  assert isinstance(path, list)&lt;br /&gt;  assert isinstance(children, list)&lt;br /&gt;  self.path = path&lt;br /&gt;  self.children = {}&lt;br /&gt;  for child in children:&lt;br /&gt;   self.add(child)&lt;br /&gt;  &lt;br /&gt; def __len__(self):&lt;br /&gt;  return 1 + sum([len(child) for child in self.children.values()])&lt;br /&gt;&lt;br /&gt; def __getitem__(self, key):&lt;br /&gt;  return self.children.__getitem__(key)&lt;br /&gt;&lt;br /&gt; def __setitem__(self, key):&lt;br /&gt;  return self.children.__setitem__(key)&lt;br /&gt; &lt;br /&gt; def __iter__(self):&lt;br /&gt;  yield self.path&lt;br /&gt;  for child in self.children.values():&lt;br /&gt;   for i in child:&lt;br /&gt;    yield self.path + i             &lt;br /&gt;  &lt;br /&gt; def __repr__(self):&lt;br /&gt;  return os.sep.join(self.path)&lt;br /&gt;&lt;br /&gt; def add(self, path):&lt;br /&gt;  assert len(path), "Cannot add an empty path."&lt;br /&gt;  if len(path) == 1:&lt;br /&gt;   self.children[path[0]] = Node(path)&lt;br /&gt;  elif self.children.has_key(path[0]):&lt;br /&gt;   self.children[path[0]].add(path[1:])&lt;br /&gt;  else:&lt;br /&gt;   self.children[path[0]] = Node([path[0]], [path[1:]])&lt;br /&gt; &lt;br /&gt;def allsame(seq):&lt;br /&gt; # An empty sequence is true because is has no mismatches. &lt;br /&gt; # A 1-length sequence should always be true, i.e., imap(...) will&lt;br /&gt; # return an empty list, of which False is not a member (duh!).&lt;br /&gt; # Otherwise, compare all elements to the first. The iteration &lt;br /&gt; # over the sequence should stop as soon as False becomes&lt;br /&gt; # a member of the output sequence.&lt;br /&gt; return not seq or False not in itertools.imap(lambda x: x == seq[0], itertools.islice(seq, 1, None))&lt;br /&gt; &lt;br /&gt;def createNodeSorter(paths):&lt;br /&gt; assert paths, "Cannot create a base node for 0 paths"&lt;br /&gt; # Split the paths along the directory separator character.&lt;br /&gt; splitFiles = [f.split(os.sep) for f in paths]&lt;br /&gt; # Now "pivot" these lists in order to find out if they share a common&lt;br /&gt; # base path. Note that os.commonprefix() is character-based; it is &lt;br /&gt; # not suitable.&lt;br /&gt; zipped = zip(*splitFiles)&lt;br /&gt; # How many sequences consist of identical elements?&lt;br /&gt; # Concatenate those, and you've got the common base path.&lt;br /&gt; common = [f[0] for f in itertools.takewhile(allsame, zipped)]&lt;br /&gt; print "Common base path: " + str(common)&lt;br /&gt; # Now strip the common base path.&lt;br /&gt; splitFiles = [f[len(common):] for f in splitFiles if f != common]&lt;br /&gt; print "\tchildren: " + str(splitFiles)&lt;br /&gt; return Node(common, splitFiles)&lt;br /&gt; &lt;br /&gt;class AllSameTest(unittest.TestCase):&lt;br /&gt; def testEmptySequence(self):&lt;br /&gt;  self.assertTrue(allsame([]))&lt;br /&gt;  &lt;br /&gt; def testSingleElement(self):&lt;br /&gt;  self.assertTrue(allsame([1]))&lt;br /&gt;  self.assertTrue(allsame(['test']))&lt;br /&gt;  &lt;br /&gt; def testTwoIntegers(self):&lt;br /&gt;  self.assertTrue(allsame([1, 1]))&lt;br /&gt;  self.assertFalse(allsame([0, 1]))&lt;br /&gt;  &lt;br /&gt; def testTwoStrings(self):&lt;br /&gt;  self.assertTrue(allsame(['test', 'test']))&lt;br /&gt;  self.assertFalse(allsame(['test', 'fail']))&lt;br /&gt;  &lt;br /&gt;class CreatorTest(unittest.TestCase):&lt;br /&gt; def testEmptyFileSet(self):&lt;br /&gt;  self.assertRaises(AssertionError, lambda: createNodeSorter([]))&lt;br /&gt;  &lt;br /&gt; def testSinglePath(self):&lt;br /&gt;  n = createNodeSorter([r'D:\trunk\Projects\Build\NAnt\LineCounter\LineCount.cs'])&lt;br /&gt;  self.assertEqual(1, len(n))&lt;br /&gt;  &lt;br /&gt; def testTwoPaths(self):&lt;br /&gt;  files = [&lt;br /&gt;   r'D:\trunk\Projects\Build\NAnt\LineCounter\LineCount.cs',&lt;br /&gt;   r'D:\trunk\Projects\Build\NAnt\LineCounter\LineCountCollection.cs']&lt;br /&gt;  n = createNodeSorter(files)&lt;br /&gt;  self.assertEqual(3, len(n))&lt;br /&gt;  self.assertEqual(['D:', 'trunk', 'Projects', 'Build', 'NAnt', 'LineCounter'], n.path)&lt;br /&gt;  &lt;br /&gt; def testOverlappingPaths(self):&lt;br /&gt;  files = [&lt;br /&gt;   r'D:\trunk\Projects\Build\NAnt\LineCounter',&lt;br /&gt;   r'D:\trunk\Projects\Build\NAnt\LineCounter\LineCountCollection.cs']&lt;br /&gt;  n = createNodeSorter(files)&lt;br /&gt;  self.assertEqual(2, len(n))&lt;br /&gt;  self.assertEqual(['D:', 'trunk', 'Projects', 'Build', 'NAnt', 'LineCounter'], n.path)&lt;br /&gt; &lt;br /&gt;class NodeSorterTest(unittest.TestCase):&lt;br /&gt; def testBlankNode(self):&lt;br /&gt;  n = Node()&lt;br /&gt;  self.assertEqual('', repr(n))&lt;br /&gt;  self.assertEqual(1, len(n))&lt;br /&gt;  &lt;br /&gt; def testSingleChild(self):&lt;br /&gt;  n = Node(['base'], [['child1']])&lt;br /&gt;  self.assertEqual(2, len(n))&lt;br /&gt;  self.assertEqual('base', `n`)&lt;br /&gt;  &lt;br /&gt; def testNoChildren(self):&lt;br /&gt;  n = Node(['C:'])&lt;br /&gt;  self.assertEqual(1, len(n))&lt;br /&gt;  &lt;br /&gt; def testInvalidPath(self):&lt;br /&gt;  self.assertRaises(AssertionError, lambda: Node('base'))&lt;br /&gt;  &lt;br /&gt; def testIteratorBlankNode(self):&lt;br /&gt;  n = Node()&lt;br /&gt;  nodes = [node for node in n]&lt;br /&gt;  self.assertEqual([[]], nodes)&lt;br /&gt;  &lt;br /&gt; def testIteratorNodeWithPath(self):&lt;br /&gt;  n = Node(['C:', 'Program Files'])&lt;br /&gt;  nodes = [node for node in n]&lt;br /&gt;  self.assertEqual([['C:', 'Program Files']], nodes)&lt;br /&gt;  &lt;br /&gt; def testIteratorWithOneChild(self):&lt;br /&gt;  n = Node(['C:'], [['Program Files']])&lt;br /&gt;  nodes = [node for node in n]&lt;br /&gt;  self.assertEqual(['C:'], nodes[0])&lt;br /&gt;  self.assertEqual(['C:', 'Program Files'], nodes[1])&lt;br /&gt;  &lt;br /&gt; def testThreeLevels(self):&lt;br /&gt;  n = Node(['C:'], [['Program Files', 'Adobe', 'Acrobat 7.0'], ['Program Files', "CruiseControl.NET"]]) &lt;br /&gt;  self.assertEqual(5, len(n))&lt;br /&gt;  &lt;br /&gt; def testNoCommonBase(self):&lt;br /&gt;  n = Node([], [['C:', 'Program Files', 'Adobe', 'Acrobat 7.0'], ['D:', 'Program Files', "CruiseControl.NET"]])&lt;br /&gt;  self.assertEquals(8, len(n))&lt;br /&gt; &lt;br /&gt;if __name__ == '__main__':&lt;br /&gt; unittest.main()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-2299367157404176323?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/2299367157404176323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=2299367157404176323' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2299367157404176323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/2299367157404176323'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/02/generating-loc-from-nant.html' title='Generating LOC from NAnt, Part 1'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-1419220352294138282</id><published>2007-01-19T17:39:00.000-05:00</published><updated>2007-02-03T14:32:40.768-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CruiseControl'/><category scheme='http://www.blogger.com/atom/ns#' term='NAnt'/><title type='text'>Boosting Your Assembly.cs Revision Number from NAnt</title><content type='html'>I know that others have blogged about this, esp. &lt;a href="http://www.hanselman.com/blog/NewVersionOfUpdateVersiongreatForThosePeskyAssemblyInfocsFiles.aspx"&gt;Scott Hanselman&lt;/a&gt; did, somewhere...I'm digressing already. I wanted the simplest possible solution: I wanted to leverage already existing NAnt or NAntContrib tasks, rather than write my own; I did &lt;span style="FONT-STYLE: italic"&gt;not &lt;/span&gt;want to use the CruiseControl.NET label, since a new CC.NET installation starts from zero, as I found out after I hosed my own, and in any case I would want CC.NET ultimately to do no more than call a bunch of NAnt tasks, pass all the artifacts through some XSLT, and create the Web pages. What I ended up with was 1, a file called &lt;code&gt;version.txt&lt;/code&gt;, containing only (at the moment) the text &lt;code&gt;3.0.0.1401&lt;/code&gt;; 2, a file called &lt;code&gt;CommonAssemblyInfo.cs&lt;/code&gt;, including assembly-level metadata common to the entire application, e.g. &lt;code&gt;[assembly: AssemblyVersionAttribute("3.0.0.1412")]&lt;/code&gt;; 3, the following NAnt task:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;target name="createAsmInfo" description="Overwrite the rev. #" &amp;gt;&lt;br /&gt;  &amp;lt;if test="${not property::exists('version.txt')}"&amp;gt;&lt;br /&gt;    &amp;lt;property name="version.txt" value="version.txt" /&amp;gt;&lt;br /&gt;  &amp;lt;/if&amp;gt;&lt;br /&gt;  &amp;lt;if test="${not property::exists('common.assembly.info')}"&amp;gt;&lt;br /&gt;    &amp;lt;property&lt;br /&gt;      name="common.assembly.info"&lt;br /&gt;      value="Applications/CommonAssemblyInfo.cs" /&amp;gt;&lt;br /&gt;  &amp;lt;/if&amp;gt;&lt;br /&gt;  &amp;lt;!-- I.e., increment the revision number and *not* the build number. --&amp;gt;&lt;br /&gt;  &amp;lt;version path="${version.txt}"&lt;br /&gt;    buildtype="NoIncrement"&lt;br /&gt;    revisiontype="Increment" /&amp;gt;&lt;br /&gt;  &amp;lt;asminfo output="${common.assembly.info}" language="CSharp"&amp;gt;&lt;br /&gt;    &amp;lt;imports&amp;gt;&lt;br /&gt;      &amp;lt;import namespace="System.Reflection" /&amp;gt;&lt;br /&gt;    &amp;lt;/imports&amp;gt;&lt;br /&gt;    &amp;lt;attributes&amp;gt;&lt;br /&gt;      &lt;!-- Variables 'buildnumber.*' are created by this task. --&gt;&lt;br /&gt;      &amp;lt;attribute type="AssemblyVersionAttribute"&lt;br /&gt;     &lt;br /&gt;value="${buildnumber.major}.${buildnumber.minor}.${buildnumber.build}.${buildnumber.revision}" /&amp;gt;&lt;br /&gt;      &amp;lt;attribute type="AssemblyCopyrightAttribute" value="Copyright © 2006 MDi" /&amp;gt;&lt;br /&gt;      &amp;lt;attribute type="AssemblyCompanyAttribute" value="MegaDyne, Inc." /&amp;gt;&lt;br /&gt;      &amp;lt;attribute type="AssemblyProductAttribute" value="KillerApp" /&amp;gt;&lt;br /&gt;    &amp;lt;/attributes&amp;gt;&lt;br /&gt;  &amp;lt;/asminfo&amp;gt;&lt;br /&gt;  &amp;lt;!-- The svn task is inadequate. It doesn't handle commits! --&amp;gt;&lt;br /&gt;  &amp;lt;exec program='${svn.exe}'&lt;br /&gt;    commandline='commit --non-interactive --username=${svn.username} --password=${svn.password} -m "Updated by CruiseControl.NET" ' /&amp;gt;&lt;br /&gt;&amp;lt;exec program='${svn.exe}' commandline='update' /&amp;gt;&lt;br /&gt;&amp;lt;/target&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;The &amp;lt;asminfo&amp;gt; task should really not overwrite existing metadata unless so instructed, but I haven't tested that yet. Note that I immediately commit the changed files back into the repository; this might be the wrong thing to do, unless the build succeeds. If it does fail, then CruiseControl.NET will be unable to update the source code from the repository; i.e., there files I've changed will be in conflict. This suggests that I really do &lt;i&gt;all&lt;/i&gt; my work from NAnt, but I haven't gotten around to that yet. A further problem is that &lt;code&gt;version.txt&lt;/code&gt; and &lt;code&gt;CommonAssemblyInfo.cs&lt;/code&gt; will look new to CC.NET the next time it checks, triggering another build. CC.NET offers a "filter trigger" for this sort of thing, but I found the documentation incomprehensible, so I haven't been able to use it yet.&lt;br /&gt;&lt;p&gt;&lt;br /&gt;The one additional step is to remove the common metadata from AssemblyInfo.cs files throughout the solution, and add &lt;code&gt;CommonAssemblyInfo.cs&lt;/code&gt; as a link. This turned out to be somewhat tricky for me. I have an old habit of configuring Windows Explorer to open files with a &lt;i&gt;single&lt;/i&gt; click; a more experienced developer once told me that he was "more productive" with this option; I fell for it, and now I'm stuck with this wierd tic. Anyway, I would select "Add existing item" from a project's context menu, navigate to &lt;code&gt;CommonAssemblyInfo.cs&lt;/code&gt;, click on it...and of course a &lt;i&gt;copy&lt;/i&gt; would be added to the project. You won't have this problem! Anyway, I had to right-click on the file from the File Open dialog, select "Select" from the context menu, and only &lt;i&gt;then&lt;/i&gt; would the little triangle be enabled:&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_yClxQhTLbco/RbJK6Z1jpaI/AAAAAAAAAAM/j0vHyv2HK-o/s1600-h/AddAsLink.PNG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5022158901699913122" style="MARGIN: 0px 0px 10px 10px; CURSOR: hand" alt="" src="http://1.bp.blogspot.com/_yClxQhTLbco/RbJK6Z1jpaI/AAAAAAAAAAM/j0vHyv2HK-o/s320/AddAsLink.PNG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;That's pretty much it. You have to remember to change every project this way, but that's about it. Some (Scott Hanselman) prefer to edit the .csproj files themselves; I am probably revealing myself as a non-guru by this admission, but I hate doing that.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-1419220352294138282?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/1419220352294138282/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=1419220352294138282' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1419220352294138282'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/1419220352294138282'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2007/01/boosting-your-assemblycs-revision.html' title='Boosting Your Assembly.cs Revision Number from NAnt'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_yClxQhTLbco/RbJK6Z1jpaI/AAAAAAAAAAM/j0vHyv2HK-o/s72-c/AddAsLink.PNG' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115907143055042841</id><published>2006-09-24T00:15:00.000-04:00</published><updated>2006-09-24T00:17:10.560-04:00</updated><title type='text'>Land of the Free</title><content type='html'>From a review of David Brion Davis's &lt;em&gt;Inhuman Bondage&lt;/em&gt; in the London &lt;em&gt;Telegraph&lt;/em&gt;: "The story Davis tells is strikingly at odds with traditional accounts. Those used to thinking of New World settlement in terms of a widening of horizons and a breaking free from European constraints will be surprised to learn that, before 1820, African slaves outnumbered European settlers by a ratio of more than five to one. This is not to deny that some Europeans did find the experience liberating, although it is worth remembering that many of them arrived themselves as indentured servants or transported criminals, and so were hardly in a position to feel liberated. The picture is certainly very different from that painted by those who like to think of early America either as a refuge for the oppressed or a gigantic adventure playground. If numbers are what count, the “typical” American settler was neither a swaggering conquistador nor a Bible-quoting Puritan but a miserable African slave toiling on a sugar plantation."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115907143055042841?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115907143055042841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115907143055042841' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115907143055042841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115907143055042841'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/09/land-of-free.html' title='Land of the Free'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115894677953452339</id><published>2006-09-22T13:36:00.000-04:00</published><updated>2006-09-22T13:39:39.546-04:00</updated><title type='text'>CEOs are Vile</title><content type='html'>I was cheerfully reading &lt;a href="http://sports.espn.go.com/espn/page2/story?page=easterbrook/060919"&gt;Tuesday Morning Quarterback&lt;/a&gt; while I snarfed my Thai fish cakes, and nearly spat when I came to the passage below. Perhaps I'm naive, but what "market forces" are operating on behalf of a CEO &lt;span style="font-weight: bold;"&gt;who's about to quit&lt;/span&gt;?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Much news and sports commentary focuses on the ever-larger paychecks of professional athletes. But even Peyton Manning is a day laborer compared to the modern Fortune 500 CEO. In May, Exxon Mobil shareholders passed the first resolution in company history to be enacted over opposition of the board of directors; at issue was shareholder fury regarding the $168 million retiring CEO Lee Raymond awarded himself in his final year. "There's some unhappiness about the way Raymond's compensation was handled," new Exxon Mobil CEO Rex Tillerson dryly told a news conference. During the summer Hank McKinnell was ousted as CEO of Pfizer. Over his last five years at the helm, he got $162 million, even as Pfizer earnings faltered. Carol Hymowitz of the Wall Street Journal reported that the head of Pfizer's "compensation committee" defended McKinnell's windfall on grounds of market forces in executive pay -- which in this context appears to mean, "CEOs at other companies are picking shareholders' pockets, too." There just wasn't anybody who would have taken the Pfizer job for less than $162 million? McKinnell's pay for his tenure atop Pfizer equates to $130,000 per work day.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115894677953452339?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115894677953452339/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115894677953452339' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115894677953452339'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115894677953452339'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/09/ceos-are-vile.html' title='CEOs are Vile'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115893698164409813</id><published>2006-09-22T10:53:00.000-04:00</published><updated>2006-09-23T10:02:58.480-04:00</updated><title type='text'>African Pop</title><content type='html'>&lt;a href="http://profile.myspace.com/index.cfm?fuseaction=user.viewprofile&amp;amp;friendid=102008465"&gt;Ok-Oyot Project&lt;/a&gt; live at &lt;a href="http://www.iotaclubandcafe.com/"&gt;Iota &lt;/a&gt;in Arlington, VA: should be beautiful!&lt;br /&gt;&lt;br /&gt;...and it wasn't. Why did the sound engineer insist on mixing them like some stupid rock band, with the thudding bass all the way up, obscuring the interlocking guitar figures?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115893698164409813?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115893698164409813/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115893698164409813' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115893698164409813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115893698164409813'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/09/african-pop.html' title='African Pop'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115747193264244451</id><published>2006-09-05T11:08:00.000-04:00</published><updated>2006-09-05T14:58:53.270-04:00</updated><title type='text'>xUnit for Scheme LISP</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1104/900/1600/browseForFolder.1.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://photos1.blogger.com/blogger/1104/900/320/browseForFolder.1.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;After a bit of pain, caused by my trying to install the &lt;a href="http://schematics.sourceforge.net/"&gt;SchemeUnit 2.0 download&lt;/a&gt; available on SourceForge, I managed to locate the 3.0 download &lt;a href="http://planet.plt-scheme.org/"&gt;here&lt;/a&gt;. I'm a novice when it comes to LISP, and to DrScheme, so the sparse installation instructions were inadequate for me. After some trial and error, which included messing up my DrScheme installation enough that I had to reinstall it, I found the happy path. First, I downloaded the .plt file and saved it an arbitary location (Firefox insists on showing the contents of the file as text, which is &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; what you want; I had to resort to IE).&lt;br /&gt;&lt;br /&gt;Noel Welsh, the author of SchemeUnit, has dropped hints about how to install the .plt &lt;a href="http://sat.inesc-id.pt/%7Epocm/minority/index.php/2006/02/23/ann-new-beta-of-schemeunit/"&gt;using planet&lt;/a&gt;, but my only goal is to learn a little more about LISP, and I don't want to be distracted by another utility. Noel's also turned out to be slightly inconsistent with the original documentation, as we'll see. In any case, Dr. Scheme's File menu has an Install .plt File... option, which seemed like the right method, so I clicked it and saw the dialog on the right. Which path to pick? After some trial and error, including a reinstallation of DrScheme, I noticed &lt;span style="font-style: italic;"&gt;another &lt;/span&gt;dialog that had been concealed by the file selector. It turns out that I needed to navigate to &lt;span style="font-family:courier new;"&gt;C:\Program Files\PLT\collects&lt;/span&gt;, and make a new directory called &lt;span style="font-family:courier new;"&gt;schematics&lt;/span&gt;, after which I'd click OK and be happy forever. Oops! When I typed (require (lib "test.ss" "schemeunit")) at the DrScheme console, as SchemeUnit's &lt;a href="http://schematics.sourceforge.net/schemeunit/schemeunit-Z-H-2.html#node_chap_2"&gt;Quick Start&lt;/a&gt; page says, it responded, "&lt;span style="color: rgb(255, 0, 0); font-style: italic;"&gt;collection not found: "schemeunit" in any of [the paths displayed below]&lt;/span&gt;." D'oh! So, just to be one the safe side, I reinstalled DrScheme &lt;span style="font-style: italic;"&gt;again&lt;/span&gt;, then followed the same steps as before to install the .plt file, but this time created a &lt;span style="font-family:courier new;"&gt;schemeunit &lt;/span&gt;directory within my PLT installation. It works! Now I actually have to write some LISP. &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1104/900/1600/installerProgress.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://photos1.blogger.com/blogger/1104/900/320/installerProgress.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115747193264244451?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115747193264244451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115747193264244451' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115747193264244451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115747193264244451'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/09/xunit-for-scheme-lisp.html' title='xUnit for Scheme LISP'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115663388868845767</id><published>2006-08-26T17:28:00.000-04:00</published><updated>2006-09-22T12:16:11.573-04:00</updated><title type='text'>Voodoo in New Orleans</title><content type='html'>I recently read Ned Sublette's superb &lt;a style="font-style: italic;" href="http://www.amazon.com/gp/product/1556525168/sr=8-1/qid=1156628591/ref=pd_bbs_1/002-4454817-0014436?ie=UTF8"&gt;&lt;span class="sans"&gt;Cuba and Its Music: From the First Drums to the Mambo&lt;/span&gt;&lt;/a&gt;&lt;span class="sans"&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;b class="sans"&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-weight: bold;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span class="sans"&gt;and&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt; &lt;/span&gt;&lt;/span&gt;would certainly recommend it to anyone interested in one of the world's most musically brilliant cultures. Sublette cofounded the QubaDisc label  &lt;/span&gt;and is coproducer of public radio's &lt;i&gt;Afropop Worldwide&lt;/i&gt;.&lt;span style="font-weight: bold;"&gt; &lt;/span&gt;I don't know how  well-founded is his account of musical commerce in the ancient world between Phoenicia, North Africa, and Cadiz, and the steady flow of African rhythmic innovation into Europe and the Western hemisphere over the last millenium. For example, a Kikongo etymology for &lt;span style="font-style: italic;"&gt;sarabande&lt;/span&gt; seems rather fanciful when a very similiar Persian word for "song" is at hand. Nonetheless, a North American reader should be especially grateful to Sublette for his ambition to detail all the musical options available to Cuban musicians.&lt;br /&gt;&lt;br /&gt;Many of these are really quite different from those available here. Spanish and even Moorish song forms are still typical of &lt;span style="font-style: italic;"&gt;son&lt;/span&gt;, for example, and there really is no Cuban equivalent of the 12-bar blues (though Guillermo Portabales's "Hay Jaleo" in in AAB form).  There are also significant differences in the African material that predominates here and there respectively. Dizzy Gillespie, whose musical understanding of the rumba (the additional 'h' was some strange marketing trick) was beyond reproach, thought that the differences between jazz and rumba were due to drums having been "taken from" North American slaves. However, the slave trade to Cuba and that to North America didn't work the same way. The trade to Cuba continued longer; in Cuba, Africans could much more readily maintain their particular traditions (or, more accurately, they could build on them within Cuba, a comparatively large place; they could also integrate the musical forms brought by refugees from Haiti, or shipments of slaves from different areas). On the other hand, there's really no Cuban music that has "blue notes."&lt;br /&gt;&lt;br /&gt;Paul Oliver noticed decades ago (cf. &lt;span class="sans" style="font-style: italic;"&gt;Savannah Syncopators) &lt;/span&gt;that blues sounds more like Malian than Ghanaian music, i.e., solo string instruments figure much more heavily than percussion ensembles. Gerhard Kubik built on this idea in such works as &lt;span class="sans"&gt;&lt;span style="font-style: italic;"&gt;Africa and the Blues (American-Made Music)&lt;/span&gt;&lt;font&gt;. Slaves brought to this country may have lost some of their percussive knowledge, may have had less to begin with, or may simply have selected from among their musical options those most useful: those of savannah herders. Hence the "field holler," etc.&lt;br /&gt;&lt;br /&gt;I have one quibble with Sublette's book, and that is that the rather nasty cultic practices of &lt;span style="font-style: italic;"&gt;palo&lt;/span&gt; (conjury with dead body parts, etc.) are treated rather too enthusiastically. For me, only the music justifies the religion. As for New Orleans voodoo, I'm rather skeptical about how profoundly &lt;a href="http://www.csicop.org/si/2002-01/i-files.html"&gt;anyone has ever believed it&lt;/a&gt;.&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115663388868845767?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115663388868845767/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115663388868845767' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115663388868845767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115663388868845767'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/08/voodoo-in-new-orleans.html' title='Voodoo in New Orleans'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115637814202952943</id><published>2006-08-23T20:08:00.000-04:00</published><updated>2006-09-24T11:20:58.443-04:00</updated><title type='text'>Agility in Gov't Contracting</title><content type='html'>In any large bureaucracy, one is often deprived of the requisite managerial power to carry out one's responsibilities, or even of the right to express and exercise one's better judgement. Yes, here's my gratuitous, parenthetical reference to the failed &lt;a href="http://http://www.washingtonpost.com/wp-dyn/content/article/2006/08/17/AR2006081701485.html?sub=AR"&gt;Trilogy project at the FBI&lt;/a&gt;. I'm happy not to work at SAIC, or any other big contractor. "Agile" has become a buzzword in gov't contracting, but I'm more than skeptical. There's too much money to be made in Northern Virginia by feeding gov't clients' hopes that the more that more requirements they state up front, the more they'll get. In case you don't have access to the &lt;span style="font-style: italic;"&gt;Washington Post&lt;/span&gt; online, I'll quote:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;Matthew Patton, a programmer who worked on the contract for SAIC, said the company seemed to make no attempts to control costs. It kept 200 programmers on staff doing "make work," he said, when a couple of dozen would have been enough. The company's attitude was that "it's other people's money, so they'll burn it every which way they want to," he said. &lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;Patton, a specialist in IT security, became nervous at one point that the project did not have sufficient safeguards. But he said his bosses had little interest. "Would the product actually work? Would it help agents do their jobs? I don't think anyone on the SAIC side cared about that," said Patton, who was removed from the project after three months when he posted his concerns online.&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To respond preemptively to any defenses of SAIC, let me say that if they knew that they couldn't deliver, they should have refused to take any more money, money that came, in the end, from taxpayers. This isn't a game, people; &lt;a href="http://www.amazon.com/gp/product/037541486X/sr=8-1/qid=1155951074/ref=pd_bbs_1/002-4454817-0014436?ie=UTF8"&gt;we're talking about national security here&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115637814202952943?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115637814202952943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115637814202952943' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115637814202952943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115637814202952943'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/08/agility-in-govt-contracting.html' title='Agility in Gov&apos;t Contracting'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115532883436178507</id><published>2006-08-11T08:48:00.000-04:00</published><updated>2006-08-23T20:17:14.100-04:00</updated><title type='text'>Developers Considered as Obstacle to Agility</title><content type='html'>A few days before I flew to Minneapolis to attend Agile2006, I underwent my performance review at work. My project manager and practice manager lauded my "passion" for my work, and suggested only that I could be even more valuable to the company if I could help them pass the CMMI Level 3 audit. I immediately thought that the work I'd already done to bring the legacy code base "under test," devise regression and acceptance tests in FIT, and automate builds via CruiseControl.NET could be extended by my entire team, under the aegis of the company's CMMI certification initiative, i.e., I could test-infect my coworkers &lt;em&gt;and &lt;/em&gt;advance my employer's interests at the same time. Anyone who knows what a pessimist I am will know that I wouldn't be telling this story at all if it had turned out as I had hoped.&lt;br /&gt;&lt;br /&gt;I must say that I like my job, and I like my coworkers. After working on government contracts for several years for employers and under project managers who could and did expressly forbid me the very use of testing and refactoring tools (Watir, Resharper, Eclipse, JMock, FitNesse, etc.), I ride the bus to Arlington in an enthusiastic mood. They are certainly the cleverest and most diligent team I've worked with in a good while.&lt;br /&gt;&lt;br /&gt;Anyway, I'm not so much discouraged that my coworkers didn't rally to NUnit (some of them &lt;em&gt;are &lt;/em&gt;familiar with JUnit; they seem to think it's just something you do in the Java world) and FitNesse after I brought the test coverage of a dismal yet significant code base up to 40% from 0% in a few months, as I am puzzled. I mused over this problem rather a lot during lulls in the action at Agile2006. At a status meeting at work last week, my jaw nearly dropped when the technical lead insisted that WinRunner was the appropriate tool for getting the application under test. I consider WinRunner to be a dead end, and in fact several teams near mine have attempted to make it part of their process and then given up. I don't want to review all the arguments against testing only through the GUI; I'll just say that if you test that way, you'll code that way, namely from the GUI on down, and your code will almost certainly be incoherent, just as if you code from the database schema on up. In one day, I managed to write tests in FitNesse that cover &lt;span style="FONT-STYLE: italic"&gt;every single one&lt;/span&gt; of the use cases we were able to conceive for the application.&lt;br /&gt;&lt;br /&gt;I should be pleased to have had the chance to create a &lt;span style="FONT-STYLE: italic"&gt;fait accompli&lt;/span&gt; in FitNesse, yet it's somewhat tiring, always to have to carry out an alternative strategy before I can argue the case for it! My next goal is to configure CruiseControl.NET to pull the newest source from Subversion, rebuild it, run all the unit and acceptance tests, etc., and to make all this so easy that no one could think of doing things any other way. Until I've figured it out, everyone will continue to do manual testing. In fact, they'll continue even after I've created my next &lt;span style="FONT-STYLE: italic"&gt;fait accompli&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Why? They're smart and diligent. Why do they not also realize how much time they waste every day? Perhaps the typical software developer's education provides him with an illusion that experience may never dispel, namely that if he simply throws himself at a problem the way he threw himself at assignments, he'll solve them. Then, of course, his professional progress becomes a matter of learning more about domains, or about platforms, but never about the resolute micrological analysis of one's own practices. Perhaps the most accomplished developer at my workplace, who participates in W3C's &lt;a href="http://www.w3.org/XML/Binary/"&gt;Binary XML Working Group&lt;/a&gt;, considers TDD to be unsuitable for "deep programming." It's not worth my time to debate him, here or to his face. The point I want to make is that the agile community's characterization of the resistance to agility as coming from "pointy-headed bosses" (yes, I heard this phrase more than once Agile2006), or from within "dysfunctional" organization, or from customers (!), is wrong. Competent developers are the source of this resistance, developers whose history of accomplishment deceives them that they don't need to think any harder about how they do things. Kent Beck, in &lt;span style="FONT-STYLE: italic"&gt;Extreme Programming Explained&lt;/span&gt;, 2nd ed., has argued that overwork is, paradoxically, a retreat from professional responsibility. Yet overwork is strangely tempting to developers—I'm still tempted even now, when I have two small boys at home—so I can only conclude that on some level, we think that what our job is about is cranking out code, rather than exercising good judgment.&lt;br /&gt;&lt;br /&gt;As evidence, I would present the odd attachment that my technical lead has to WinRunner, and to test plans written in Excel! As a software developer, in Northern VA and Silicon Valley, I have had 8 different technical leads, all of them entirely competent developers, and &lt;span style="FONT-STYLE: italic"&gt;every single one&lt;/span&gt;, when he became a manager,&lt;span style="FONT-STYLE: italic"&gt; &lt;/span&gt;suddenly developed a mania for Gantt charts, or documentation that &lt;span style="FONT-STYLE: italic"&gt;immediately and obviously&lt;/span&gt; got out of sync with the application's functionality, Microsoft Word documents specifying variable naming conventions or coding standards, or, &lt;span style="FONT-STYLE: italic"&gt;quel horreur&lt;/span&gt;, IEEE 830 requirements! Every one, in other words, was utterly incapable of analyzing his own and his subordinates' productivity, believing instead that imposing additional such adventitious requirements on the only sort of development they'd every practiced could be possible, desirable, and effective. In my experience, when my development process actually conformed to a prior Gantt chart, that was due to dumb luck, deliberate ass-dragging, or retrofitting the Gantt chart entries to the work I'd actually done with my manager's explicit approval. Is this poignant, or hilarious?&lt;br /&gt;&lt;br /&gt;In short, the resistance to agility, to the continuous and deliberate analysis and refinement of one's &lt;span style="FONT-STYLE: italic"&gt;modus operandi &lt;/span&gt;individually and as part of a team, conflicts with a profound motivation of many software developers: to do as much of it as they can without thinking about how they do it. The avoidance of self-reflection, let alone the kind of reflection that pair programming induces, becomes a professional prerogative. I think of TDD as a kind of carefully calibrated negative reinforcement, in which one purposefully causes only as many tests to fail as one can easily fix. In return, of course, one gets the positive reinforcement of the green bar. In contradiction to the frequent claim that TDD should be the most attractive practice to introduce first to a team, many developers resist precisely &lt;em&gt;this&lt;/em&gt; practice, because it seems like the intrusion into their personal space of a less intelligent &lt;em&gt;alter ego&lt;/em&gt;, one who purposefully writes code that he knows won't work! Even younger developers will say, "I know how to do this; why would I test it &lt;span style="FONT-STYLE: italic"&gt;first&lt;/span&gt;?" If you begin with that attitude, it can take an extreme effort of will to let go of it when you really &lt;span style="FONT-STYLE: italic"&gt;do &lt;/span&gt;know how to do things.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115532883436178507?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115532883436178507/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115532883436178507' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115532883436178507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115532883436178507'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/08/developers-considered-as-obstacle-to.html' title='Developers Considered as Obstacle to Agility'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115497793121686731</id><published>2006-08-07T15:11:00.000-04:00</published><updated>2006-08-08T11:58:23.223-04:00</updated><title type='text'>The Change-Counting Algorithm &amp; TDD</title><content type='html'>I recently decided to make my way through Sussman &amp; Abelson's classic CS textbook &lt;i&gt;&lt;a href="http://http://mitpress.mit.edu/sicp/"&gt;The Structure and Interpretation of Computer Programs&lt;/a&gt;&lt;/i&gt;, partly for reasons of professionalism, partly to clarify my thoughts on "test-driven development" and algorithmic programming. Every familiar example, and nearly every question I see on the &lt;a href="http://groups.yahoo.com/group/testdrivendevelopment/"&gt;Test-Driven Development&lt;/a&gt; newsgroup concerns object-oriented programming. The "bowling game" is simply too trivial a counterexample, in my opinion, but we can dispute this elsewhere.&lt;br /&gt;&lt;br /&gt;At any rate, SICP poses this problem in Chapter 1: "How many different ways can we make change of $ 1.00, given half-dollars, quarters, dimes, nickels, and pennies? More generally, can we write a procedure to compute the number of ways to&lt;br /&gt;change any given amount of money?" There is, it turns out, a fair amount of literature on this problem (cf. &lt;a href="http://citeseer.ist.psu.edu/pearson94polynomialtime.html"&gt;David Pearson's paper&lt;/a&gt;, or &lt;br /&gt;&lt;a href="http://citeseer.ist.psu.edu/613434.html"&gt;What This Country Needs is an 18 Cent Piece&lt;/a&gt;). Despite having the solution right in front of me, an implementation eluded me until I really considered the structure of the problem, which is recursive. The authors provide unequivocal hints: "Consider this reduction rule carefully, and convince yourself that we can use it to describe an algorithm if we specify the following degenerate cases: 1, If a is exactly 0, we should count that as 1 way to make change; 2, If a is less than 0, we should count that as 0 ways to make change; 3, If n [the number of available denominations] is 0, we should count that as 0 ways to make change." These should be the starting points for the test-driven development of the algorithm, but I only understood their significance within the recursion after pondering the problem while riding the bus. Imagine you've got quarters and pennies, and are asked to make $.26 in change. You can try to use 1 quarter. Now you've got to make $.01 with quarters and pennies. If you use another quarter, you've then got to make - $.24, at which point condition 1 will obtain; if you use a penny, you'll then have to make $.00, at which point condition 2 will obtain (and the recursion will stop). Condition 3 will also stop the recursion, i.e., you've got no more denominations to try. Once I'd understood all this, I could eliminate some of the recursive calls by using integer division, i.e., the largest number of quarters that I can use to make &lt;i&gt;a&lt;/i&gt; will be a / 25. &lt;br /&gt;&lt;br /&gt;After that, I quickly wrote the following code in Python, and indeed this implementation evolved in order with precisely these tests:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import unittest&lt;br /&gt;&lt;br /&gt;def countChange(amount, coins):&lt;br /&gt;    if amount == 0:&lt;br /&gt;        return 1&lt;br /&gt;    if len(coins) == 0:&lt;br /&gt;        return 0&lt;br /&gt;    if len(coins) == 1 and amount % coins[0] == 0:&lt;br /&gt;        return 1&lt;br /&gt;    currentCoin = coins[0]&lt;br /&gt;    remainingCoins = coins[1:]&lt;br /&gt;    currentPossibilities = [currentCoin * i for i in range(0, amount / currentCoin + 1)]&lt;br /&gt;    return sum([countChange(amount - p, remainingCoins) for p in currentPossibilities])&lt;br /&gt;&lt;br /&gt;class TestCountChange(unittest.TestCase):&lt;br /&gt;    def testAmountZeroWithNoCoins(self):&lt;br /&gt;        self.assertEqual(1, countChange(0, []))&lt;br /&gt;        &lt;br /&gt;    def testAnyAmountWithNoCoins(self):&lt;br /&gt;        self.assertEqual(0, countChange(100, []))&lt;br /&gt;        &lt;br /&gt;    def testAmountZeroWithAnyCoins(self):&lt;br /&gt;        self.assertEqual(1, countChange(0, (1, 5, 10)))&lt;br /&gt;        &lt;br /&gt;    def testAmountsWithOneCoin(self):&lt;br /&gt;        self.assertEqual(1, countChange(1, [1]))&lt;br /&gt;        self.assertEqual(1, countChange(5, [5]))&lt;br /&gt;        self.assertEqual(1, countChange(10, [10]))&lt;br /&gt;        &lt;br /&gt;    def testAnyAmountWithPennies(self):&lt;br /&gt;        self.assertEqual(1, countChange(10, [1]))&lt;br /&gt;        self.assertEqual(1, countChange(10000, [1]))&lt;br /&gt;        &lt;br /&gt;    def testMultiplesOfSingleCoin(self):&lt;br /&gt;        self.assertEqual(1, countChange(100, [5]))&lt;br /&gt;        self.assertEqual(1, countChange(100, [50]))&lt;br /&gt;        &lt;br /&gt;    def testTwoKindsOfCoins(self):&lt;br /&gt;        self.assertEqual(2, countChange(10, [5, 10]))&lt;br /&gt;        self.assertEqual(2, countChange(7, [5, 1]))&lt;br /&gt;        self.assertEqual(0, countChange(7, [5, 10]))&lt;br /&gt;        &lt;br /&gt;    def testTextbookExample(self):&lt;br /&gt;        self.assertEqual(292, countChange(100, [1, 5, 10, 25, 50]))&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    unittest.main()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115497793121686731?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115497793121686731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115497793121686731' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115497793121686731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115497793121686731'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/08/change-counting-algorithm-tdd.html' title='The Change-Counting Algorithm &amp; TDD'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115454544584010224</id><published>2006-08-02T14:25:00.000-04:00</published><updated>2006-08-02T15:30:10.536-04:00</updated><title type='text'>Generating A Regular Expression</title><content type='html'>A bioinformaticist recently posed &lt;a href="http://regexadvice.com/forums/thread/19555.aspx"&gt;an interesting problem&lt;/a&gt; to the RegexAdvice forum, where I like to lurk. Can a regular expression let me search for a pattern such as &lt;code&gt;ATGGCACAGGTTATCCATTATCAGACCTTTACAAAAATCAGATAA&lt;/code&gt;, allowing exactly 1 mismatched character? Trust me, the patterns get a lot longer than this! Anyway, inexact matching algorithms would be too inefficient here: we know exactly how many mismatches we're allowed. On the other hand, I'm not sure that &lt;span style="font-style:italic;"&gt;any &lt;/span&gt;solution could take advantage of optimized exact matching algorithms, whether those were part of a regex implementation or a custom implementation. At first, I considered how to write an expression that allowed a single mismatch somewhere in the pattern; then I realized that the general case, allowing m mismatches, is actually easier to write. I would like to present this work as part of a prospective talk on "test-driven development and recursive algorithms," or something like that, but to be honest, this provisional implementation was preceded by a lot of trial and error. At some point, however, I knew how the algorithm had to work, and how the expression had to look, and at that point, of course, it was easy to employ TDD.&lt;br /&gt;&lt;br /&gt;OK, here it is, in Python:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import unittest&lt;br /&gt;&lt;br /&gt;def expand(s, mismatches):&lt;br /&gt;    assert mismatches &lt;= len(s)&lt;br /&gt;    # Only exact matching from this point on.&lt;br /&gt;    if mismatches == 0:&lt;br /&gt;        return s&lt;br /&gt;    # All mismatches allowed.&lt;br /&gt;    if mismatches == len(s):&lt;br /&gt;        return '.' * mismatches&lt;br /&gt;    # Branch: match the next character exactly, or mismatch.&lt;br /&gt;    return [[s[0], expand(s[1:], mismatches)], &lt;br /&gt;           ['.', expand(s[1:], mismatches - 1)]]&lt;br /&gt;&lt;br /&gt;def collapse(l):&lt;br /&gt;    if type(l) == str:&lt;br /&gt;        return l&lt;br /&gt;    if type(l[0]) == str:&lt;br /&gt;        # They're all strings, in this case.&lt;br /&gt;        return ''.join(map(collapse, l))&lt;br /&gt;    return '(' + '|'.join(map(collapse, l)) + ')'&lt;br /&gt;&lt;br /&gt;class TestExpansion(unittest.TestCase):&lt;br /&gt;    def testZeroMismatches(self):&lt;br /&gt;        self.assertEquals('string', expand('string', 0))&lt;br /&gt;        &lt;br /&gt;    def testOneCharOneMismatch(self):&lt;br /&gt;        self.assertEqual('.', expand('a', 1))&lt;br /&gt;        &lt;br /&gt;    def testMultipleCharsAllMismatches(self):&lt;br /&gt;        self.assertEqual('..', expand('ab', 2))&lt;br /&gt;        &lt;br /&gt;    def testTwoCharsOneMismatch(self):&lt;br /&gt;        self.assertEqual([['a', '.'], ['.', 'b']], expand('ab', 1))&lt;br /&gt;        &lt;br /&gt;    def testCollapseString(self):&lt;br /&gt;        self.assertEqual('a', collapse('a'))&lt;br /&gt;        &lt;br /&gt;    def testCollapseTwoStrings(self):&lt;br /&gt;        self.assertEqual('(a.|.b)', collapse([['a', '.'], ['.', 'b']]))&lt;br /&gt;        &lt;br /&gt;    def testThreeCharsOneMismatch(self):&lt;br /&gt;        l = expand('abc', 1)&lt;br /&gt;        self.assertEqual( [['a', [['b', '.'], ['.', 'c']]], ['.', 'bc']], l)&lt;br /&gt;        r = collapse(l)&lt;br /&gt;        self.assertEqual('(a(b.|.c)|.bc)', r)&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    unittest.main()&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And it seems to work, according to my understanding of what it should be able to do! I'd like to turn on "explicit capture only", to spare the expense caused by all those parentheses, but Python doesn't have that option:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt;&gt; r.findall('tony bony tiny toby tonk toaa blah')&lt;br /&gt;[('tony', 'ony', 'ny'), &lt;br /&gt; ('bony', '', ''),&lt;br /&gt; ('tiny', 'iny', ''),&lt;br /&gt; ('toby', 'oby', 'by'),&lt;br /&gt; ('tonk', 'onk', 'nk')]&lt;br /&gt;&gt;&gt;&gt; &lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115454544584010224?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115454544584010224/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115454544584010224' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115454544584010224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115454544584010224'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/08/generating-regular-expression.html' title='Generating A Regular Expression'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115325032466693054</id><published>2006-07-18T15:16:00.000-04:00</published><updated>2006-07-29T14:00:14.840-04:00</updated><title type='text'>Generating Column Fixtures for Collections Known at Runtime</title><content type='html'>I recently wanted to use FitNesse for regression testing of a set of data provided by the customer, namely, about 200 models for a rich client application with some very poorly written XML serialization code. I wanted to use the Visual Studio's XSD.EXE utility to generate the C# for all the classes, and then verify that serialization and deserialization worked as expected. I could not even be sure that the schema I inherited was complete, so I wanted to read each file using a validation XML parser, then reserialize the resulting object graph, read it in &lt;i&gt;again&lt;/i&gt; and do some round-trip comparisons. Moreover, not all the models were still valid, so they might disappear from the directory, or new models might be dropped there, etc. To make a long story short, what I felt I needed was to be able to model a column fixture, with a "*" where the file name would appear, then generate the rows based on what was now in the directory. The markup would appear something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;!|Validation.BaseFileListingFixture|C:\FitNesse\dotnet|*.*|&lt;br /&gt;|file name|do stuff?|&lt;br /&gt;|*|0|&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and in this case the DoStuff() method would simply compare the file size to 0. Something like that. &lt;br /&gt;&lt;br /&gt;The code works out differently in Java and C# (the former was easier; the latter required me to hack into Fit more than I would have liked). I can send you either one if you drop me a line. I had to eliminate the code from this posting because it ruined the format of the blog; can't have that!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115325032466693054?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115325032466693054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115325032466693054' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115325032466693054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115325032466693054'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/07/generating-column-fixtures-for.html' title='Generating Column Fixtures for Collections Known at Runtime'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115203846029032904</id><published>2006-07-04T14:40:00.000-04:00</published><updated>2006-07-04T14:41:00.303-04:00</updated><title type='text'>Aww!</title><content type='html'>&lt;a href="http://www.anthonynassar.com/leo/00000001.JPG"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 200px;" src="http://www.anthonynassar.com/leo/00000001.JPG" border="0" alt="" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115203846029032904?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115203846029032904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115203846029032904' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115203846029032904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115203846029032904'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/07/aww.html' title='Aww!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115195168576262459</id><published>2006-07-03T14:32:00.000-04:00</published><updated>2006-07-03T16:58:04.446-04:00</updated><title type='text'>Welcome, Leo!</title><content type='html'>&lt;a href="http://photos1.blogger.com/blogger/1104/900/1600/00000004.0.jpg"&gt;&lt;img style="FLOAT: left; MARGIN: 0px 10px 10px 0px; CURSOR: hand" alt="" src="http://photos1.blogger.com/blogger/1104/900/320/00000004.0.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I have now been blessed with two lovely little boys: Carlo, age 4, and Leo, just a few days old!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115195168576262459?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115195168576262459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115195168576262459' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115195168576262459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115195168576262459'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/07/welcome-leo.html' title='Welcome, Leo!'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115132859635606248</id><published>2006-06-26T09:29:00.000-04:00</published><updated>2006-07-04T12:43:14.630-04:00</updated><title type='text'>Generating C# from XML: A Mixed Blessing</title><content type='html'>I was recently confronted with a considerable legacy application (I use "legacy" the way &lt;a href="www.objectmentor.com/resources/articles/WorkingEffectivelyWithLegacyCode.pdf"&gt;Michael Feathers&lt;/a&gt; does: a legacy application is any without tests) that at first seemed to have exploited .NET's lovely support for XML serialization: most of the classes from the domain model had the&lt;span style="font-family:courier new;"&gt; [Serializable]&lt;/span&gt; attribute. It turned out, however, that I could &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; simply instantiate an XmlSerializer for any of these classes, since they all had public properties of type &lt;span style="font-family:courier new;"&gt;System.Type&lt;/span&gt; (which already suggests that the developers who preceded me had some peculiar ideas about OO) or &lt;span style="font-family:courier new;"&gt;IDictionary.&lt;/span&gt; Now, of course, I could have changed the design only minimally in the following fashion:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;[XmlSerializable]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;public class LegacyClass {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;nbsp;&amp;nbsp;[NonSerialized]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;nbsp;&amp;nbsp;public Type ValueType { ... }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;nbsp;&amp;nbsp;public TypeEnum ValueTypeEnum {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;get { &lt;i&gt;based on ValueType property&lt;/i&gt; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set { &lt;i&gt;likewise&lt;/i&gt; }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Similarly, events have to be marked thus:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;nbsp;&amp;nbsp;[field:NonSerialized]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&amp;nbsp;&amp;nbsp;public event BlahHandler Blah;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-family:courier new;"&gt;field&lt;/span&gt; qualifier causes the compiler to apply the attribute to the underlying multicast delegate field. An event is not serializable as such.&lt;br /&gt;&lt;br /&gt;Still, after only half a day, I realized that my effort to leverage .NET's handy code generation for XML serialization of a class was adding more cruft to an already crufty code base, that any unit tests I tried to create as motivation for my new code were compromised by the low quality of the code I was adding to, that I would have to version these additions without knowing what effect they would have on the application as a whole...that I was driving blind, in other words. Our XML wizard advised me simply to write all my custom serialization code (via the &lt;span style="font-family:courier new;"&gt;ISerializable&lt;/span&gt; interface)...and at that point, I knew I was getting into pissing-match territory. I simply don't feel any need to prove myself by writing this code. I'm perfectly happy accepting the output from the VS2003 &lt;span style="font-family:courier new;"&gt;xsd.exe &lt;/span&gt;utility. The only obstacle was that I didn't have an XML schema to work from! All I had was the existing and very unorthodox custom serialization code that was already in place, which navigated a DOM, thus losing the performance advantages of custom serialization.&lt;br /&gt;&lt;br /&gt;So I decided to use the &lt;a href="http://www.dofactory.com/Patterns/PatternMemento.aspx"&gt;memento&lt;/a&gt; pattern: treat XML as a persistence layer, generate a .cs file from a schema that I would have to create incrementally, based on what I could learn about the object graph and interpose a factory class that could populate the memento from the object graph or vice versa. Now all my serialization code is in one place, and testable as such, and I need never break the existing code. All I have to do is deserialize the XML, serialize, create a DOM, and determine what was lost during the round trip. Then I extend the XML schema, regenerate the .cs file for the memento, and repeat as necessary. I sidestep the existing deserialization code, and continue extending my factory until it creates an object graph that works, or seems to work. This latter step is obviously the difficult one, since I don't really know what the application requires in order to work properly! Ultimately, my factory has to reverse-engineer the existing code, but I can avoid destabilizing that code by virtue of having a completely discrete factory class in a separate file, and write unit tests in a strict A/B fashion (i.e., compare the object graph produced by my factory with that produced by the legacy code).&lt;br /&gt;&lt;br /&gt;Then I dump the legacy code and open the champagne!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115132859635606248?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115132859635606248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115132859635606248' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115132859635606248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115132859635606248'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/06/generating-c-from-xml-mixed-blessing.html' title='Generating C# from XML: A Mixed Blessing'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-27708939.post-115081121852021454</id><published>2006-06-20T09:45:00.000-04:00</published><updated>2006-06-27T10:38:52.023-04:00</updated><title type='text'>Running Fit via Visual Studio</title><content type='html'>Robert C. Martin has &lt;a href="http://butunclebob.com/ArticleS.TimOttinger.YesForDebuggers"&gt;written&lt;/a&gt;, "My attitude is that every time I must fire up a debugger, I have failed. Perhaps I have failed to make my code so clear that I don't need a debugger to understand it. Perhaps I have failed to work in cycles that are so small that I don't need a debugger to find out what went wrong. Whatever the reason, when I am forced to use a debugger it means that I need to adjust my practices so that I can avoid using a debugger next time." I agree with him whole-heartedly. The necessity of understanding legacy code (i.e., code not covered by tests) is often given as a counter-example, but the more time I spend debugging code I didn't write, the more I'm moved to bring it under test, for which I've found &lt;a href="http://fitnesse.org/"&gt;FitNesse &lt;/a&gt;indispensable. &lt;a href="http://www.bookpool.com/sm/0321269349"&gt;Ward Cunningham&lt;/a&gt; has likewise recommended writing exploratory tests when many developers would be inclined to single-step through code they haven't seen before.&lt;br /&gt;&lt;br /&gt;Nonetheless, I've felt the need this week to single-step through some legacy code that is exercized by tests I wrote in FitNesse. I willingly concede that this feeling is evidence of some kind of failure, perhaps my own and not just the legacy coders'. The appropriate FitNesse &lt;a href="http://fitnesse.org/FitNesse.DotNet.DotNetTestRunner"&gt;documentation &lt;/a&gt;was not entirely clear to me, perhaps because I needed to better understand how FitNesse works in the first place. It seems obvious now that the test runner would have to get the HTML representing the tests from FitNesse, but I didn't quite grasp that. However, the application that needed to call my code is &lt;span style="font-family: courier new;"&gt;C:\FitNesse\dotnet\TestRunner.exe&lt;/span&gt;.  In order to have TestRunner invoke my code (single-stepping through your own fixtures, let alone TestRunner or the Fit code, takes a little more work), I needed to open the Property Pages for the project and change the Debug Mode setting from "Project" to "Program," after which I could specify the Start Application. I then set the Command Line Arguments to &lt;span style="font-family:courier new;"&gt;-debug -nopaths -v localhost 8080 SerializationSuite.RoundTripSuite Validation.dll&lt;/span&gt;, though &lt;span style="font-style: italic;"&gt;not &lt;/span&gt;all of that is required. My FitNesse server is listening on port 8080, so yes, those options are required. &lt;span style="font-family:courier new;"&gt;-debug&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;-v&lt;/span&gt; lead to more console output, though they might provide you with more insight into what, precisely, TestRunner is doing. FitNesse has to provide the tests, and in this case the tests that drive the problematic code are on the specified page (i.e., I normally see them at &lt;span style="font-family:courier new;"&gt;http://localhost:8080/&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;SerializationSuite&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;...&lt;/span&gt;). Validation.dll is the project output; in this case it contains custom fixtures. The Validation project references the code I want to bring under test; those DLLs are copied to the Validation project by default. If I set the Working Directory to the &lt;span style="font-family: courier new;"&gt;bin\Debug&lt;/span&gt; directory for the project, TestRunner should be able to find all the necessary DLLs. &lt;span style="font-family:courier new;"&gt;fit.dll&lt;/span&gt; and &lt;span style="font-family:courier new;"&gt;fitLibrary.dll&lt;/span&gt; should have been installed to the same directory as TestRunner.exe.  I specified &lt;span style="font-family:courier new;"&gt;-nopaths&lt;/span&gt; in order to avoid any potential collisions with paths set in FitNesse. At this point, TestRunner can find all the DLLs it needs, so I can freely hit F5, just like in the old days, when I was looking for dangling pointers in C++.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/27708939-115081121852021454?l=slideguitarist.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://slideguitarist.blogspot.com/feeds/115081121852021454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=27708939&amp;postID=115081121852021454' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115081121852021454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/27708939/posts/default/115081121852021454'/><link rel='alternate' type='text/html' href='http://slideguitarist.blogspot.com/2006/06/running-fit-via-visual-studio.html' title='Running Fit via Visual Studio'/><author><name>SlideGuitarist</name><uri>http://www.blogger.com/profile/09086439769845185477</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://bp0.blogger.com/_yClxQhTLbco/R56xzJsYZSI/AAAAAAAAABg/W0mj5hpRQzY/S220/Leo,+sleeping+with+bottle.jpg'/></author><thr:total>2</thr:total></entry></feed>
