I have a large piece of code consisting of thousands of equations. Example of two connective lines, corresponding to lines 998 and 999 is
sum(x[i]*y[i]for i in 1:n) == 15;
sum(z[i]*x[i]for i in 1:n) == 30;
I would like to replace such lines with the following
sum(x[i]*y[i]for i in 1:n) - 15 >= -s[998];
sum(x[i]*y[i]for i in 1:n) - 15 <= s[998];
sum(z[i]*x[i]for i in 1:n) - 30 >= -s[999];
sum(z[i]*x[i]for i in 1:n) - 30 <= s[999];
How to automate this process?
Our first step is to transform each individual line:
sum(x[i]*y[i]for i in 1:n) == 15;
into this:
sum(x[i]*y[i]for i in 1:n) - 15 >= -s[998];
While doing it in one command would be a nice parlor trick, we will do it in several easier to follow steps.
==
to -
Here is our first command, it is very simple:
:%s/==/-<CR>
:
enters command-line mode.%
is the range of lines on which to execute the following command. Here, %
is a shortcut for 1,$
(from line 1 to last line), thus "every line".s
is the "substitute" command, see :help :s
./==
is what you want to substitute./-
is what you want to substitute it with.<CR>
(ENTER) to execute the command.In plain english: "substitute every ==
with -
".
We should get something like:
sum(x[i]*y[i]for i in 1:n) - 15;
sum(z[i]*x[i]for i in 1:n) - 30;
;
to >= -s[XXX];
Here is our second command:
:%s/;/ >= -s[XXX];
Where we substitute every ;
with a generic >= -s[XXX];
. This is also quite simple.
In plain english: "substitute every ;
with >= -s[XXX];
.
We should get something like:
sum(x[i]*y[i]for i in 1:n) - 15 >= -s[XXX];
sum(z[i]*x[i]for i in 1:n) - 30 >= -s[XXX];
XXX
to line number
Here is our third command:
:%s/XXX/\=line('.')
The big change between this command and the other ones is that the replacement part is dynamic. With \=
, we use an expression that is evaluated during each execution instead of a fixed string. line('.')
is a vimscript function that returns a line number, which is exactly what we want between those brackets.
In plain english: "substitute every XXX
with the current line number".
We should get something like:
sum(x[i]*y[i]for i in 1:n) - 15 >= -s[998];
sum(z[i]*x[i]for i in 1:n) - 30 >= -s[999];
Here we duplicate each line with a single command:
:g/^/t.|s/-s/ s
:g
is the :help :global
command./^/
matches every line so the following command will be executed on every line.t
is the :help :t
command..
represents the current line.|
separates two commands.s/-s/ s
removes the -
before the s
on the duplicated line.In plain english: "mark every line, then copy each marked line below itself, then remove that leading -
before the s
".
We should get something like:
sum(x[i]*y[i]for i in 1:n) - 15 >= -s[998];
sum(x[i]*y[i]for i in 1:n) - 15 >= s[998];
sum(z[i]*x[i]for i in 1:n) - 30 >= -s[999];
sum(z[i]*x[i]for i in 1:n) - 30 >= s[999];
We use one last command to add a line between our blocks:
:g/ s[/put=''
s[
is marked.:help :put
to append an empty line.In plain english: "put an empty line after each line with s[
".
We should get something like:
sum(x[i]*y[i]for i in 1:n) - 15 >= -s[998];
sum(x[i]*y[i]for i in 1:n) - 15 >= s[998];
sum(z[i]*x[i]for i in 1:n) - 30 >= -s[999];
sum(z[i]*x[i]for i in 1:n) - 30 >= s[999];
:help :substitute
,:help :range
,:help sub-replace-\=
,:help :global
,:help :t
,:help :put
,:help :|
.