2013-11-25

Cartesian product

The challenge. Make every combination of the numbers 1 to 5.

Cartesian Product

V5"=r^I1,5)^Mp:sor^M^V)yuPZZ for 24.

Note: If you haven't upgraded your Vim to at least 7.3.272, this solution will not work at all. Before then, the expression register converted lists to strings without a newline at the end. If that happened here, there would be seams where 51 would be on the same line. Not good. This challenge was posted to vimgolf.com seven months before Vim 7.3.272, so you can't blame the early players for not getting it.

Pasting range() from the expression register ("=) to make an array of numbers is a standard golfing trick. We use V so it replaces the blank line, rather than pasting over or under it. The only unusual part is giving it a numeric argument, so it pastes range(1,5) 5 times.

That sets up the right side of the output. You can sort the numbers to get the left side, but that destroys the right side. So we :sort it, block visual yank the result, undo the :sort, and paste the sorted version in front of the unsorted version.

The cool trick is making the spaces. ^VGA ^[ takes 5 strokes... way too long. Instead, we yank nonexistent spaces from beyond the end of the line; I call these "void spaces". Visual mode lets you move one tile beyond the end of a line, and in block visual, yanking or deleting beyond the end of the line adds aligning spaces to the register that weren't in the file originally. The ) command normally moves the cursor forward a sentence, but on sentence-less single-paragraph blocks like this one, in visual mode it moves beyond the end of the last line (unlike }). :help 'virtualedit'

Alternate solutions

"=r^I6,56)^MP^VGA ^[:g/6/d5^MZZ for 26.

Write out all the numbers in range(11,55), remove those with a digit in [06-9], and add spaces with block visual. We'll start the range from 6 though, because it has fewer digits than 11, and the extras will be deleted anyway. Instead of using a stroke-wasting regex like [06-9], we'll just :g/6/d5 to delete 5 lines from every line with a 6. It'll also remove the blank line we didn't remove by pasting the range() from visual mode.

i6^[qqYi ^[p^Aq49@q:g/6/d5^MZZ for 26.

Inserting the space on each line as the macro progresses saves the 2-stroke overhead of making a visual selection. That makes up for the 2 strokes lost setting up an increment macro instead of using range(). As long as you add the space between the yank and the paste, it won't end up on the next line.

Similar challenges

No comments:

Post a Comment