Привет, коллеги. Сегодня короткая заметка на тему удобств и подстройки редактора под себя. Мне оказалось частенько нужно складывать несколько (от трех до дюжины) чисел. Нехитро это сделать, есть тысяча способов, но я всё время работаю в Вим и хочу суммировать числа с минимумом лишних движений.
Вот как я это делаю. В .vimrc помещены функция и команда:
function! Sum(...)
let s=0
for x in a:000
let s=s+x
endfor
return s
endfunc
command! -nargs=+ Sum echo Sum(<f-args>)
И я могу суммировать командой
:Sum 1 2 3 42 666
Как видите, несложно и лаконично. И сама команда весьма удобна: ни знаков плюс, ни скобок, ничего не надо.
Давайте разберем.
На первой строке мы создаем функцию командой function. Восклицательный знак разрешает переопределить функцию, если она уже существует. В данном случае это не так важно. Аргументов у функции может быть сколько угодно (не более 20), они все безымянные и доступны в функции в списке a:000.
На второй строке мы обнуляем будущую сумму.
На третьей запускаем цикл, перебирающий элементы списка a:000, то есть все аргументы.
Тело цикла - четвертая строка; на ней мы суммируем элементы.
Завершаем цикл. Возвращаем сумму. Завершаем функцию.
Команда тоже называется Sum и ключ -nargs=+ означает, что параметр у команды хотя бы один должен быть, но может быть и больше. Смысл восклицательного знака тот же: если команда уже есть, она будет переопределена. Сама команда состоит в вызове функции, которой передаются параметры команды.
Можно перед возвратом суммы сохранить ее в регистре (&s) или глобальной переменной (g:s). В первом случае можно эту сумму потом вставить в текст ("sp в нормальном режиме или <C-R>s в режиме вставки), во втором она просто будет известна Виму и ее можно потом как-то использовать.
Ещё я добавил привязки:
map <F12>s :echo SumL(split(getline('.')))<CR>
imap <F12>s <ESC>:echo SumL(split(getline('.')))<CR>
Они берут строку под курсором, разделяют ее по пробелам на слова (предполагается, что это числа) и передают полученный список функции SumL, которая отличается от Sum только в первой строке (вместо ... стоит L или другое имя списка, который надо просуммировать), а вместо a:000 на третьей строке стоит a:L.
В общем-то, можно теперь тело Sum так переписать: return SumL(a:000).
Для чего всё это? Чтобы можно было просуммировать числа в буфере, на отдельной строке.
Несложно сделать аналогичную привязку для выделения:
:vmap <F12> "wy:echo SumL(split(@w))
Это даже для прямоугольников работает! И можно просуммировать столбик таблицы, например. Или два столбика. Или строку.
Есть ограничение: число аргументов функции ограничено двадцатью. Для команды такого ограничения нет, но команда вызывает функцию. Зато привязки, которые используют функцию SumL, просуммируют и больше двадцати чисел.
Ещё нюанс: обе функции корректно работают с вещественными числами, так что Sum(4.2, 6.66) вернет дробь 10.86. А вот команда игнорирует дробную часть, то есть чисто целочисленная: команда :Sum 4.2 6.66 вернет 10. Дело в том, что <f-args> заключит каждое число в кавычки, а текст преобразуется обратно в число, но целое. Суммируйте так:
let s=s+str2float(x)
и будет всё как надо.
Можно даже косплеить электронную таблицу, суммируя столбцы. Но это уже юмор такой, так как для всего свой инструмент. В качестве шутки рассмотрим и такое. Попозже.
Удачи, коллеги!