Найти в Дзене
Блокнот математика

Рубрика "Секреты Вим". Сумматор

Привет, коллеги. Сегодня короткая заметка на тему удобств и подстройки редактора под себя. Мне оказалось частенько нужно складывать несколько (от трех до дюжины) чисел. Нехитро это сделать, есть тысяча способов, но я всё время работаю в Вим и хочу суммировать числа с минимумом лишних движений.

https://suplicio.ru/wp-content/uploads/images/stories/37/1.png
https://suplicio.ru/wp-content/uploads/images/stories/37/1.png

Вот как я это делаю. В .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)

и будет всё как надо.

Можно даже косплеить электронную таблицу, суммируя столбцы. Но это уже юмор такой, так как для всего свой инструмент. В качестве шутки рассмотрим и такое. Попозже.

Удачи, коллеги!

Научно-популярные каналы на Дзене: путеводитель
Новости популярной науки12 марта 2022

Наука
7 млн интересуются