Just seeing this thread—I’ve had the same general thought but never got around to implementing it. The bitwise approach is clever!
My first thought is to use Ruby’s map
method, which is much more concise than the explicit iteration in most of the code posted above:
define :ringand do | c, d |
l = c.length() - 1
return (0..l).map { |x| c[x] & d[x] }.ring
end
define :ringor do | c, d |
l = c.length() - 1
return (0..l).map { |x| c[x] | d[x] }.ring
end
a = (spread 2, 16)
b = (spread 1, 3)
puts a
puts b
puts ringand(a, b)
puts ringor(a, b)
output:
├─ (ring true, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false)
├─ (ring true, false, false)
├─ (ring true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false)
└─ (ring true, false, false, true, false, false, true, false, true, true, false, false, true, false, false, true)
This isn’t as elegant as it could be. I’d prefer to write the function once and pass the Boolean operator as a symbol (similarly to how you’ve done it above, but without having to translate from an intermediate string like "and"
), but Sonic Pi doesn’t seem to like any of my attempts to make that happen. Maybe someone better at Ruby can make it work. And ideally there would be an infix syntax, but that seems like WAY more trouble than it’s worth.
Note that this implementation works fine with rings of different sizes! And perhaps a little more intuitively than the equivalent using bitwise arithmetic. It’s not commutative in those cases, but otherwise I expect it’s mathematically well behaved.