2013-11-14

Line Zipper

Line Zipper is a quality challenge. One of my favorites. This is the sort of zipper we have in mind. If the input was six lines, like a/b/c/A/B/C, the output would be A/a/B/b/C/c.

M:1m+<CR&tg;11@:ZZ

M:1m+^M11@:ZZ for 12.

There are actually two patterns in this file you can use. You can zip the lines based on their position, or join lines together based on their common prefix (the name of the country). The prefix pattern can be expressed in 13 strokes in a couple slightly different ways:

  • qqd*pjq11@qZZ
  • :%no<S-Tab>*ddNP^MZZ

Both methods use * to find the matching line. The first one moves half the remaining file at once. It progresses like this (cursor in bold): a/b/c/A/B/C -> A/a/b/c/B/C -> A/a/B/b/c/C -> A/a/B/b/C/c. The second version runs on every line from 1 to 24, and for each line, moves its match above the line you started from. The progression step-to-step is the same, but you're moving one line across half the file, instead of half the file across one line. (Actually, every time it runs on an even-numbered line, * takes the cursor back to the previous line, which got there in the last step, deletes it, and puts it right back. Whatever works.)

Anyway, that approach is a stroke too slow. Back on topic.

The winning approach ignores the contents of the lines, and uses their positions. This approach starts with the cursor in the middle, and moves a line from one end of the file to two lines away on the other side of the cursor. The cursor moves where the line was moved, so you can repeat with @:. You can go in either direction:

  • M:1m+^M11@:ZZ (the winning 12)
  • *:$m--^M11@:ZZ

The first version starts at line 12/24 (M), and repeatedly moves the current first line two lines beyond the cursor. The second version starts at line 13/24 (it's the same country, so it takes one stroke), and works in the opposite direction.

It may be surprising that + and -- are symmetrical to each other. - is the opposite of . for an :m target; they mean "before the current line" and "after the current line", respectively. -- means "before the previous line", which is opposite to +, "after the next line".

If we use the winning pattern on the sample 6-line file, the macro progresses like this: a/b/c/A/B/C -> b/c/A/a/B/C -> c/A/a/B/b/C -> A/a/B/b/C/c.

Read the manual

  • :help :copy for (very little) info on :t and :m, to copy and move lines around from the command line.
  • :help 10.3 is a gentle explanation of command line ranges.
  • :help cmdline-ranges is the real explanation.
  • :help @: for repeating ex commands like macros. It runs the ": register like you'd expect, but it's as if there's an imaginary : and ^M around it.
  • :help linewise might clarify the behavior of d* in the alternate solution.

Similar challenges

No comments:

Post a Comment