Алексей Кутумов, Вектор с нуля
Что я хочу показать:
Представим, что мы разработчики стандартной библиотеки C++, что нам нужно от стандартной библиотеки, чтобы мы смогли реализовать std::vector.
В этом докладе мы возьмем только компилятор (я делал с помощью компилятора из MSVS 2015 – cl.exe версии 19). Он довольно хорошо умеет C++11 и, возможно, что-то из C++14. И сами построим свою стандартную библиотеку.
Вообще, изначально, я хотел сделать основной целью доклада мысль, что реализовать std::vector – это просто. Но потом я подумал, и понял, что это неинтересно. Ну, во-первых, это действительно просто сделать (как мне кажется), ну а во-вторых, это не совсем интересно. Понятно, что упрощение реализации неизменно ведет к сужению кейсов использования, или ухудшении производительности, ухудшении гарантий и т.д. В конце концов, int* x = new int[10], чем не вектор?
Итак, какой у нас план:
Сначала мы осмотрим интерфейс нашего вектора – только декларацию, и посмотрим, что-же из стандартной библиотеки нам нужно, чтобы задекларировать интерфейс вектора.
Затем, мы будем реализовывать вектор. Придумаем такую штуку как vector_base, поймем, для чего она нужна.
После этого, мы пойдем реализовывать вектор потихоньку.
Начнем сначала с методов, которые не изменяют размер вектора.
Потом реализуем метод reserve, поговорим подробнее о гарантиях, которые он дает, и как мы их достигаем.
Потом реализуем метод insert, который вставляет один элемент, поговорим о его гарантиях.
Затем реализуем insert с парой итераторов.
Затем реализуем insert с n элементами.
После этого, реализуем erase с парой итераторов.
А после этого, внезапно, реализуем весь остальной функционал вектора.
Да, и еще, если не сказано обратное, то по умолчаию считаем, что мы находимся внутри пространства std
Итак, тут нам все понятно – два параметра шаблона.
Первый параметр – это тип объектов, которые будут храниться в векторе
Второй параметр – это аллокатор, он имеет значение по умолчанию – стандартный аллокатор.
Итак, мы плавно переходим к обсуждению аллокаторов.
От аллокатора требуется совсем немного, суметь выделить и удалить память, ну и кроме этого, объявить тип value_type. Кто нибудь смотрел доклад Александреску про аллокаторы? Поднимите руки.
Итак, вопрос, кто нибудь знает, зачем изначально были придуманы аллокаторы? Изначально аллокаторы были придуманы Александром Степановым для того чтобы контейнеры не зависели от модели памяти (если кто знает, то в 16-битном режиме есть разные типы указателей, near pointer, far pointer). Как раз аллокаторы абстрагировали это знание предоставлением нужного типа указателей и механизма аллокации и деаллокации памяти.
В С++11 ввели понятие minimal allocator interface, теперь от аллокатора требуется совсем немного обязательных вещей:
Объявить value_type – тип объектов, которые аллоцируются этим аллокатором.
Объявить функции allocate и deallocate для управления памятью.
Итак, с аллокатором тоже все понятно. В принципе, мы можем даже предоставить реализацию этого аллокатора
Итак, давайте поговорим про типы, которые вектор предоставляет клиентам. Я опустил заголовок класса и разбил декларации типов на два слайда.
Итак, на этом слайде представлены типы, которые отдаются на откуп реализации.
Давайте вспомним, какие требования стандарт предъявляет к этим типам?
Давайте сначала поговорим про difference_type и size_type
Какие требования стандарт предъявляет к двум следующим типам?
difference_type – это должен быть тем же самым типом что и тип, значение которого возвращается при вычислении разницы двух итераторов.
size_type – это тип, который может представить любое неотрицательное число типа difference_type
Кто знает, какие требования к типу итератора предъявляет стандарт? Итератор должен удовлетворять модели (или концепту) RandomAccessIterator.
Что самое простое подходит – обычный указатель, его и возьмем.
В принципе, нас эта полностью устраивает.
Я уверен, многие из вас знакомы с std::reverse_iterator, поднимите руки, кто знает что такое reverse_iterator?
Отлично, тогда, я думаю, нет смысла подробно объяснять что это такое – это адаптер, который сам является итератором – он определяет операции инкремента, декремента, сравнения и т.д. Но при этом, операции инкремента и декремента работают противоположным образом по сравнению с базовым итератором – в обратную сторону.
allocator_traits – относительно новая сущность, которая появилась в стандарте C++11. Что она из себя представляет, как и любые traits она позволяет получать и использовать различные свойства аллокаторов – определяемые типы, вызывать методы аллокатора и пр. единым способом.
Давайте посмотрим на интерфейс, сначала типы, причем типы мы тоже разделим на несколько частей.
Сюда же относятся и типы difference_type, void_pointer и const_void_pointer, но я не стал их указывать, так как места на слайдах не очень много, но мы про них помним.
Итак, что нам говорит стандарт – первые два типа – тут все понятно, откуда взять остальные типы? У кого есть какие идеи?
На самом деле стандарт говорит следующее – если тип Allocator предоставляет нужный тип, то нужно взять его, иначе нужно взять другой тип, причем для каждого типа стандарт говорит что нужно брать.
Вот давайте разберем pointer – про него стандарт говорит следующее.
Ну вот, давайте разберем, как это можно реализовать на примере pointer
Собственно, в приведенном примере я так и реализовал с помощью detail::get_pointer.
Давайте на него посмотрим
Здесь приведены декларации двух шаблонных статических методов deduce_type.
Первый метод принимает на вход int и возвращает Alloc2::pointer.
Второй метод принимает на вход нечто по имени wrap_int и возвращает другой тип – указатель на value_type.
Ну и в самом низу мы декларируем тип type на основе типа возвращаемого фукнцией select_type
Итак, осталась пара вопросов, почему вторая функция select_type принимает на вход аргумент типа wrap_int и что это за аргумент?
Ну здесь на самом деле все просто, wrap_int – это структура, у которой объявлен один единственный конструктор, принимающий на вход int. Для чего это сделано – для того чтобы управлять процессом выбора перегруженной функции, в случае если обе функции подходят.
Смотрите, если наш Allocator предоставляет тип pointer, то первая функция будет определена, точно также будет определена и вторая функция. Более того, вторая функция всегда будет определена, потому что тип value_type* всегда определен – стандарт требует наличие value_type. Таким образом компилятор встает перед нелегким выбором, ему нужно выбрать одну из функций, вот он и выбирает ту, у которой входной аргумент получается явно, без неявных конверсий (и вызовов конструкторов).
Кстати, может кто нибудь сказать, как еще можно управлять процессом выбора перегруженной функции?
Ну можно вместо wrap_int воткнуть … — эллипсис. Но мы использовали wrap_int, потому что он нам пригодится в еще одном месте, там где эллипсис использовать нельзя.
Кто нибудь знает, для чего эти типы нужны?
Эти три типа определяют поведение контейнеров при вызове оператора копирующего присваивания, при вызове оператора перемещающего присваивания, и при вызове метода swap.
Собственно, механизм определения этих типов точно такой же как и остальных типов allocator_traits, SFINAE магия решает. Этиы типы могут принимать значения либо true_type либо false_type
Теперь, давайте разберем. Допустим propagate == true_type, тогда аллокатор участвует в операции. Это может быть нужно, если аллокатор имеет состояние (например, держит какую-то арену), тогда при копировании он может сделать новую арену, или же расшарить существующую и т.д.
Если propagate == false_type, то аллокатор не участвует в операции, это может быть полезно, если аллокатор не имеет состояния, например, зовет malloc и free. Такому аллокатору нечего копировать.
Первые два метода обязаны предоставляться аллокатором, поэтом реализация просто их зовет.
С остальные методами ситуация следующая: если аллокатор предоставляет такие методы, то позвать их, иначе позвать стандартные реализации.
Если с первыми 4 методами все более или менее поняно, то последний метод стоит обговорить отдельно.
Итак вопрос, кто нибудь знает для чего нужен этот метод?
Этот метод используется в контейнерах при реализации копирующего конструктора. Он определяет, каким образом нам копировать аллокатор. В принципе, мы опять, можем не копировать аллокатор, а создать новый и его вернуть. Или же сделать что-то еще.
Идея реализации абсолютно аналогична выбору типов. Точно также SFINAE магия и приоритет перегруженных методово позволяет нам выбрать нужный.
Я намеренно не обсуждал многие вещи, иначе мы бы до конца презентации обсуждали их, а нам надо еще реализацию вектора сделать.
Если кому интересно, я могу после доклада, в кулуарах рассказать, как реализуются те или иные сущности в стандартной библиотеке.
Мы еще не рассматривали методы вектора и пр, при рассмотрении методов еще добавится парочка сущностей.
Что такое vector_base, зачем я выделил его?
На самом деле все просто. Для того чтобы реализовывать вектор нужно определиться с его представлением. Как мы будем хранить начало вектора, как будем хранить размер, как будем хранить диапазон выделенной памяти?
Кроме этого в этом классе мы соберем утилитарные функции по управлению вектором.
Сначала конструкторы, операторы присваивания и деструктор.
Здесь мы, кроме всего прочего определяем и внутреннее представление вектора, мы берем три итератора – итератор на начало, на конец данных и на конец выделенной области.
Кстати, кто нибудь может сказать, зачем я здесь использую приватное наследование от аллокатора?
Это для оптимизации. Есть такая оптимизация Empty base class optimization, если базовый класс пустой, то реализация вправе не выделять под него память при наследовании. Таким образом, sizeof(vector_base) будет равен 3* sizeof(pointer) в случае, если аллокатор пустой.
Заметьте, что такого нельзя добиться если аллокатор объявить членом. Так как стандарт требует чтобы объект (даже пустой) всегда имел уникальный адрес.
Первый конструктор делегирует второму.
Второй конструктор копирует аллокатор и выставляет m_first, m_last и m_endOfCapacity в значения по умолчанию (0).
Третий конструктор – забирает у other его указатели и аллокатор.
Четвертый конструктор забирает у other указатели, и ставит новый аллокатор.
Реализация статических методов swap_base и tidy тривиальна.
В первом случае мы свопаем указатели, во втором случае мы их зануляем.
Здесь нет ничего сложного
В принципе, они тоже довольно тривиальны.
В первом случае мы освобождаем this если он чем то владел
Затем обрабатываем аллокатор. Помните, мы обсуждали, что в allocator_traits задается стратегия как использовать аллокатор при move присваивании, вот здесь мы это обрабатываем.
Ну а дальше просто – мы свопаем this и other, а потом обнуляем other.
Оператор копирующего присваивания еще проще, в нем мы обрабатываем только аллокатор, все остальные действия будет производиться в другом месте.
Мы диспетчеризуем вызов двум другим функциям. И в зависимости от типа мы либо муваем аллокатор, либо вообще ничего не делаем!
Точно также реализован и propagate_on_copy_assignment.
На самом деле, 2 из 3-х функций вам должны быть знакомы. Они практически полностью повторяют функции std::uninitialized_copy и std::unititalized_fill_n.
Две остальные функции –unitialized_construct_a – дополнительная вспомогательная функция, котора тоже нам понадобится.
Как мы уже видели ранее, в реализации construct, там вызывается placement new.
Все остальные функции реализуются аналогичным образом.
Один вопрос, для чего здесь используется функция addressof?
Все очень просто – тип, по которым итерируется итератор FwdIt может иметь перегруженный оператор &, и поэтому записать &*c будет некорректно. Функция addressof как раз предназначена для получения адреса из ссылки.
Важный вопрос, какую гарантию относительно возникновения исключений дает эта функция?
Эта функция дает сильную гарантию. Если возникнет исключение, то функция оставит входной диапазон неизменным относительно начала выполнения этой функции. То есть никакого эффекта не будет.
Кстати, это касается и других функций, это очень важно, так как эти функции являются строительными кирпичиками для нашего вектора.
Что касается стандартной библиотеки, то вот несколько новых сущностей, которые мы добавили в библиотеку.
Что касается последних двух функций, то их легко получить из _a версий, просто предоставив стандартный аллокатор в качестве аргумента. И это будет удовлетворять стандарту.
Практически все функции однострочные.
Звездочками я обозначил либо const, либо cr префиксы для функций begin и end.
Начнем с reserve
Проверили входные аргументы, потом выделили память для нового хранилища.
И потом позвали функцию relocate, которая переместит правильным образом наш вектор в новый регион памяти.
Кстати, теперь нас интересуют гарантии исключений для этой функции. Кто помнит, какие требования в стандарте?
Сильная гарантия – если возникнет исключение в конструкторе копирования объектов, или при выделении памяти то функция не имеет эффекта.
Если же возникнет исключение в move конструкторе, то поведение не определено. Ну это и понятно, если конструктор копирования не изменяет исходный объект, то move конструктор изменяет, и нет гарантии, что мы сможем вернуть объект в изначальное состояние.
Текущая реализация не ухудшает сильной гарантии. Остается только посмотреть на реализацию relocate.
Как мы помним, функция uninitialized_copy предъявляет сильную гарантию, но здесь мы делаем финт. Мы делаем move_iterator из обычного итератора, таким образом, вместо копирования будет производиться перемещение, если оно доступно.
Таким образом, мы вполне легально можем переместить все объекты без копирования, что обычно дешевле.
Ну и в конце остается рутина – очистить и удалить старый регион и переназначить указатели на новое хранилище.
Таким образом, если у типа есть move конструктор или move оператор присваивания, то именно он и вызовется.
На самом деле, я не реализовывал basic_string, вместо этого я реализовал шаблонный конструктор у length_error, который принимает любой аргумент, в том числе и строку.
Давайте вспомним какие требования к безопасности к ним предъявляет стандарт.
Стандарт говорит, что если вставляем один элемент в конец и он CopyInsertable или move конструктор не кидает исключений, то в случае исключения функция не имеет эффекта — строгая гарантия. Во всех остальных случаях – базовая гарантия, инварианты вектора не нарушены, состояние не определено, но валидно.
Кто может сказать, для чего в 4-й декларации используется enable_if? Чтобы различить 3 и 4 метод, например, позвали insert(pos, 0, 0). Какую функцию выбрать? Компилятор не сможет. И вот в этом случае нужен наш enable_if.
Итак, давайте посмотрим на интерфейс и подумаем, может мы сможем выразить некоторые методы через другие.
Благодаря тому, что у нас есть perfect forwarding, то мы можем одинаково передавать любые типы ссылок. Чем мы и воспользовались здесь. То есть две реализации свелись к одной.
Вторая часть insert – вставляет диапазон.
Я здесь не стал указывать пятую реализацию, которая принимает пару итераторов. Ее реализация не менее тривиальна чем представленные здесь.
Отдельно стоит сказать про последнюю реализацию. Изначально казалось, что она ближе к первым двум, но на самом деле нет, и я сейчас объясню. Мы можем сделать специальный итератор, который просто считает свою позицию, и при разыменовании всегда возвращает один и тот же элемент.
Таким образом, мы можем сделать такой итератор и передать его в insert_range.
Ну и операции инкремента и декремента оператора сответственно, уменьшают или увеличивают значение счетчика.
Таким образом мы получаем хитрый итератор, который всегда возвращает одно и тоже значение
Кстати, какая категория итератора может быть у этого итератора – random access iterator?
Итак, что мы делаем:
Вызываем метод grow, запрашиваем новый размер.
Потом конструируем один элемент в КОНЦЕ вектора, увеличиваем размер вектора.
Затем вызываем функцию из стандартной библиотеки rotate, которая меняет местами элементы в последовательности так, что нужный элемент оказывается на своем месте.
Ну вот и все!
Давайте посмотрим более детально.
grow – это очень простой метод, он по сути реализует стратегию роста capacity вектора. По сути он вычисляет новое значение capacity, так чтобы оно было не меньше запрашиваемого и текущего capacity, и потом зовет reserve.
Reserve как мы помним, дает строгую гарантию.
unitialized_construct_a – мы уже разбирали, он конструирует объект в памяти.
После этого мы увеличиваем размер вектора на 1.
А потом, происходит самая магия, мы вращаем элементы в векторе так, чтобы последний элемент встал на место first + offset (то есть на место нашего pos), а все последующие элементы встанут за ним в том же порядке, в котором они и были.
Вспоминаем про наши требования – вставка в конец одного элемента должна иметь строгую гарантию.
Вставка в конец элемента – это значит, что pos == end(), мы помним, что вставляем всегда перед позицией.
А это значит, что элемент уже на своем месте, и ничего вращать не надо!
Получается, что все условия соблюдены!
Итак, реализация алгоритма rotate довольно простая:
rotate на вход принимает 3 итератора, начало и конец, и еще один параметр – новое начало, элементы от new_firtst до end будут идти перед элементами [first, new_first).
Вот я здесь привел картинку, которая, как мне кажется поясняет работу алгоритма.
Я не буду приводить реализацию этого алгоритма, она довольно простая.
Вообще говоря, функция rotate имеет базовую гарантию, но в случае, когда ничего делать не надо – эта функция как раз ничего не делает.
Гарантии здесь те же самые.
Кстати, кто может сказать, какое здесь сделано допущение?
Мы используем std::distance, вообще говоря, distance определена для InputIterator. Но InputIterator вообще говоря только однопроходные, то есть повторно итерироваться нельзя. Поэтому здесь надо диспетчеризировать по категории итератора.
std::rotate, которая в свою очередь зовет iter_swap
distance, для реализации метода insert_range – для подсчета расстояния между итераторами.
is_base_of, для определения категории итератора
Стандарт требует только базовой гарантии для методов.
Мы опять используем фокус с rotate, чтобы ненужные элементы переместить в конец. После того как мы переместили в конец, мы зовем у них деструкторы и обновляем m_last, заметьте, capacity мы не трогали.
type_traits, algorithm, utility, memory, iterator. В общем всех подсистем понемногу.
Также почти 1000 строк кода у меня заняли тесты на вектор.
Владимир Васильев: Восточный вектор может ослабить связи Дальнего Востока с Москвой
Большое интервью с главным научным сотрудником Центра внутриэкономических исследований Института США и Канады РАН (Москва)
О возможных негативных последствиях «восточного вектора» России — отток кадров в Азию, а прежде всего в Китай, опасное ослабление экономических связей дальневосточных регионов с Центральной Россией — в эксклюзивном интервью ИА PrimaMedia с Владимиром Васильевым, доктором экономических наук, главным научным сотрудником Центра внутриэкономических исследований Института США и Канады РАН (Москва). В этом же интервью — о провале «западного вектора» России, неоколониализме Европейского Союза, планах расколоть РФ на 17 независимых государств, экономической расстыковке Китая и США и возможностях России в этой ситуации.
Интервьюер: журналист Антон Ефимов
От Лиссабона до Владивостока…
— Владимир Сергеевич, у вас колоссальный опыт, огромные знания. Вы наблюдали самые разные векторы развития нашей страны, как и политические, так и экономические. Вот недавно министр иностранных дел Сергей Лавров заявил, что лживая политика коллективного Запада позволяет России развиваться в восточном и южном векторах. Меня, конечно, больше всего интересует восточный вектор. Азия. Индо-Тихоокеанский регион. Но интересно услышать, а почему же провалился «западный вектор»? Есть ли какие-то глубинные причины?
— То, что заявил министр иностранных дел — констатация нынешнего положения. На протяжении последних 30 лет внешняя политика, а прежде всего внешнеэкономическая политика России была ориентирована на Запад. На то, что сегодня мы можем назвать «коллективный Запад». Таким образом, в постсоветской России произошла перестройка всей политической, социальной, экономической системы. И апофеозом этой интеграции, вернее — нашего настойчивого движения в ту сторону стало то, что была провозглашена единая зона безопасности и сотрудничества от Лиссабона до Владивостока. Мы делали всё, чтобы создать единое информационное, экономическое, образовательное и так далее пространство с тем самым коллективным Западом.
— Примечательно, они готовы были иметь с нами экономические связи, но политически мы ведь были для них неприемлемы все эти 30 лет. Они поддерживали дестабилизирующие процессы внутри России, поддерживали антироссийскую внешнюю политику в странах бывшего СССР, дошли в итоге до поддержки русофобии, нетерпимого отношения к россиянам, а прежде всего — к русским, докатились до вандализма по отношению к российской культуре. Ведь все эти годы всё это дремало в их головах. А мы, получается, были недальновидны? Наивны в своём стремлении на запад?
— Причина этого, с моей точки зрения, состояла в одном. Мы, сближаясь с Западом, не учли один очень важный аспект. Это европейский менталитет. Который даже американизация не смогла сломать.
Старые европейские скелеты в шкафу. Неоколониализм
После Второй мировой войны Западная Европа шла в фарватере США, она американизировалась. Американцы сделали всё, чтобы Европа была не просто встроена во внешнюю политику США, а была именно обращена в Америку, то есть, именно «американизирована». Прежде всего, это американская трансатлантическая система ценностей, которая должна была заменить сугубо европейскую. «Сугубо европейское» американцам было очевидно — это зачастую непреодолимые противоречия, серьёзные геополитические амбиции великих европейских государств, борьба за колонии. Европа являлась источником двух мировых войн, и они носили сугубо европейский характер, отражали противоречия между европейскими странами. И в США посчитали, что старая система европейских ценностей — амбиции, национальное самосознание европейских стран — должна быть устранена. Как следствие — в 1990-е годы европейские страны стали объединяться в Европейский Союз. Началось создание Соединённых Штатов Европы. Но если США — это союз территорий, то Евросоюз стал мыслиться как союз наций. С течением времени эта идея оказалась не очень жизнеспособной (выход из ЕС Великобритании, многие внутренние противоречия — прим. ред.), но важно другое.
Вдруг дали о себе знать старые европейские «скелеты в шкафу». Европа же была не только колыбелью мировых войн, но именно Европа и создала колониальную систему. Это важно понимать. Вторую мировою войну, её причины, цели участников тоже можно рассматривать как стремление стран Европы расширить свои колониальные владения.
И когда Европа, чего не было в её истории, стала объединённой, возродилась отнюдь не американская идея воссоздать в новом виде колониальную систему.
— Идея из тёмного подсознания Европы…
— Да, и в данном случае восток Европы — Прибалтика, Балканы, Румыния, Чехия, Польша, а также бывшие республики СССР, а дальше и сама Россия — должны быть неким сырьевым придатком, чем-то осваиваемым, колонизируемым. Эти территории должны Европе служить, обеспечивать её высокий жизненный уровень.
— Тут и натягивание НАТО на восточные земли.
— Украинский кризис — прим.ред, если что и показал (сейчас мы находимся в очень неопределённой турбулентной ситуации), то только то, что их ставка на восточную Европу как топливо для Европы западной — это верная ставка, так и надо им действовать. Потому что, если бы эта концепция была реализована в полной мере, многие вопросы, тревожащие Евросоюз, отсутствовали бы. А именно — не было бы энергетического кризиса, кризиса с поставками продовольствия, с поставками цветных металлов, удобрений и прочего.
— Но произошло столкновение с Россией и колониальные планы начали рушиться.
— То, что сегодня наша дипломатия говорит о том, что мы не можем вернуться к делам как прежде, как раз отражает наше представление о Евросоюзе и его целях. У нас иногда думают, ну ладно, сейчас там что-то изменится и опять вернёмся к старым отношениям.
С моей точки зрения — не вернёмся, потому что Европа и европейские политики понимают, что вот это вот равноправие между ими и нами — никаких благ им не несёт. Евросоюзу нужно строить свои отношения с её восточными соседями по лекалам колониальным.
На треугольнике «Индия — Китай — Юго-Восточная Азия» будет находиться гравитационный центр мировой экономики
И естественно, что наши правящие круги обратили внимание сегодня и на юг, и на восток. Особенно на Китай. Если посмотреть экономически, тезисно на все макроэкономические тренды, то можно сказать, что есть представление о том, где находятся центры гравитации мировой экономики. Вот когда пришла глобализация, пусть даже это несколько ментальная конструкция, нежели реальная, выяснилось, что в начале этой глобализации, где-то в 1990-х, гравитационный центр мировой экономики находился в Европе, США, а сейчас он действительно стал очень смещаться в сторону Индо-Тихоокеанского региона. Проще говоря — в Азию. И вот эта гравитационная сила в течение нескольких десятилетий сместится в Азию полностью. Вот где-то на треугольнике «Индия — Китай — Юго-Восточная Азия» будет находиться гравитационный центр мировой экономики. И эта тенденция является объективной.
И, наверное, мы бы и так, без всех конфликтов, смещались в сторону Азии, шли восточным вектором, но вмешались ещё и внешнеполитические факторы. И движение на восток стало просто потребностью.
Восточный вектор — дело сложное и выходит далеко за пределы одних лишь товаров
— Какие сложности и опасности нас ждут на Востоке?
— Мы пока ещё в начале пути на восток. Востоковеды говорят об одной очень важной проблеме. Вопрос переориентации экономических связей, это не просто разговор о номенклатуре товаров и услуг, это очень важный культурный сдвиг. Сегодня контрагенты должны быть и культурно встроены в Азию, и иметь соответствующую специализацию. Грубо говоря, хватит учить английский, пора учить китайский. Или хинди. Или какие-то другие азиатские языки. А это уже требует системы образования. Наши бизнесмены тоже отмечают, что контрагенты из Японии, Китая, Кореи заинтересованы в том, чтобы говорить на одном языке. Им важно иметь дело с людьми, которые понимают их культуру, их менталитет. Без этого на нас будут смотреть прохладно. Это будет дорога в никуда. Таким образом, восточный вектор — дело сложное и выходит далеко за пределы одних лишь товаров.
К тому же, вложив огромные силы в изучение языков, культур и прочее — не так-то просто будет потом с этих регионов сойти, отношения будут крепкими, на протяжении поколений. Поэтому, объявив несколько по-кавалерийски, с наскоком, восточный вектор, должны быть предприняты усилия и для скорейшей подготовки квалифицированных кадров.
Процесс расстыковки экономик США и Китая — возможность для России закрепиться в Азии
— У нас образование стоит на европейских рельсах. Болонскую систему зачем-то ввели, очень её все критиковали. Сейчас отказываемся. Ну допустим, справились. У нас сильные научные школы, высококомпетентная профессура. Мы вполне в состоянии выстроить систему образования, предусматривающую широкий спектр знаний об Азии. А как справиться с такой вещью как зависимость Азии от доллара, от финансов США? Мы же там почти ничего не весим со своим рублём.
— Да, в этих странах уже сложились свои связи, свои привычные торгово-экономические связи. А нам, чтобы тут зарабатывать очки, нужна маркетинговая стратегия и стратегия вытеснения конкурентов. И вот здесь очень интересный момент. Америка, может быть, с Трампа (но эту эстафету в той или иной степени переняла и администрация Байдена), стала заниматься расстыковкой экономик США и Китая, чтобы уменьшить зависимость американской экономики. Понизить значение этих связей. Это делается, главным образом, из-за недопущения Китая к технологиям, в чём заинтересован Китай. Это соображения уже не экономические, это соображения национальной безопасности. Американцы забили тревогу. Но выяснилось, что другие страны, Тайвань тот же, Япония, Южная Корея, Сингапур не очень-то спешат расстыковываться с Китаем. Даже наоборот. Они дали понять, что если вы, американцы, уходите, то давайте мы вашу нишу в экономических связях с Китаем займём. И вдруг выяснилось, что Китай здесь очень хорошо работает, находя партнёров и заполняя ниши, откуда уходит США.
«Восточный вектор» американской экономики привёл к падению зарплат в США
— Как Китаю удалось так вырасти экономически и стать не просто конкурентом, а угрозой для США? У США же тоже был «восточный вектор».
— В экономике есть понятие издержек. Затраты при выпуске продукции. Традиционно считается, что наукоёмкое, высокотехнологичное производство должно обеспечиваться высококвалифицированными работниками, которые требуют высокой заработной платы. Это корреляция. Так вот Китай и, может быть, вообще Азия изобрели другой механизм. Там высокие технологии делались людьми, скажем, с не очень высокой зарплатой. Вот этого Запад не ожидал. В этом и состояло главное конкурентное преимущество Китая, и оно ещё будет оставаться у Китая в обозримом будущем. Когда квалифицированные, но не высокооплачиваемые работники производят высокотехнологичную и конкурентную продукцию. И вот тут встаёт вопрос у американцев — а что с этим делать?
Вот этот восточный вектор американской экономики привёл к тому, что стали падать заработные платы в самих США, что было беспрецедентно. Ну перенесли в Китай производства, где платить работникам надо меньше, чем в США. И в США зарплаты упали.
Все американские крики о том, что китайцы присваивают себе чужие идеи, интеллектуальную собственность — правда. И это не в замочную скважину посмотреть или сфотографировать, как в кино про шпионов. А просто купить, предложив большие средства, чем люди внутри США зарабатывают.
«Восточный вектор» может привести к оттоку кадров в Китай
— У нас тоже такое возможно в рамках «восточного вектора»?
— У нас в основном сырьё, и вариация в зарплатах будет не очень велика, но когда мы будем говорить о технологиях, все же ждут укрепления этих связей, и что они приведут к экономическому росту в России, а может случиться с точностью до наоборот. Требование повышения наукоёмкости, или как я иногда говорю — «мозгоёмкости», выпускаемой продукции может привести, в том числе, к утечке кадров. И уже нечто подобное мелькало в СМИ, что наши учёные где-то что-то предлагали Китаю и не за красивые глаза. Более того, то же самое прошло и по США: кто-то где-то стал работать на китайцев. И они преуспели.
И известна тенденция, что на Дальнем Востоке и в Сибири некоторые специалисты пакуют чемоданы и уезжают жить в Китай. И наша стратегия восточного вектора, конкурентной борьбы может где-то немножечко быть подрезана, когда мы увидим действие механизма оттока кадров в условиях глобализации экономики.
— Из бесед с другими экспертами знаю, что в научно-технической сфере Китаю от нас мало что нужно.
— Это ещё один важный аспект. И да, он уже был затронут в одном из ваших предыдущих интервью. Дело не только в засилье доллара в Азии, и не только в том, что кто-то кого-то хочет закабалить, а они и не против — возьмите нас в холопы. Нет. Мои личные разговоры с китайскими товарищами лишний раз подтвердили, что для них важно иметь отношения со странами, которые стремятся к научно-техническому прогрессу, вот почему китайцы стараются закрепиться в Америке, почему мостят дорогу в Европу. Китайцы не ждут, когда новые технологии распространятся к ним, а сокращают это время до минимума. Российские специалисты, с которыми я общался, говорили, что производственные возможности Китая сегодня таковы, что получив какие-то образцы, они имеют возможность запустить их в серию. Все экономисты понимают, что если у вас есть производственные мощности соответствующие, то вы с небольшими модификациями можете запустить продукцию в серию и даже быть впереди планеты всей, или не на много отстать. Или перебить качество количеством.
Были у меня и такие полуприватные разговоры с китайцами, и они откровенно говорили, что Россия сегодня мало чего может нам дать в плане научно-технической прогрессивности, технологий. А вот в военных технологиях… но это, как вы понимаете, вещь коварная.
«Восточный вектор» может привести и к ослаблению связей дальневосточных регионов с центральной Россией
— Это же в конечном итоге распространение оружия, опасного и для нас.
— Именно. И ещё один аспект, на который надо обращать внимание. Что происходит на Украине. Вот сближение с западной Европой привело к тому, что наши традиционные исконные территории вдруг заявили, что хотят интегрироваться в военное, политическое и экономическое пространство Европы. Это опять о гравитации. Китай сегодня может начать притягивать наши восточные регионы. Сибирские или Дальнего Востока. Он может начать ослаблять их связи с центральной Россией. Поэтому постепенная переориентация с Запада на Восток, вот даже теоретически давайте посмотрим, что у нас происходит к востоку от Енисея — всё в большей степени будет ориентировано на Китай, на АТР.
— Опасность политической дезинтеграции? Это же в принципе сопутствует экономической глобализации. За экономическим размежеванием территорий внутри одной крупной страны, следует и политическое. Наталья Михайловна Травкина, ваша коллега, в интервью, которое я с ней сделал, указала на то, что, например, в США штаты западного побережья экономически гораздо ближе к Азии, чем к штатам восточного побережья, которые в свою очередь экономически ближе к Европе. То есть, внешнеэкономические связи вызывают разрывы во внутриэкономических связях в стране.
— Регион АТР сегодня в состоянии обеспечить Дальний Восток всей нужной ему продукцией. Чего уж говорить про курорты. Таиланд и прочее. И возникает вопрос, а что Дальнему Востоку может дать Центральная Россия? И ведь и в правящих кругах заговорили в последнее время, что, может быть, и пора столицу куда-то из Москвы за Урал перенести. И эти разговоры тоже рождаются не на пустом месте. Конечно, на Дальнем Востоке пока проблема выбора между Азией и европейской Россией не стоит. Она стоит в бывших советских республиках, расположенных в Восточной Европе.
Вот наша попытка интегрироваться с Западом привела к тому, что началась «растащиловка». Пример — Украина. Но такое может начаться и на востоке страны. Это возможное следствие долгосрочных процессов интеграции и взаимозависимости со странами Азии. И это уже вопрос национальной безопасности. Америка на подобные процессы у себя смотрит как раз с точки зрения национальной безопасности.
Поэтому сменив вектор на восточный, мы должны понимать общие экономические закономерности. И ведь важно ещё другое. Ну представим себе министра иностранных дел будущего, ну не пойдёт у нас что-то с Китаем, какой новый вектор будет определён? Арктический? Куда дальше подаваться? Сегодня всё понятно. Не получилось с Западом, пойдём на Восток.
— Ощущается отсутствие какой-то самодостаточности у современной России. Прежде всего, экономической. Нужно же исходить из того, что должно быть своё производство. Ведь условия, когда быстрее и дешевле купить у соседей, чем произвести самому, логичны только в мире чистой экономики, а не в мире, где ещё, вообще-то, есть политика. Сосед может продать нам компьютер, а мы можем купить, но вот взял и не захотел продавать. По своим причинам.
— Надо иметь собственные развитые регионы, самодостаточные, и только на этой базе мы можем ими куда-то интегрироваться. А тут другое получается.
Мы считаем, что внешнеэкономические связи являются основным фактором развития и этих регионов, и России. Вот в чём вся проблема и ошибка.
Мы видели это в европейской части, и вот теперь снова надо опираться на внешнеэкономические связи, чтобы что-то развить. Внешнеэкономические связи тянут за собой проблемы национальной безопасности, сохранения территориальной целостности. Нужно понимать, что запущенные процессы в перспективе при определённых условиях могут привести к точке невозврата.
— Перед разговором с вами я всё-таки представлял, что Россия нечто цельное, монолитное, а выясняется, что смена векторов ставит под вопрос сохранение России в нынешних территориальных границах. Вот вы комментировали назначение нового американского посла в России, «советолога» Линн Трейси. А кто такой советолог? Это специалист по вопросу причин развала Советского Союза. Вот она знает эти причины. И, наверное, эти лекала могут быть применены и к России?
— Сложность в том, что мы являемся не унитарной, а многонациональной страной. И сегодня можно посмотреть, где прошли разломы в Украине, в других республиках бывшего СССР. Всё делается по этническому принципу. Чтобы раскачать ситуацию используют регионы, где у населения другая культурная, религиозная ориентация. Мы видим это по Северному Кавказу, Татарстану и прочее. Есть это и на Дальнем Востоке. Якутия, Бурятия. Я не исключаю то, что при определённых условиях эти разломы могут быть усилены. Унитарное государство всегда крепче. Но у нас страна сложная, и при этом у нас не союз территорий, а союз наций. И то что сегодня наши геополитические противники делают, это очень и очень просто. Они работают с регионами, имеющими свои этнические особенности. В своей последней научной статье «Россия и Америка в XXI в.: логика цивилизационного противостояния» (12+) я их версию карты раздела России.
— Они называют это «деколонизацией» России… Просто, чтобы все понимали — круглый стол на эту тему был организован правительственной организацией США, некой Комиссией по безопасности и сотрудничеству в Европе. Посмотрите, как топорно и грубо они пытаются включить нас в свою позорную повестку, в своё колониальное прошлое, геноцид народов в Азии, Африке, Северной Америке, Южной Америке. Скоро они начнут искать у нас ущемление прав чернокожего населения…
— Это может быть и смешно, но этнический фактор в деле разрушения стран — очень хорошо отработанный механизм. Я помню, что происходило в Советском Союзе, когда он распадался. Я был в Прибалтике. И все эти наши лозунги «Перестройка», «Гласность» и многое, что было запущено весною 1985 года — воспринималось в Прибалтике, на Украине, в Закавказье — совершенно иначе, чем хотелось бы центру. Вот они, грубо говоря, мыслили это всё с точностью до наоборот. В своих национальных приоритетах. И в 1990-ом году, когда один человек [Борис Ельцин — прим. ред.] сказал, берите суверенитета столько, сколько сможете проглотить, сразу начались известные процессы, но уже в рамках Российской Федерации. Мы знаем к чему это всё привело.
— К крови.
— И вот эта тенденция суверенитетов, она существует. И последнее, что я хочу сказать, что пронизывает любую федерацию — это соотношение регионов-доноров и регионов-реципиентов (по данным за 2022 год — только 23 региона не получают дотации от Минфина на выравнивание бюджетной обеспеченности, а 62 — получают. При этом на Дальнем Востоке только Сахалин, по данным на 2022 год, является регионом-донором — прим. ред.). Экономически, с моей точки зрения, самый главный фактор дезинтеграции любой страны является вот эта вот неоднородность по этому важнейшему параметру. Это вот Каталония и Испания, северная и южная Италия, Англия и Шотландия. Территории хотят распрощаться с другой частью страны, когда они могут жить самостоятельно экономически и только для себя. А платить налоги в центр не хочется, что и понятно. А экономическая глобализация только усиливает центробежные процессы.
— Но большинство наших национальных республик как раз являются жёстко дотационными, в том числе и на Дальнем Востоке. И деньги в данном случае — всё-таки фактор интеграции. А есть ещё какие-то сильные факторы единства страны?
— Если Китай и извлёк для себя какие-то уроки из развала СССР, то только то, что должен быть стержневой институт. В разноплановом обществе некая единая конструкция. И там это Коммунистическая партия Китая. Если вынуть этот стержень, то Китай может распасться на шесть-семь независимых государств. Вот и получается, что такие стержни нужны. И это приводит потом к разговорам о демократических или авторитарных правлениях. У нас КПСС было этим стержнем. Компартию распустили в августе 1991 года и через четыре месяца Советского Союза не стало.
— Слабый какой-то стержень. Если он единственный из возможных, то, может быть, он и не нужен вообще?
— Ну найдите другой «эмпирический аналог», как в экономике говорят. В Китае в партии почти 98 миллионов человек, вот это стержень так стержень.
— А вот у нас общее русское языковое пространство, культурное пространство, учитывающее национальное разнообразие, общая история, патриотизм и прочее-прочее. Вот это вот всё не стрежни?
— А на окраинах это понимается по-другому. В центре одно понимание, а на местах не то чтобы противоположенное, но иное. Вот и возникает опасность утраты единого знаменателя. Эта опасность есть всегда в союзах наций. И на этом наши противники умеют играть.
— Спасибо за интереснейшую беседу!
Новости — только важное! Подпишись в WhatsApp
Создание, изменение и доступ к векторным элементам
В этой статье вы узнаете о векторах в программировании на R. Вы научитесь создавать их, получать доступ к их элементам с помощью различных методов и изменять их в своей программе.
Вектор — это базовая структура данных в R. Она содержит элементы того же типа. Типы данных могут быть логическими, целочисленными, двойными, символьными, сложными или необработанными.
Тип вектора можно проверить с помощью функции typeof()
.
Еще одним важным свойством вектора является его длина. Это количество элементов в векторе, и его можно проверить с помощью функции 9.0007 длина() .
Как создать вектор в R?
Векторы обычно создаются с помощью функции c()
.
Так как вектор должен иметь элементы одного типа, эта функция попытается привести элементы к одному типу, если они разные.
Приведение от младших к более высоким типам от логического к целому к двойному к символьному.
> х <- с(1, 5, 4, 9, 0) > тип(х) [1] «двойной» > длина (х) [1] 5 > x <- c(1, 5.4, ИСТИНА, "привет") > х [1] «1» «5.4» «ИСТИНА» «привет» > тип(х) [1] "персонаж"
Если мы хотим создать вектор последовательных чисел, очень полезен оператор :
.
Пример 1: Создание вектора с помощью: оператора
> х <- 1:7; Икс [1] 1 2 3 4 5 6 7 > у <- 2:-2; у [1] 2 1 0 -1 -2
Более сложные последовательности могут быть созданы с помощью функции seq()
, например определения количества точек в интервале или размера шага.
Пример 2. Создание вектора с помощью функции seq()
> seq(1, 3, by=0.2) # указать размер шага [1] 1,0 1,2 1,4 1,6 1,8 2,0 2,2 2,4 2,6 2,8 3,0 > seq(1, 5, length.out=4) # указать длину вектора [1] 1,000000 2,333333 3,666667 5,000000
Как получить доступ к элементам вектора?
Доступ к элементам вектора можно получить с помощью векторной индексации. Вектор, используемый для индексации, может быть логическим, целочисленным или вектором символов.
Использование целочисленного вектора в качестве индекса
Индекс вектора в R начинается с 1, в отличие от большинства языков программирования, где индекс начинается с 0.
Мы можем использовать вектор целых чисел в качестве индекса для доступа к определенным элементам.
Мы также можем использовать отрицательные целые числа, чтобы вернуть все элементы, кроме указанных.
Но мы не можем смешивать положительные и отрицательные целые числа, в то время как индексация и действительные числа, если они используются, усекаются до целых чисел.
> х [1] 0 2 4 6 8 10 > x[3] # доступ к третьему элементу [1] 4 > x[c(2, 4)] # доступ ко 2-му и 4-му элементам [1] 2 6 > x[-1] # доступ ко всем элементам, кроме 1-го [1] 2 4 6 8 10 > x[c(2, -4)] # нельзя смешивать положительные и отрицательные целые числа Ошибка в x[c(2, -4)]: только 0 могут быть смешаны с отрицательными нижними индексами > x[c(2.4, 3.54)] # действительные числа усекаются до целых [1] 2 4
Использование логического вектора в качестве индекса
Когда мы используем логический вектор для индексации, возвращается позиция, где логический вектор равен TRUE
.
Эта полезная функция помогает нам фильтровать вектор, как показано ниже.
> х[с(ИСТИНА, ЛОЖЬ, ЛОЖЬ, ИСТИНА)] [1] -3 3 > x[x < 0] # фильтрация векторов по условиям [1] -3 -1 > х [х > 0] [1] 3
В приведенном выше примере выражение x>0
даст логический вектор (ЛОЖЬ, ЛОЖЬ, ЛОЖЬ, ИСТИНА)
, который затем используется для индексации.
Использование вектора символов в качестве индекса
Этот тип индексации полезен при работе с именованными векторами. Мы можем назвать каждый элемент вектора.
> x <- c("первый"=3, "второй"=0, "третий"=9) > имена(х) [1] "первый" "второй" "третий" > х["секунда"] второй 0 > х[с("первый", "третий")] первая треть 3 9
Как изменить вектор в R?
Мы можем изменить вектор с помощью оператора присваивания.
Мы можем использовать методы, описанные выше, для доступа к определенным элементам и их модификации.
Если мы хотим обрезать элементы, мы можем использовать переназначения.
> х [1] -3 -2 -1 0 1 2 > х[2] <- 0; x # изменить 2-й элемент [1] -3 0 -1 0 1 2 > х[х<0] <- 5; x # изменить элементы меньше 0 [1] 5 0 5 0 1 2 > х <- х[1:4]; x # обрезать x до первых 4 элементов [1] 5 0 5 0
Как удалить вектор?
Мы можем удалить вектор, просто назначив NULL
к нему.
> х [1] -3 -2 -1 0 1 2 > х <- NULL > х НУЛЕВОЙ > х[4] НУЛЕВОЙ
- ПРЕДЫДУЩИЙ
Функция R switch() - СЛЕДУЮЩИЙ
R Матрица
Как создать вектор в R и получить к нему доступ?
Вы можете создать вектор в R, используя примитивную функцию c()
. В программировании на R вектор содержит элементы одного и того же типа, и типы могут быть логическими, целочисленными, двойными, символьными, сложными или необработанными. Помимо c() вы также можете создать вектор, используя функции vector(), character().
В этой статье рассматриваются следующие способы создания векторов в R.
- Создать вектор в R с помощью функции c()
- Создать именованный вектор
- Создать вектор из списка
- Вектор нулей
- Вектор заданной длины
- Числовой вектор со значениями от 0 до 10 с использованием вектора
210900()
1. Создайте вектор в R с помощью функции c()
В R вектор — это фундаментальная структура данных, которая используется для хранения элементов данных одного типа. и типы могут быть логическими, целочисленными, двойными, символьными, сложными или необработанными. R Vector создается с помощью комбинированной функции c(). Давайте посмотрим на синтаксис этой функции и на то, как создать вектор.
1.1. Синтаксис c()
Ниже приведен синтаксис функции c(), которая используется для создания вектора в R.
# Синтаксис функции c() с(...)
1.2. Пример создания вектора
Использование функции c() является наиболее часто используемым и распространенным способом создания вектора в R. На самом деле c() — это комбинированная функция, которая используется для объединения элементов в вектор или список. В следующем примере создаются числовой вектор, вектор символов и вектор даты с именами переменных id 9.0008 ,
имя
и доб
соответственно.
# Создать векторы идентификатор <- с (10,11,12,13) имя <- c('сай','рам','дипика','сахити') доб <- as.Date(c('1990-10-02','1981-3-24','1987-6-14','1985-8-16'))
Здесь переменные
-
id
— числовой вектор, в котором хранятся числовые значения. -
имя
– Вектор символов, в котором хранятся значения символов. -
доб
– Вектор даты, который хранит значения даты.
В приведенном выше примере создаются 3 вектора, теперь давайте отобразим тип этих векторных переменных с помощью функции typeof()
. вы можете получить размер вектора, используя length().
# Типы векторов > тип (идентификатор) #[1] "двойной" > тип (имя) #[1] "персонаж" > тип(доб) #[1] "двойной"
2. Создать именованный вектор
Вы также можете назначать имена значениям при создании вектора, если у вас есть имена, он называется именованным вектором. В следующем примере создается вектор с именами C1
, C2
и C3
.
# Создать именованный вектор x <- c(C1='A',C2='B',C3='C') печать (х) # Выход # С1 С2 С3 #"А" "Б" "С"
3. Создать вектор из списка
Если у вас есть список, вы можете легко создать вектор из списка в R с помощью функции unlist()
. Эта функция принимает список в качестве аргумента и преобразует его в вектор. Используя is.vector()
, проверьте, имеет ли преобразованный вектор тип vector.
# Создать вектор из списка li <- список('A','B','C') v <- удалить из списка (li) печать (v) печать (тип (v)) печать (is.vector (v)) # Выход #[1] «А» «Б» «В» #[1] "персонаж" #[1] ИСТИНА
4. Вектор нулей
Чтобы создать вектор нулей, используйте функцию integer(). вектор.
# Создать вектор нулей v <- целое число (6) печать (v) # Выход #[1] 0 0 0 0 0 0
5. Вектор длины N
Допустим, вы хотите создать вектор в R заданной длины N со значениями по умолчанию. В приведенном выше примере создается числовой вектор со значением 0 и указанной длиной. Точно так же, чтобы создать вектор символов с указанными пустыми местами, используйте символ(N)
.
# Создать вектор заданной длины v <- символ(5) печать (v) # Выход #[1] "" "" "" "" ""
6. Вектор от 1 до 10
Если вам нужен вектор с порядковыми номерами от 1 до 10, используйте функцию seq(1,10)
или 1:10
.
# Создать числовой вектор со значениями от 0 до 10 v <- 1:10 v <- последовательность (1, 10) печать (v) # Выход # [1] 1 2 3 4 5 6 7 8 9 10
7.
Использование Vector() Вектор () 9Функция 0008 используется для создания вектора любого типа. Он принимает параметр
, режим
и длину. режим используется для указания типа, а длина используется для указания длины вектора со значениями по умолчанию. В следующем примере создается логический вектор с 5 элементами.
# Создать вектор с помощью vector() x <- вектор (режим = 'логический', длина = 5) печать (х) печать (есть.вектор (х)) печать (тип (х)) # Выход #[1] ЛОЖЬ ЛОЖЬ ЛОЖЬ ЛОЖЬ ЛОЖЬ ЛОЖЬ #[1] ИСТИНА #[1] "логический"
8. Полный пример создания вектора
Ниже приведен полный пример различных способов создания вектора в R. Полный пример из этой статьи можно найти в Github R Programming Examples Project.
# Создать вектор с помощью c() идентификатор <- с (10,11,12,13) имя <- c('сай','рам','дипика','сахити') доб <- as.Date(c('1990-10-02','1981-3-24','1987-6-14','1985-8-16')) # Создать именованный вектор x <- c(C1='A',C2='B',C3='C') # Создать вектор с помощью vector() x <- вектор (режим = 'логический', длина = 5) # Создать вектор символов х <- символ(5) # Создать вектор из списка li <- список('A','B','C') v <- удалить из списка (li) # Создать вектор нулей v <- целое число (6) # Создать вектор заданной длины v <- символ(5) # Создать числовой вектор со значениями от 0 до 10 v <- последовательность (1, 10) v <- 1:10 # Создать вектор с помощью vector() x <- вектор (режим = 'логический', длина = 5)