# Standards

Blogged by Ujihisa. Standard methods of programming and thoughts including Clojure, Vim, LLVM, Haskell, Ruby and Mathematics written by a Japanese programmer. github/ujihisa

## Thursday, May 7, 2009

### About Array#<=> (a.k.a. Spacecraft Operator)

The documentations about `Array#<=>` are:

For example:

``````[1, 2, 3, 4] <=> [1, 2, 3, 3] #=> 1
[1, 2, 3, 4] <=> [1, 2, 3, 4] #=> 0
[1, 2, 3, 4] <=> [1, 2, 3, 5] #=> -1
[1, 2, 3, 4] <=> [1, 2, 3] #=> 1
``````

The implementation in MRI 1.9 is:

``````VALUE
rb_ary_cmp(VALUE ary1, VALUE ary2)
{
long len;
VALUE v;

ary2 = to_ary(ary2);
if (ary1 == ary2) return INT2FIX(0);
v = rb_exec_recursive(recursive_cmp, ary1, ary2);
if (v != Qundef) return v;
len = RARRAY_LEN(ary1) - RARRAY_LEN(ary2);
if (len == 0) return INT2FIX(0);
if (len > 0) return INT2FIX(1);
return INT2FIX(-1);
}

static VALUE
recursive_cmp(VALUE ary1, VALUE ary2, int recur)
{
long i, len;

if (recur) return Qnil;
len = RARRAY_LEN(ary1);
if (len > RARRAY_LEN(ary2)) {
len = RARRAY_LEN(ary2);
}
for (i=0; i<len; i++) {
VALUE v = rb_funcall(rb_ary_elt(ary1, i), id_cmp, 1, rb_ary_elt(ary2, i));
if (v != INT2FIX(0)) {
return v;
}
}
return Qundef;
}
``````

I translated it from C to Ruby literally:

``````class Array
def yet_another_cmp(you)
you = you.to_ary
return 0 if self == you
v = nil
(0...[self.length, you.length].min).each do |i|
v = self[i] <=> you[i]
return v if v != 0
end
len = self.length - you.length
return 0 if len == 0
return 1 if len > 0
-1
end
end
``````

It works exactly the same.

You may think it is easy to write a simpler equivalent implement with `Array#zip`, but unfortunately I found that it was not so simple. The following is the simplest code I can write. Of course it works exactly the same as original `<=>`.

``````class Array
def simple_cmp(you)
self.zip(you) {|x, y|
break if (x && y).nil?
(v = x <=> y) == 0 or return v
}
self.length <=> you.length
end
end
``````

In conclusion, `Array#<=>` itself is complicated one. Enjoy your space travel!

1. Indeed, it's not trivial to compare arrays.

Since you have such a good eye at finding my errors in rubyspecs, I hope you won't mind if note a couple of differences in your "translation".

First, the test for equality should be using equal?, not == (otherwise you'd loop forever!). More importantly, the Ruby code handles recursive arrays, so the ruby translation is actually much more complicated!

Finally, your "zip" version won't work for arrays containing nil values, like [nil, :foo].yet_another_cmp [nil, :bar] # ==> 0, should be 1

Checking the ruby code led me to find a cute quirk of Ruby 1.8.7/1.9. Here's the quiz for you: without writing any method/block/lambda, can you find ways to have:
x == y # ==> true
y == x # ==> false

There are many ways!
I'll post a bug report tomorrow (although I'm not sure they will consider it a bug!)

2. I saw merely the tip of the iceberg! I never imagine about [nil, ...].

Although I had been thinking about the asymmetricity quiz until now, I couldn't find it. At first I suspected the following is the answer, but not.

a, b = [], []
a << a
b << a