Найти тему
Блокнот математика

Рубрика "Секреты Вим". Скриптовый язык: функции

Привет, коллеги! Это вторая заметка о скриптовом языке Вим.

В предыдущей заметке мы познакомились с основами этого языка. В нашем арсенале есть самое необходимое: переменные, циклы и условия, вывод на экран, арифметика, регулярные выражения, логика. И мы можем выполнять команды Вим.

Мальчик - это функция IRL. Указания - это параметры. Возвращаемое значение - понятно. Побочные эффекты тоже могут быть. А могут и не быть, если приказано не отсвечивать.
Мальчик - это функция IRL. Указания - это параметры. Возвращаемое значение - понятно. Побочные эффекты тоже могут быть. А могут и не быть, если приказано не отсвечивать.

Теперь о функциях. Функция — это самостоятельная программа (скрипт), на поведение которой можно влиять, передавая параметры (иначе называемые аргументами) и которая может вычислять некоторое значение. Некоторые функции делают только это: преобразуют аргументы в значение. Примером служит функция sin(). Другие могут иметь побочные эффекты, например, выводить сообщения или изменять текст в буфере Вим.

Возвращаемое значение есть всегда, но иногда оно игнорируется. Если вам нужно значение, используйте функцию в выражении:

echo sin(1.5)
или
let one = sin(4.2)*sin(4.2) + cos(4.2)*cos(4.2)

Обязательны скобки, даже если аргументов нет.

Если нужны только побочные эффекты, используйте команду call, о которой чуть далее. А определяем мы функции так:

function Name(arg1, arg2)
...
endfunction

Имя пользовательской функции должно начинаться с заглавной буквы. Зато можно использовать нотацию фигурных скобок, как с переменными: все, что в {}, будет вычислено и результат подставлен. Так можно создавать функции с именами, зависящими от переменных или опций. Еще можно использовать префикс s: для функций, локальных для данного скрипта.

Аргументы функции доступны в ней с префиксом a:. Например,

function Fac(n)
let i=1
let s=1
while(i<=a:n)
let s=s*i
let i+=1
endwhile
return(s)
endfunction

Далее можно

echo Fac(6)
или
let b:f6=Fac(6)

Можно и call Fac(6), но это большого смысла в данным случае не имеет: потеряем значение, а побочных эффектов у функции нет. Если перед return поставить echo a:n .'!=' s, то это побочный эффект, и теперь call Fac(6) совершенно нормально. Правда, при использовании в выражении значение будет выводиться на экран, но что поделаешь.

Просто :function покажет имена всех функций, :function f покажет код функции f. А вариант function! позволит переопределить функцию, если она уже существует.

Возвращаемое значение указывается в команде return. Если не указано (просто return) или return вообще отсутствует, то возвращается нуль. Это удобно, если значение означает успех или код ошибки. Можно при успехе return 1 и тогда легко проверить, всё ли прошло гладко. Либо, наоборот, при успехе можно возвращать 0 или ничего не возвращать, а ненулевыми значениями кодировать ошибки.

Переменные внутри функции локальны, если только не снабжены префиксом g:. Таким образом,

let x=6
function X()
let x=7
echo x
endfunction
call X

выведет 7. Глобальная переменная доступна тоже: как g:x.

Если команда :call снабжена диапазоном строк, то функция вызывается на каждой строке. Доступ к этой строке у нее, конечно, есть: функция line('.') возвращает номер строки, функция getline() возвращает строку по номеру (можно тоже '.' использовать для текущей строки). Функция setline(n,v) с двумя аргументами заменит строку с номером n текстом v.

Можем, например, посчитать строки, просто сосчитав в глобальной переменной, сколько раз функция вызвана. А вот пример посложнее:

function Count_words()
let n = len(split(getline('.')))
return n
endfunction

А можно указать слово range после скобок с аргументами, и тогда функция вызывается один раз и сама разбирается с диапазоном. Она получает дополнительные аргументы a:firstline и a:lastline, в которых указаны номера первой и последней строки, и делает с ними что ей надо.

function Count_words() range
let lnum = a:firstline
let n = 0
while lnum <= a:lastline
let n = n + len(split(getline(lnum)))
let lnum = lnum + 1
endwhile
echo "found " . n . " words"
endfunction

Можно создать функцию с переменным числом аргументов. Для этого после обязательных аргументов ставим многоточие. Аргументы доступны под именами a:1 и так далее, и их может быть до двадцати. А a:0 содержит их количество. Пример:

function Sum(x,...)
let s=a:x
let i=1
while i=<a:0
let s+=a:{i}
let i+=1
endwhile
return s
endfunction

Также все дополнительные аргументы доступны в списке с именем a:000. О списках в другой раз.

Функцию можно удалить командой delfunction.

Ссылки на функции существуют: для этого есть функция function(). Она превращает имя функции в ссылку на нее. Эту ссылку можно сохранить в переменной, только имя ее должно начинаться с заглавной буквы. Вызов функции по ссылке осуществляет функция call(), которая принимает ссылку и список аргументов.

Мы не обсудили комментарии. Комментарий начинается от двойной кавычки и простирается до конца строки. Некоторые команды комментария на своей строке не допускают: это echo, map, abbr, execute, обращение к системе через ! (например, !ls) и ещё некоторые команды.

Теперь мы можем писать уже почти полноценные скрипты. Не хватает лишь массивов, списков и словарей, и вещей более сложных, вроде исключений и объектного программирования (да, это тоже есть в зачатке, для эстетов). Зато мы можем создавать функции (иногда это необходимо) и способны выполнять любые команды Вим: для этого всё и затевалось, не так ли?

В следующей заметке обсудим списки.

А пока — ещё пара примеров!

Эта функция переводит число в двоичную форму. Использует индексацию строки как списка символов квадратными скобками. Обратите внимание на взятие остатка (%) и операцию конкатенации.

function Num2Bin(nr)
let n = a:nr
let r = ""
while n
let r = '01'[n % 2] . r
let n = n / 2
endwhile
return r
endfunction

А эта функция выводит и возвращает число в десятичной форме:

function ToDec(nr)
echo(a:nr+0)
return a:nr+0
endfunction

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

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

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