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

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

Привет, коллеги. Продолжаем изучать язык для скриптов! Основы изложены здесь, создание и вызов функций, а также несколько примеров здесь, а теперь обсудим списки и массивы. Список — это множество пронумерованных значений любого типа. Значения перечисляются через запятую в квадратных скобках: :let mylist = [1, two, 3.0, "four"] Списки могут быть вложенными. Доступ к элементам — через квадратные скобки: echo mylist[0]. Нумерация с нуля. Отрицательные индексы отсчитываются с конца. Так, mylist[-2] — это предпоследний элемент; в нашем случае это то же, что и mylist[2], и это 3.0. Есть функция get(list, index, def), которая возвращает элемент списка по номеру, а если его нет, то нуль, либо указанное значение def по умолчанию. А вот индексация несуществующего элемента квадратными скобками — ошибка. Срезы (подсписки) получаются интуитивным методом: mylist[2:-1], mylist[2:], mylist[1:2], mylist[:] и т.д. Если список получился пустой, то значит пустой. Это не ошибка. Поскольку двоеточие исполь

Привет, коллеги. Продолжаем изучать язык для скриптов! Основы изложены здесь, создание и вызов функций, а также несколько примеров здесь, а теперь обсудим списки и массивы.

Не такой... хотя и такой можно тоже.
Не такой... хотя и такой можно тоже.

Список — это множество пронумерованных значений любого типа. Значения перечисляются через запятую в квадратных скобках:

:let mylist = [1, two, 3.0, "four"]

Списки могут быть вложенными. Доступ к элементам — через квадратные скобки: echo mylist[0]. Нумерация с нуля. Отрицательные индексы отсчитываются с конца. Так, mylist[-2] — это предпоследний элемент; в нашем случае это то же, что и mylist[2], и это 3.0.

Есть функция get(list, index, def), которая возвращает элемент списка по номеру, а если его нет, то нуль, либо указанное значение def по умолчанию. А вот индексация несуществующего элемента квадратными скобками — ошибка.

Срезы (подсписки) получаются интуитивным методом: mylist[2:-1], mylist[2:], mylist[1:2], mylist[:] и т.д.

Если список получился пустой, то значит пустой. Это не ошибка.

Поскольку двоеточие используется в именах переменных, то это может мешать: mylist[b:ind] может означать как элемент с номером, который задает переменной b:ind, так и срез от номера b до номера ind. Используйте пробелы для однозначности: [b : ind].

Если переменная — список, и присваивается другой переменной, то обе указывают на один и тот же список, так что изменение одной переменной изменяет и другую. Сделать копию можно через полный срез [:] или функцию copy, но при этом элементы-списки остаются связанными. Функция deepcopy копирует списки И все их элементы, рекурсивно, создавая полностью независимую копию.

Сравнение списков посредством == сравнивает их элементы и возвращает истину, если элементы в списках одинаковые. А оператор is проверяет, указывают ли две переменные на один список или нет:

let a=[42,666]
let b=[42,666]
let c=a
echo a is b "0
echo a == b "1
echo a is c "1
echo a == c "1

Списки можно склеить в один операцией +. Например,

let mylist = [1,2] + [3,4]

Нюанс: при сравнении числа со строкой преобразование не делается, так что это разные вещи (в отличие от сравнения скаляров):

echo [42]==['42'] "0
echo [42]==['42'+0] "1

Список можно "разобрать" по переменным очевидной конструкцией:

let [x,y]=[42,666]

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

let [x, y; list]=[42,666,1,2,3,4]

Вставка элементов в список: функция insert(list, item, n). Элемент получит номер n, отпихнув элемент номер n на единицу вправо.

Можно добавить элемент справа, не вычисляя его номер: add(list, item)

Функция remove удалит элемент по номеру или элементы от одного номера до другого. Можно то же сделать проще: unlet mylist[1] или unlet mylist[2,-1].

Можно профильтровать список, оставив элемент по условию:

filter(list, 'v:val =~ ''\d\+''') "оставить только числа

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

Функции sort, reverse и uniq, соответственно:

  1. сортирует список;
  2. меняет порядок элементов на обратный;
  3. и удаляет дубликаты из списка.

Имеется цикл for, перебирающий элементы списка:

for item in list
...
endfor

Здесь удобна функция range, создающая список: о ней чуть дальше.

Если элементы списка надо только поменять по одному, то лучше использовать map:

echo map([1,2,3], 'v:val*v:val')

Допустима и такая нотация:

for [i,j] in [[1,1],[1,2],[2,1],[2,2]]

Функция empty проверяет список на пустоту.

Число элементов определяет len.

Функция count идет вместе с filter и map и подсчитывает число элементов, подходящих под условие. А index определяет номер первого такого элемента.

Функции min и max находят минимум и максимум в списке.

Функция join соединяет элементы списка, образуя строку; разделитель можно указать вторым аргументом.

Обратное действие имеет split, которая разделяет строку, образуя список. По умолчанию строка делится по пробелам, но можно указать выражение, которое совпадает на разделителях. Например, split('abcd', '\zs') разделит строку на символы. Якорь \zs совпадает везде, вот он после каждого символа и совпадет.

Умелое использование всех этих средств позволяет записывать сложные вещи просто. Например,

execute 'let sum = ' . join(list, '+')

просуммирует элементы списка. Сначала запишем список в строку через плюсик, потом добавим к выражению 'let sum=', и вычислим это выражение.

А вот пилотаж повыше классом:

execute 'let sum = ' . join(map(filter(list, 'v:val =~ ''\d\+'''), 'v:val*v:val'), '+')

Строки считаются списками символов и к ним применима нотация квадратных скобок:

echo 'This is a text'[5:6]

Список можно создать функцией range, у которой от одного до трех аргументов:

range(N) создает [0, 1, ..., N-1]
range(N,M,D) создает [N, N+D, N+2D, ..., M]

По умолчанию D=1. Число D может быть отрицательно: range(2,-2,-1). Эту функцию удобно использовать в циклах: for i in range(12) эквивалентно

let i=0
while i<12
let i+=1

В обоих случаях мы опустили тело цикла и его завершение.

А массивы?, спросите вы. А массивов нет. Их роль успешно играют списки.

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

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

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

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