Обобщенное программирование c
Обзор обобщений
После появления первого выпуска платформы .NET программисты часто использовали пространство имен System.Collections для получения более гибкого способа управления данными в приложениях. Однако, начиная с версии .NET 2.0, язык программирования C# был расширен поддержкой средства, которое называется обобщением (generic) . Вместе с ним библиотеки базовых классов пополнились совершенно новым пространством имен, связанным с коллекциями — System.Collections.Generic.
Термин обобщение, по существу, означает параметризированный тип. Особая роль параметризированных типов состоит в том, что они позволяют создавать классы, структуры, интерфейсы, методы и делегаты, в которых обрабатываемые данные указываются в виде параметра. С помощью обобщений можно, например, создать единый класс, который автоматически становится пригодным для обработки разнотипных данных. Класс, структура, интерфейс, метод или делегат, оперирующий параметризированным типом данных, называется обобщенным, как, например, обобщенный класс или обобщенный метод.
Следует особо подчеркнуть, что в C# всегда имелась возможность создавать обобщенный код, оперируя ссылками типа object. А поскольку класс object является базовым для всех остальных классов, то по ссылке типа object можно обращаться к объекту любого типа. Таким образом, до появления обобщений для оперирования разнотипными объектами в программах служил обобщенный код, в котором для этой цели использовались ссылки типа object.
Но дело в том, что в таком коде трудно было соблюсти типовую безопасность, поскольку для преобразования типа object в конкретный тип данных требовалось приведение типов. А это служило потенциальным источником ошибок из-за того, что приведение типов могло быть неумышленно выполнено неверно. Это затруднение позволяют преодолеть обобщения, обеспечивая типовую безопасность, которой раньше так недоставало. Кроме того, обобщения упрощают весь процесс, поскольку исключают необходимость выполнять приведение типов для преобразования объекта или другого типа обрабатываемых данных. Таким образом, обобщения расширяют возможности повторного использования кода и позволяют делать это надежно и просто.
Обобщения — это не совсем новая конструкция; подобные концепции присутствуют и в других языках. Например, схожие с обобщениями черты имеют шаблоны С++. Однако между шаблонами С++ и обобщениями .NET есть большая разница. В С++ при создании экземпляра шаблона с конкретным типом необходим исходный код шаблонов. В отличие от шаблонов С++, обобщения являются не только конструкцией языка C#, но также определены для CLR. Это позволяет создавать экземпляры шаблонов с определенным типом-параметром на языке Visual Basic, даже если обобщенный класс определен на C#.
Давайте рассмотрим основные преимущества использования обобщений:
Производительность
Одним из основных преимуществ обобщений является производительность. Использование типов значений с необобщенными классами коллекций вызывает упаковку (boxing) и распаковку (unboxing) при преобразовании в ссылочный тип и обратно.
Типы значений сохраняются в стеке, а типы ссылок — в куче. Классы C# являются ссылочными типами, а структуры — типами значений. .NET позволяет легко преобразовывать типы значений в ссылочные, поэтому их можно использовать там, где ожидаются объекты (т.е. ссылочные типы). Например, объекту можно присвоить значение типа int.
Преобразование типа значений в ссылочный тип называется упаковкой (boxing). Упаковка происходит автоматически, когда метод ожидает параметр ссылочного типа, а ему передается тип значений. С другой стороны, упакованный тип значений может быть обратно преобразован к простому типу значений с помощью распаковки (unboxing). При распаковке требуется операция приведения.
Безопасность
Другим свойством обобщений является безопасность типов. Обобщения автоматически обеспечивают типовую безопасность всех операций. В ходе выполнения этих операций обобщения исключают необходимость обращаться к приведению типов и проверять соответствие типов в коде вручную.
Повторное использование двоичного кода
Обобщения повышают степень повторного использования двоичного кода. Обобщенный класс может быть определен однажды, и на его основе могут быть созданы экземпляры многих типов. При этом не нужно иметь доступ к исходным текстам, как это необходимо в случае шаблонов С++.
«Разбухание» кода
Насколько много кода генерируется при создании экземпляров конкретных типов из обобщений? Поскольку определение обобщенного класса включается в сборку, создание на его основе конкретных классов специфических типов не приводит к дублированию кода в IL.
Однако когда обобщенные классы компилируются JIT-компилятором в родной машинный код, для каждого конкретного типа значения создается новый класс. Ссылочные типы при этом разделяют общую реализацию одного родного класса. Причина в том, что в случае ссылочных типов каждый элемент представлен в памяти 4-байтным адресом (на 32-разрядных системах) и машинные реализации обобщенного класса с различными ссылочными типами-параметрами не отличаются друг от друга. В отличие от этого, типы значений содержатся в памяти целиком, и поскольку каждый из них требует разного объема памяти, то для каждого из них создаются свои экземпляры классов на основе обобщенного.
Рекомендации по именованию
Если в программе используются обобщения, то очень полезно, когда переменные обобщенных типов легко можно отличить от необобщенных. Ниже представлены рекомендации по именованию обобщенных типов:
Имена обобщенных типов должны начинаться с буквы Т.
Если обобщенный тип может быть заменен любым классом, поскольку нет никаких специальных требований, и используется только один обобщенный тип, T — вполне подходящее имя для обобщенного типа:
Если к обобщенному типу предъявляются специальные требования (например, что тип должен реализовывать интерфейс или наследоваться от определенного класса), либо же используется два или более обобщенных типа в качестве параметров, то следует применять осмысленные имена типов:
Обобщенное программирование
Обобщённое программирование — парадигма программирования, заключающаяся в таком описании данных и алгоритмов, которое можно применять к различным типам данных, не меняя само это описание. В том или ином виде поддерживается разными языками программирования. Возможности обобщённого программирования впервые появились в 70-х годах в языках CLU и Ada, а затем во многих объектно-ориентированных языках, таких как C++, D и языках для платформы .NET.
Содержание
Общий механизм
Средства обобщённого программирования реализуются в языках программирования в виде тех или иных синтаксических средств, дающих возможность описывать данные (типы данных) и алгоритмы (процедуры, функции, методы), параметризуемые типами данных. У функции или типа данных явно описываются формальные параметры-типы. Это описание является обобщённым и в исходном виде непосредственно использовано быть не может.
В тех местах программы, где обобщённый тип или функция используется, программист должен явно указать фактический параметр-тип, конкретизирующий описание. Например, обобщённая процедура перестановки местами двух значений может иметь параметр-тип, определяющий тип значений, которые она меняет местами. Когда программисту нужно поменять местами два целых значения, он вызывает процедуру с параметром-типом «целое число» и двумя параметрами — целыми числами, когда две строки — с параметром-типом «строка» и двумя параметрами — строками. В случае с данными программист может, например, описать обобщённый тип «список» с параметром-типом, определяющим тип хранимых в списке значений. Тогда при описании реальных списков программист должен указать обобщённый тип и параметр-тип, получая, таким образом, любой желаемый список с помощью одного и того же описания.
Компилятор, встречая обращение к обобщённому типу или функции, выполняет необходимые процедуры статического контроля типов, оценивает возможность заданной конкретизации и при положительной оценке генерирует код, подставляя фактический параметр-тип на место формального параметра-типа в обобщённом описании. Естественно, что для успешного использования обобщённых описаний фактические типы-параметры должны удовлетворять определённым условиям. Если обобщённая функция сравнивает значения типа-параметра, любой конкретный тип, использованный в ней, должен поддерживать операции сравнения, если присваивает значения типа-параметра переменным — конкретный тип должен обеспечивать корректное присваивание.
Способы реализации
Известно два основных способа реализации поддержки обобщённого программирования в компиляторе.
- Порождение нового кода для каждой конкретизации. В этом варианте компилятор рассматривает обобщённое описание как текстовый шаблон для создания вариантов конкретизаций. Когда компилятору требуется новая конкретизация обобщённого типа или процедуры, он создаёт новый экземпляр типа или процедуры, чисто механически добавляя туда тип-параметр. То есть, имея обобщённую функцию перестановки элементов, компилятор, встретив её вызов для целого типа, создаст функцию перестановки целых чисел и подставит в код её вызов, а затем, встретив вызов для строкового типа — создаст функцию перестановки строк, никак не связанную с первой. Этот метод обеспечивает максимальное быстродействие, поскольку варианты конкретизаций становятся разными фрагментами программы, каждый из них может быть оптимизирован для своего типа-параметра, к тому же в код не включаются никакие лишние элементы, связанные с проверкой или преобразованием типов на этапе исполнения программы. Недостатком его является то, что при активном использовании обобщённых типов и функций с различными типами-параметрами размер откомпилированной программы может очень сильно возрастать, поскольку даже для тех фрагментов описания, которые для разных типов не различаются, компилятор всё равно генерирует отдельный код. Этот недостаток можно затушевать путём частичной генерации общего кода (часть обобщённого описания, которая не зависит от типов-параметров, оформляется специальным образом и по ней компилятор генерирует единый для всех конкретизаций код). Зато данный механизм даёт естественную возможность создания специальных (обычно — сильно вручную оптимизированных) конкретизаций обобщённых типов и функций для некоторых типов-параметров.
- Порождение кода, который во время исполнения выполняет преобразование фактических параметров-типов к одному типу, с которым фактически и работает. В этом случае на этапе компиляции программы компилятор лишь проверяет соответствие типов и включает в код команды преобразования конкретного типа-параметра к общему типу. Код, определяющий функционирование обобщённого типа или функции, имеется в откомпилированной программе в единственном экземпляре, а преобразования и проверки типов выполняются динамически, во время работы программы. В этом варианте порождается, как правило, более компактный код, но программа оказывается в среднем медленнее, чем в первом варианте, из-за необходимости выполнения дополнительных операций и меньших возможностей оптимизации. Кроме того, в компилированный код для типов-параметров далеко не всегда включается динамическая информация о типах (в первом варианте она есть, если вообще поддерживается, поскольку конкретизации для каждого типа-параметра различны), что определяет некоторые ограничения на применение обобщённых типов и функций. Подобные ограничения есть, например, в Java.
Обобщённое программирование в языках
В языке C++ обобщённое программирование основывается на понятии «шаблон», обозначаемом ключевым словом template. Широко применяется в стандартной библиотеке C++ (см. STL), а также в сторонних библиотеках boost, Александр Степанов.
В качестве примера приведём обобщённую функцию, возвращающую большее значение из двух.
Java предоставляет средства обобщённого программирования, синтаксически основанные на C++, начиная с версии J2SE 5.0. В этом языке имеются generics или «контейнеры типа T» — подмножество обобщённого программирования.
На платформе .NET средства обобщённого программирования появились в версии 2.0.
Пример на C#
В языке «D» средства обобщённого программирования значительно эволюционировали в сравнении с С++, как в процедурной, так и в объектно ориентированной составляющей. Значительно проще стало и лингвистическое представление.
Пример рекурсивной генерации на основе шаблонов D:
Wikimedia Foundation . 2010 .
Смотреть что такое «Обобщенное программирование» в других словарях:
обобщенное программирование — — [Е.С.Алексеев, А.А.Мячев. Англо русский толковый словарь по системотехнике ЭВМ. Москва 1993] Тематики информационные технологии в целом EN generalized programmingGP … Справочник технического переводчика
Сравнение языков программирования — Эту статью следует викифицировать. Пожалуйста, оформите её согласно правилам оформления статей. Условные обозначения … Википедия
C++ — У этого термина существуют и другие значения, см. C. См. также: Си (язык программирования) C++ Семантика: мультипарадигмальный: объектно ориентированное, обобщённое, процедурное, метапрограммирование Тип исполнения: компилируемый Появился в … Википедия
D (язык программирования) — У этого термина существуют и другие значения, см. D. D Семантика: мультипарадигменный: императивное, объектно ориентированное, обобщённое программирование Тип исполнения: компилятор Появился в: 1999 Автор(ы) … Википедия
С++ — См. также: Си (язык программирования) C++ Семантика: мультипарадигмальный: объектно ориентированное, обобщённое, процедурное, метапрограммирование Тип исполнения: компилируемый Появился в: 1985 г. Автор(ы): Бьёрн Страуструп … Википедия
F Sharp — У этого термина существуют и другие значения, см. F (значения). Правильный заголовок этой статьи F#. Он показан некорректно из за технических ограничений. F# Класс языка: мультипарадигменный: функциональное, объектно ориентированное,… … Википедия
F♯ — Этим сочетанием символов также часто обозначают аккорд или тональность Фа диез мажор F♯ это функциональный язык программирования общего назначения. Разработан Доном Саймом (англ. Don Syme) в Microsoft Research в Кембридже. Структура F♯ во многом… … Википедия
— F# это функциональный язык программирования общего назначения. Разработан Доном Саймом (англ. Don Syme) в Microsoft Research в Кембридже. Структура F# во многом схожа со структурой библиотек и среды исполнения .NET. Некоторые задачи решаются… … Википедия
Loki — У этого термина существуют и другие значения, см. Loki (значения). Библиотека Loki для языка программирования C++ написана Андреем Александреску как часть книги «Современное проектирование на С++: Обобщенное программирование и прикладные шаблоны… … Википедия
Обобщённое программирование
Материал из Seo Wiki — Поисковая Оптимизация и Программирование
Обобщённое программирование — парадигма программирования, заключающаяся в таком описании данных и алгоритмов, которое можно применять к различным типам данных, не меняя само это описание. В том или ином виде поддерживается разными языками программирования. Возможности обобщённого программирования впервые появились в 70-х годах в языках CLU и Ada, а затем во многих объектно-ориентированных языках, таких как C++, Java, Object Pascal [1] , D и языках для платформы .NET.
Содержание
Общий механизм
Средства обобщённого программирования реализуются в языках программирования в виде тех или иных синтаксических средств, дающих возможность описывать данные (типы данных) и алгоритмы (процедуры, функции, методы), параметризуемые типами данных. У функции или типа данных явно описываются формальные параметры-типы. Это описание является обобщённым и в исходном виде непосредственно использовано быть не может.
В тех местах программы, где обобщённый тип или функция используется, программист должен явно указать фактический параметр-тип, конкретизирующий описание. Например, обобщённая процедура перестановки местами двух значений может иметь параметр-тип, определяющий тип значений, которые она меняет местами. Когда программисту нужно поменять местами два целых значения, он вызывает процедуру с параметром-типом «целое число» и двумя параметрами — целыми числами, когда две строки — с параметром-типом «строка» и двумя параметрами — строками. В случае с данными программист может, например, описать обобщённый тип «список» с параметром-типом, определяющим тип хранимых в списке значений. Тогда при описании реальных списков программист должен указать обобщённый тип и параметр-тип, получая, таким образом, любой желаемый список с помощью одного и того же описания.
Компилятор, встречая обращение к обобщённому типу или функции, выполняет необходимые процедуры статического контроля типов, оценивает возможность заданной конкретизации и при положительной оценке генерирует код, подставляя фактический параметр-тип на место формального параметра-типа в обобщённом описании. Естественно, что для успешного использования обобщённых описаний фактические типы-параметры должны удовлетворять определённым условиям. Если обобщённая функция сравнивает значения типа-параметра, любой конкретный тип, использованный в ней, должен поддерживать операции сравнения, если присваивает значения типа-параметра переменным — конкретный тип должен обеспечивать корректное присваивание.
Способы реализации
Известно два основных способа реализации поддержки обобщённого программирования в компиляторе.
- Порождение нового кода для каждой конкретизации. В этом варианте компилятор рассматривает обобщённое описание как текстовый шаблон для создания вариантов конкретизаций. Когда компилятору требуется новая конкретизация обобщённого типа или процедуры, он создаёт новый экземпляр типа или процедуры, чисто механически добавляя туда тип-параметр. То есть, имея обобщённую функцию перестановки элементов, компилятор, встретив её вызов для целого типа, создаст функцию перестановки целых чисел и подставит в код её вызов, а затем, встретив вызов для строкового типа — создаст функцию перестановки строк, никак не связанную с первой. Этот метод обеспечивает максимальное быстродействие, поскольку варианты конкретизаций становятся разными фрагментами программы, каждый из них может быть оптимизирован для своего типа-параметра, к тому же в код не включаются никакие лишние элементы, связанные с проверкой или преобразованием типов на этапе исполнения программы. Недостатком его является то, что при активном использовании обобщённых типов и функций с различными типами-параметрами размер откомпилированной программы может очень сильно возрастать, поскольку даже для тех фрагментов описания, которые для разных типов не различаются, компилятор всё равно генерирует отдельный код. Этот недостаток можно затушевать путём частичной генерации общего кода (часть обобщённого описания, которая не зависит от типов-параметров, оформляется специальным образом и по ней компилятор генерирует единый для всех конкретизаций код). Зато данный механизм даёт естественную возможность создания специальных (обычно — сильно вручную оптимизированных) конкретизаций обобщённых типов и функций для некоторых типов-параметров.
- Порождение кода, который во время исполнения выполняет преобразование фактических параметров-типов к одному типу, с которым фактически и работает. В этом случае на этапе компиляции программы компилятор лишь проверяет соответствие типов и включает в код команды преобразования конкретного типа-параметра к общему типу. Код, определяющий функционирование обобщённого типа или функции, имеется в откомпилированной программе в единственном экземпляре, а преобразования и проверки типов выполняются динамически, во время работы программы. В этом варианте порождается, как правило, более компактный код, но программа оказывается в среднем медленнее, чем в первом варианте, из-за необходимости выполнения дополнительных операций и меньших возможностей оптимизации. Кроме того, в компилированный код для типов-параметров далеко не всегда включается динамическая информация о типах (в первом варианте она есть, если вообще поддерживается, поскольку конкретизации для каждого типа-параметра различны), что определяет некоторые ограничения на применение обобщённых типов и функций. Подобные ограничения есть, например, в Java.
Обобщённое программирование в языках
В языке C++ обобщённое программирование основывается на понятии «шаблон», обозначаемом ключевым словом template. Широко применяется в стандартной библиотеке C++ (см. STL), а также в сторонних библиотеках boost, Loki. Большой вклад в появление развитых средств обобщённого программирования в C++ внёс Александр Степанов.
В качестве примера приведём обобщённую функцию, возвращающую большее значение из двух.
Java предоставляет средства обобщённого программирования, синтаксически основанные на C++, начиная с версии J2SE 5.0. В этом языке имеются generics или «контейнеры типа T» — подмножество обобщённого программирования.
На платформе .NET средства обобщённого программирования появились в версии 2.0.
Пример на C#
В языке «D» средства обобщённого программирования значительно эволюционировали в сравнении с С++, как в процедурной, так и в объектно-ориентированной составляющей. Значительно проще стало и лингвистическое представление.
Пример рекурсивной генерации на основе шаблонов D:
Generic programming in C
I am writing a generic linked list implementation in pure C.
This all works great. My problem is when I get to printing value to the screen. I can’t seem to find a generic implementation for printing.
Is there a way to determine the TYPE allocated to void * ? (And then just do conversion using a switch statement)
This is an implementation that works for int. Worst case I was thinking was writing a different function for each TYPE. Is this really my only route when using void * ?
Is there a better way to do this?
You could use user-supplied function (per function pointer). Like there could be a function to compare for sorting. – deviantfan 20 янв. 14 2014-01-20 15:48:10
Don’t cast the return value of ‘malloc’. if you’re going generic: a void pointer is all you’ll ever need – Elias Van Ootegem 20 янв. 14 2014-01-20 15:55:50
For a generic linked-list to actually be a meaningful container ADT, you will need to store the actual data inside it, and not just pointers. This means that you will have to update the ‘Node’ type with a size-in-bytes parameter. And then hard copy the data to/from the linked list whenever a node is created/deleted/searched for. – Lundin 20 янв. 14 2014-01-20 16:24:39
1 ответ
No, there’s no way to figure that out from the pointer alone. That would require type information to be stored at some well-defined location in all run-time structures, which is simply not how C uses the machine.
The common solution is for the user of the datatype to provide the print function that the application needs, since the application will know the type of data being stored. That is, there is usually an iteration function that takes a function pointer, calling the user’s function (which might print the element) on each element of the list.
Here’s how such a function could look:
The above should call func() for each element of the list, passing it the element’s data and the additional user-supplied data pointer which can be used by the caller to maintain state for the traversal. The callback func() should return false to stop the iteration, true to keep going.
To print an integer, assuming the integers are stored in the pointers, you could have:
Создан 20 янв. 14 2014-01-20 15:47:44 unwind
If I understand correctly, the developers need to write the print function to use the exact datatype they are using? For the implementation I wrote, I tested the int function with the »’void *»’ after I posted this and it didn’t work. Is there a way to safely type cast void to say »’int *»’? (and eventually others) – collinglass 20 янв. 14 2014-01-20 15:52:05
@collinglass I added more words and code to the answer. – unwind 20 янв. 14 2014-01-20 15:56:42
For readability, ‘typedef’ the function pointer. For example ‘typedef bool (print_func_t)(void *element, void *data);’. And then pass the function pointer to the function as a ‘const print_func_t*’. – Lundin 20 янв. 14 2014-01-20 16:27:35
@Lundin Sure, that’s commonly done. Wouldn’t make it a ‘const’ pointer though, since that implies functions can be modified which they can’t. – unwind 20 янв. 14 2014-01-20 16:30:31
No, the const keyword implies that the function _cannot_ be modified 🙂 – Lundin 20 янв. 14 2014-01-20 16:46:13
@Lundin I know; I meant that writing it *without* ‘const’ implies that the function can be modified, which it can’t, so the ‘const’ adds nothing. – unwind 20 янв. 14 2014-01-20 16:47:36
With the typedef as I wrote it, it is perhaps not obvious to the reader that the passed type is a function pointer. Whether or not C actually allows you to modify the contents of a function pointer (by abusing the non-const type, like for example the UB you get when you try to access the contents of a string literal), I have no idea. – Lundin 20 янв. 14 2014-01-20 16:52:58
Okay, I will give you the right answer. I am learning C, it is interesting the way generics are implemented. – collinglass 21 янв. 14 2014-01-21 03:05:19
It is still not clear to me the role of void*data in your print_int . It is not even used. Can you clarify? – johnbakers 09 янв. 16 2016-01-09 16:51:17
Как научиться c++ обобщенное программирование и шаблоны?
Как научиться c++ обобщенное программирование и шаблоны? Порекомендуйте несколько хороших книг на эту тему.
4 Ответов
- C++ Шаблоны: Полный Набор Руководство — D.Vandevoorde и N.Josuttis
- C++ шаблон Метапрограммирование: концепции, инструменты и методы Техники от Boost и выше — D.Abrahams и A.Gurtovoy
- Современный дизайн C++: общее программирование и Применяемые Шаблоны Проектирования — A.Alexandrescu
C++ Templates: The Complete Guide by Nicolai M. Josuttis — это хорошая книга для шаблонов.
Стандартная библиотека C++ от Josuttis.
Современный Дизайн C++: Обобщенное Программирование И Прикладные Шаблоны Проектирования
[бесстыдное продвижение] также проверьте Локи-Либ , который был написан для книги. Я-разработчик. 🙂 [/бесстыдное продвижение]
EDIT: также внимательно посмотрите здесь
Похожие вопросы:
У меня есть проект, который нужно сделать, это мониторинг пакетов. Я хочу захватить каждый пакет, полученный указанным сетевым интерфейсом. Я хочу знать, с чего мне следует начать свой проект .
Прежде всего, я начинающий программист (еще многому предстоит научиться). В одном из моих небольших школьных проектов я написал стек для структуры . Теперь у меня есть немного другая структура, и.
Я знаю java и c#, я хочу научиться программировать мобильное приложение, но я путаю между двумя вариантами: 1. Программирование с помощью java 2. Программирование с помощью Xamarin и c# конечно, я.
Что является хорошим введением в создание систем шаблонов выражений C++? Я хотел бы выразить арифметику на пользовательских типах, избегая временных значений (которые могут быть большими), и.
Я хочу изучить программирование C++ GUI с помощью Visual Studio 2008. Хотя я не знаю, с чего начать. Я выучил C++ в средней школе, но не GUI. Я занимаюсь C# уже около 3 лет, и вот как я learned GUI.
Я отчаянно нуждаюсь в руководстве. Мне нужно написать игровую программу Connect 4, в которой будет применяться параллельное программирование с потоками. Мне нужно научиться этому самому и написать.
Я хочу знать, можно ли сделать универсальное программирование в C++ без использования шаблонов. Можно ли написать все библиотеки, доступные в C++, которые написаны с использованием шаблонов без.
В приложении C#, которое контролирует фактическое перемещение оборудования, как происходит обмен данными? Если PC подключен к другому оборудованию через USB, и низкоуровневое программирование уже.
В чем именно заключается разница? Похоже, что эти термины можно использовать несколько взаимозаменяемо, но, читая статью Википедии для объективного-c, я наткнулся: В дополнение к стилю C.
У меня есть потребность / желание научиться программировать против Win32 в C++. Я немного смущен тем, что такое Win32, так как у меня нет опыта работы на платформе. Что бы вы посоветовали мне начать.