Re: [xsl] 99 bottles of beer

Subject: Re: [xsl] 99 bottles of beer
From: Abel Braaksma <abel.online@xxxxxxxxx>
Date: Tue, 06 Feb 2007 00:22:34 +0100
Andrew Welch wrote:

The difference there is that (1 to 2) != 10 returns a sequence of 2 items "true true", whereas (1,2) != 10 returns a single "true". I would have to look it up but I think anything other than a single "true" converts to false, so "true true" returns false.

You could rewrite it as ((1 to 2) != 10) = false() to check the values
in the sequence for any occurance of false(), and return a single
true/false.

I read an reread it, and in either case misread it. But now I see my mistake, and it is pretty simple really (well, it took me a couple of hours, hope that others grasp it quicker ;) Let's put my thoughts to paper.


The idea is (the way I see it), to find all possible combinations with your operator in between. Like this (what is left stays left and what is right stays right):

(a, b) = (c, d)
>  a = c or
  a = d or
  b = c or
  b = d

Which follows the rule as Michael put it in his book: "if any operand satisfies the operator the result of the operation is true". I had trouble following the same logic for !=, but in case anybody is listening in, this is how it finally sank in for me, following the same logic:

(1, 2) != (1, 2)
>  1 != 1 or
  1 != 2 or
  2 != 1 or
  2 != 2

Because at least one of these operations returns true, the result is *true* (in English: a group of John and Jim is not equal to the same group of John and Jim). So, even if it looks, at first sight, extremely odd, using the same logic as with the = operator, it follows that (1, 2) is NOT equal to (1, 2) (but that's why we have the deep-equal function).

This also explains why the following is always propagated as the best alternative for finding unequality for all operands:

not ((1, 2) = (1, 2))
>  not( 1 = 1 or
       1 = 2 or
       2 = 1 or
       2 = 2    )

Which will return *false*, because at least one of these operands is true. Often I read that people intuitively and wrongly expect this not-operation to be the same as the != operation. As a mnemonic, you can reverse the logic created with not(), making it easier to read:

not ((1, 2) = (1, 2))
>  1 != 1 and
  1 != 2 and
  2 != 1 and
  2 != 2

Which (surprise) returns *false* also. I find this second way of 'writing out' easier to read.

To summarize, using the != operator on sequences, will only return 'false' if all operands are equal, in all other situations it will return true:

(3, 3, 3, 3) != (3, 3, 3, 3)
> false

(3, 3, 3, 3) != (3)
> false

(3, 3, 3, (), (), 3) != (3, 3, 3, 3)
> false

The last one shows that items that are an empty sequence are discarded. All above situations are of the spurious kind where reversing the operator also reverses the result (returns true, that is) for when dealing with sequences of more than one item.

Now there's only one more fish to fry, and that is the following: why is (1 to 2) != 10 different then (1, 2) != 10. To tell you the truth, I have no idea. Both left operands are a sequence of two items that (iirc) after atomization is still a sequence of two items, of type xs:integer. I tried another processor (altovaxml) to have a comparison against Saxon. It returned the opposite of Saxon for all question marks below:

(10 to 10) != (10)
> true (?)

(10, 10) != (10)
> false

(10 to 10) != (10 to 10)
> false

(10 to 11) != (10)
> true

(10, 11) != (10)
> true

(1 to 2) != (10)
> false (?)

((1 to 2), (1 to 2)) != (10)
> true

(for $i in 1 to 2 return $i) != (10)
> false (?)

(for $i in 1 to 2 return xs:integer($i)) != (10)
> true


There's seems to be some logic involved that as soon as the 'to' operator is used, the result is different than expected. Adding any cast (xs:integer above, but any other will do) returns the expected 'true'. Why is this cast necessary to get the same result as for normal sequences as when using the 'to' operator?


Thanks Andrew, for the mind exercise, it was fun getting the grips (almost) to some less-than-obvious stuff.

Note that the trick you mentioned, does not work, because the first part (before '= false()') does not return a sequence of all false/true values, it returns, as I see it, a sequence of one item.

((1 , 3) != 4) = false()

returns the opposite of

((1 , 3) != 4) = true()

for all tests above (replace (1, 3) with (1 to 3) to see the effect).


Cheers, -- Abel Braaksma http://www.nuntia.nl

Current Thread