-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

Время чудес, или Тормоза для конца света

Среда, 19 Июля 2017 г. 10:20 + в цитатник

Источник


Когда говорят «конец света», значит, хотят продать кукурузные хлопья,
а вот если говорят «без паники», тут уж дело серьезнее.
Стивен Кинг. «Буря столетия»


Говорят, что наиболее оптимистичные пессимисты верят в счастливый конец света. Применительно к телекому эта мысль имеет второе дно. Все учебные пособия по телекоммуникациям сходятся в одном: когда взамен медных линий стали применяться волоконно-оптические линии связи (ВОЛС) вкупе с лазерами, для отрасли наступило долгожданное и практически вечное счастье, по крайней мере в части обеспечения высокой пропускной способности для почти мгновенной (буквально со скоростью света) доставки множества информационных потоков. Так в отрасли началось время чудес. Да будет свет!


Однако сегодня пропускная способность оптических транспортных сетей стремительно приближается к своему пределу. Дальнейшее ее наращивание потребует организации новой инфраструктуры ВОЛС и – новых инвестиций. Об этом и предлагаем поговорить ниже.



Наперегонки к пределам


Меж тем природа регулярно напоминает нам, что ничего вечного в ней нет. И «пределы беспредельности» оптического транспорта также не за горами, хотя дату «конца света» в ВОЛС удавалось не единожды отодвигать.


Конечно, такую простую неприятность, как отключение электричества, связисты научились преодолевать в первую очередь. Следующая неприятность, а именно непрерывное сокращение безрегенерационного (без промежуточных оптических регенераторов) промежутка на магистральных ВОЛС по мере увеличения скорости передачи, грозила было серьезными проблемами всей отрасли. Спасение пришло в лице когерентных систем класса N x 100 Гбит/с. Системы плотного спектрального волнового уплотнения (DWDM) позволили справиться с этой убийственной для отраслевого бизнеса неприятностью, обеспечив безрегенерационную дальность порядка 5–6 тыс. км в наземных линиях связи и более 10 тыс. км – в подводных (в зависимости от применяемых схем модуляции и кодирования – QPSK, QAM и пр.).



Источник



Ныне на очереди – физические пределы пропускной способности оптического волокна вкупе с ростом в них задержек распространения сигнала. И это на фоне текущего и грядущего роста информационных потоков, вызванного развитием интернета вещей и сетей мобильной связи пятого поколения (5G). Стоит напомнить, что сети 5G должны будут иметь не только сверхвысокие скорости передачи информации (до 1 Гбит/с и выше), но и чрезвычайно малые задержки передачи сигнала (порядка 1 мс и менее). Наиболее сильное влияние на суммарное значение задержки оптического сигнала в системе передачи оказывает длина оптоволокна. С учетом того, что в современном мире практически не осталось транспортных и магистральных сетей, организованных не на базе ВОЛС, получение сверхмалых задержек, если речь идет о чем-либо протяженном за границы стран, регионов и даже городов, становится невозможным.


Прежде всего, повышение эффективности ВОЛС с помощью DWDM значительно увеличивает задержки непосредственно в элементах сети DWDM, где сигнал подвергается различным преобразованиям. Также в канальном слое DWDM большое внимание с необходимостью уделяется дисперсионным характеристикам оптической линии, заставляя прибегать к компенсаторам дисперсии, которые в своем нынешнем исполнении вносят дополнительную задержку. К счастью, использование когерентных систем класса N x 100 Гбит/с позволило от компенсаторов дисперсии отказаться. Это существенно улучшило качество оптического тракта и снизило потери в нем, что благотворно сказалось на предельных дистанциях передачи сигнала. Вместе с тем вопреки утвердившемуся мнению, скорость распространения оптического сигнала в оптоволокне существенно ниже скорости распространения электрических сигналов в медных линиях связи и радиосигналов в атмосфере*.


Собственно, поэтому МСЭ-T и разработал рекомендации по допустимым уровням задержки. В большинстве случаев она не должна превышать 150 мс в одну сторону, что вполне приемлемо, например, для VoIP. Задержки от 150 до 400 мс также могут быть приемлемыми, если принимать в расчет и экономические характеристики (в первую очередь стоимость разговора).


Таким образом, с точки зрения минимальных задержек современные ВОЛС не могут считаться чем-то особенным, что существенно повлияет на физическую и логическую топологии оптических транспортных сетей будущего. Уже сегодня динамика роста наземных и подводных кабельных сетей различается – в силу разницы в стоимости права прохода и строительства, а также вносимых задержек сигнала.


«Смерть волокна»?


Приход в отрасль связи таких глобальных «возмутителей спокойствия», как Google и Facebook, создал дополнительный стимул для развития инфраструктуры оптических систем передачи континентального и межконтинентального классов, обладающих предельной пропускной способностью «на вырост», с учетом очень высоких темпов роста интернет-трафика вообще и доли видеотрафика в нем в частности. Поэтому отраслевая литература пестрит победными реляциями о запуске новых линий, очередных рекордах пропускной способности, создании международных консорциумов для прокладки новых трансокеанских подводных кабельных систем и т.п.


Стремительная динамика ИКТ-отрасли заставила также говорить и о «смерти волокна», поскольку стало понятно, что недалек тот день, когда развитие связи на базе ВОЛС упрется в физические пределы пропускной способности оптических линий. А это означает, что на уже проложенных ВОЛС нельзя будет сделать ничего нового, кроме как уплотнить их по максимуму, и следует прокладывать новые кабели, что весьма недешево. Всяческие технологические улучшения сделали сегодня «обычной» возможность создавать системы связи, позволяющие передать поток 100 Гбит/с и даже 400 Гбит/с (4 х 100 Гбит/с) по одной оптической несущей («лямбде»). Однако далее дело застопорилось, поскольку эмпирический предел спектральной эффективности для подобных оптических систем составляет примерно 5 бит/с/Гц и он уже практически достигнут, вследствие чего одно оптическое волокно имеет предельную пропускную способность порядка 25–50 ТБод в зависимости от задействованных оптических диапазонов. Впрочем, практический предел символьной скорости передачи по одному оптическому волокну, определяемый физическими характеристиками используемого материала, сейчас равняется 12–25 ТБод в зависимости от типа модуляции и пр.



Источник



С появлением систем 100 Гбит/с с когерентным приемом была достигнута почти предельная эффективность использования независимых параметров светового излучения (фазы и поляризации), и усложнение модуляции неизбежно приведет к существенному падению дальности передачи**. То есть снова встает проблема безрегенерационной дальности. Впрочем, новых успехов в системах DWDM можно добиться уплотнением каналов, расширением спектрального диапазона (т.е. подбором других оптопрозрачных материалов), более изощренными методами модуляции и детектирования (называемыми пробабилистическими), а также применением «фотонных кристаллов», многосердцевидных волокон и пр. Нетрудно видеть, что большинство этих решений сопряжено с организацией новой инфраструктуры ВОЛС и соответственно с новыми инвестициями. Разве что помудрить еще со способами модуляции.


Таким образом, в скором времени во всей отрасли дальней связи начнутся серьезные перемены, обусловленные исчерпанием ресурса проложенных кабельных линий. Этот «ползучий» процесс исчерпания емкости наследованной оптической инфраструктуры вкупе с падением тарифов и маржинальности магистрального бизнеса в целом может в ближайшие годы создать новую интригу в развитии этого сегмента и запустить «качели» «спрос – предложение». Ожидаемый результат – интенсификация работ по уплотнению уже существующего оптического волокна и строительству новых оптических линков всех классов.


Так что, господа, без паники!



*Салифов И. Расчет и сравнение сред передачи современных магистральных сетей связи по критерию латентности (задержки). T-Comm № 4 2009 с. 42.
**Трещиков В., Наний О., Леонов А. особенности разработки DWDM-систем высокой емкости. T-Comm № 9 2014 с. 83.


Авторы публикации:
Александр ГОЛЫШКО, системный аналитик ГК «Техносерв»
Виталий ШУБ, заместитель генерального директора, бизнес-направление «Телеком», IPG Photonics Russia (НТО «ИРЭ-Полюс»)

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333602/


Метки:  

Что такое DevOps: подход, который может изменить всё

Среда, 19 Июля 2017 г. 10:15 + в цитатник
Официальной датой рождения термина DevOps принято считать 2009 год, когда в Бельгии впервые прошла конференция “Devopsdays”. Год спустя желающих наберется уже на 4 подобных события. В 2017 году — 47 конференций по всему миру, в том числе в Москве. Так что такое DevOps?

DevOps это не профессия, а культура, философия, метод — набор практик, объединяющий вместе разработчиков программного обеспечения, тестировщиков и людей, отвечающих за его обслуживание. Отсюда название — акроним от “development” и “operations”. Основная цель — уменьшение разрыва между работой всех IT- подразделений компании, оптимизация ответственности за задачи «на стыке» разработки и эксплуатации, повышение производительности, снижение количества ошибок, и, как следствие, удовлетворение потребностей бизнеса и клиента.

Стандартный процесс делится на 5 этапов:
• анализ требований к проекту;
• проектирование;
• реализация;
• тестирование продукта;
• внедрение и поддержка.

При этом взаимодействие, как правило, осуществляется в лучшем случае между соседними. Таким образом, если после выпуска ПО в нём обнаруживаются ошибки, в первую очередь рассматриваются вопросы к железу и системным администраторам, потом к тестировщикам, и лишь потом дело доходит до разработчиков и планировщиков. Когда у вас один релиз в год — ничего страшного, но в современных компаниях производительность достигает десятков выпусков в день. Найти быстрое решение или хотя бы виновника в такой ситуации почти невозможно. Остается только два решения:
забивать штат десятками и сотнями сисадминов и промежуточных работников, которые бы успевали отслеживать каждый релиз, что крайне сомнительно, или же минимизировать ошибки, изменив подход к делопроизводству в целом.

DevOps — современное решение данной проблемы. Он меняет указанный подход на циклический и, в тоже время, сквозной. В таком производственном процессе все этапы взаимосвязаны, требуют четкой организации и контроля. То есть разработчики, помимо основной деятельности, должны участвовать в деплое, системщики — в разработке, тестировщики — в настройке и т.д. Реализация методологии DevOps — задача сложная и кропотливая, реализуемая, как при помощи инструментов, так и специалистов. В этом, кстати, ее главное отличие от Agile, где достаточно лишь изменить образ мышления, организацию труда, не обязательно внедряя дополнительный персонал или программные системы.

До России практика внедрения DevOps начала доходить около 5 лет назад, поэтому потребность в DevOps — инженерах неуклонно растет, конкуренция пока еще низкая, а спрос на специалистов еще не сформировался и так же находится на стадии постоянного роста.

Ещё один плюс изучения DevOps — отсутствие фундаментальной подготовки кадров. Несмотря на привлекательность методологии, многие специалисты — бывшие разработчики, желающие расширить фронт работ. Но куда чаще в DevOps идут системщики, которым мало работы с кодом в shell и bash. В целом, для того, чтобы овладеть этой методологией, не надо иметь серьезного бэкграунда. Достаточно иметь опыт работы на одной из степеней жизни ПО и знать хотя бы в теории смежные дисциплины.

Практической стороне вопроса как раз и посвящен наш курс. Программа разбита на 5 логических уровней. На первом акцентируем внимание на базовых вещах DevOps — чаты, системы контроля версий и задач, визуализация рабочего процесса, облачные сервисы, безопасное удалённое подключение. В общем, всё то, без чего сложно представить современное коллективное программирование. Из продуктов познакомимся поближе с Google Cloud Platform, Packer, Slack, HipChat, Rocketchat и конечно же Git.

После того, как заложим основы разработки и администрирования, переходим на второй уровень, где обсудим настройки и конфигурирование инфраструктуры. На примере утилиты Terraform научимся создавать, изменять и присваивать версии компонентам нашей системы и поработаем с remote backends. Кроме того, на первых двух уровнях рассмотрим модель BSA (Base-Service-App): от базового уровня до применения конфигурацией при помощи Ansible.

На третьем уровне начнём работу с Docker — системой для автоматизации и развёртывания приложений, в частности с Dockerfile,
Hub, Compose. Здесь же рассмотрим понятия непрерывной интеграции и автоматизированного конвейера поставки, немного затронем тестирование контейнеров. Тем самым уже на этом этапе мы заложим правильную организацию выпуска ПО согласно методологии DevOps.

Уровень четвёртый посвящен такой важной процедуре в жизни каждого изделия, как мониторинг с последующим выявлением и записью ошибок. Для этого сначала определимся, что надо отслеживать, рассмотрим наиболее популярные системы мониторинга, алертинга и логирования. Разумеется, на практике узнаем, как их установить, настроить и поработаем с ними.

На последнем пятом уровне, дойдём до вершины DevOps-мастерства — оркестрированию и стратегии деплоя и масштабирования. Для этого нам потребуются Docker Swarm, Service Discovery, Kubernetes и Gitlab CI.

В нашем курсе мы постарались максимально цельно продемонстрировать именно практику организации производства ПО согласно DevOps, ведь именно здесь, возникает наибольшее количество вопросов. Поэтому если вы заинтересовались этой темой и хотите на практике познакомиться с “правильным” циклом разработки и деплоя ПО, самое время приобщиться к культуре DevOps — добро пожаловать на наш курс.

До старта курса осталось меньше месяца!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333684/


Метки:  

[Перевод] Система перемотки времени в стиле Prince of Persia

Среда, 19 Июля 2017 г. 09:44 + в цитатник
image

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

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


Демонстрация основных возможностей

Вам потребуется последняя версия Unity и опыт работы с движком. Исходный код будет выложен в открытый доступ, чтобы вы могли сравнить с ним свои результаты.

Готовы? Поехали!

Как эта система используется в других играх?


Prince of Persia: The Sands of Time стала одной из первых игр со встроенной в геймплей механикой перемотки времени. Когда игрок умирает, он может не только перезапустить игру, но и перемотать её на несколько секунд назад, на момент, когда персонаж ещё жив, и сразу же попробовать снова.


Prince of Persia: The Forgotten Sands. Трилогия Sands Of Time превосходно интегрировала перемотку времени в свой геймплей. Благодаря этому игрок не прерывается на быструю загрузку и остаётся погружённым в игру.

Эта механика интегрирована не только в геймплей, но и в повествование и саму вселенную игры, и упоминается на протяжении всего сюжета.

Похожая система используется в таких играх как Braid, в которой гейплей тоже тесно связан с перемоткой времени. Героиня игры Overwatch Трейсер имеет способность, которая возвращает её на то место, где она была несколько секунд назад, то есть перематывает назад её время, даже в многопользовательской игре. В серии гоночных игр GRID тоже присутствует механика снепшотов: во время гонки у игрока есть небольшой запас перемоток, которые можно использовать, когда машина попадает в серьёзную аварию. Это избавляет игроков от раздражения, возникающего при авариях в конце гонки.


При серьёзном столкновении в GRID вы имеете возможность перемотать игру на момент до аварии

Другие примеры использования


Но эту систему можно использовать не только как замену быстрого сохранения. Ещё один способ использования — реализация «призраков» в гоночных играх и асинхронном многопользовательском режиме.

Реплеи


Это ещё один интересный способ использования функции. Он используется в таких играх как SUPERHOT, в серии Worms и в большинстве спортивных игр.

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

SUPERHOT тоже записывает движения. После прохождения уровня показывается реплей всего игрового процесса, который умещается всего в несколько секунд.

Забавны реплеи в Super Meat Boy. Пройдя уровень, игрок видит все предыдущие попытки, наложенные друг на друга.


Реплей в конце уровня Super Meat Boy. Все предыдущие попытки записываются, а затем воспроизводятся одновременно.

Призраки в гонках на время


Гонка с призраком — это техника, при которой игрок ездит по пустой трассе, стараясь показать наилучшее время. Но в то же время он соперничает с призраком — полупрозрачной машиной, в точности повторяющей путь лучшей предыдущей попытки игрока. С ней невозможно столкнуться, то есть игрок может сконцентрироваться на достижении наилучшего времени.

Чтобы не ездить в одиночку, можно состязаться с самим собой, что делает гонки на время интереснее. Эта функция используется в большинстве гоночных игр, от серии Need for Speed до Diddy Kong Racing.


Гонка с призраком в Trackmania Nations. Это «серебряная» сложность, она означает, что игрок получит серебряную медаль, если обгонит призрака. Заметьте, что модели машин пересекаются, то есть призрак не материален и его можно проехать насквозь.

Призраки в многопользовательских режимах


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

Такой тип соревнований используется в играх Trackmania, где можно гонять на разных сложностях. Такие записанные гонщики становятся противниками, которых нужно победить, чтобы получить награду.

Монтаж съёмки


В некоторых играх перемотка может быть просто забавным инструментом. В Team Fortress 2 есть встроенный редактор реплеев, в котором можно создавать собственные ролики.


Редактор реплеев в Team Fortress 2. Записанный бой можно просмотреть с любой точки зрения, а не только из глаз игрока.

После включения функции можно записывать и просматривать предыдущие матчи. Очень важно, что записывается всё, а не только то, что видит игрок. Это значит, что можно перемещаться по записанному игровому миру, видеть, где все находятся и управлять временем.

Как это реализовать


Для тестирования этой системы нам нужна простая игра. Давайте создадим её!

Игрок


Создайте в сцене куб, это будет персонаж игрока. Затем создайте новый скрипт C# под названием Player.cs и добавьте в функцию Update() следующее:

void Update()
{
    transform.Translate (Vector3.forward * 3.0f * Time.deltaTime * Input.GetAxis ("Vertical"));
    transform.Rotate (Vector3.up * 200.0f * Time.deltaTime * Input.GetAxis ("Horizontal"));
}

Так мы сможем управлять персонажем с помощью стрелок. Прикрепите скрипт к кубу. Теперь после нажатия на Play вы сможете передвигаться. Измените угол камеры, чтобы она смотрела на куб сверху. Наконец, создайте плоскость пола и назначьте каждому объекту свой материал, чтобы мы не двигались в пустоте. Должно получиться что-то подобное:



Попробуйте управлять кубом с помощью WSAD и клавиш со стрелками

TimeController


Теперь создадим новый скрипт C# TimeController.cs и добавлим его к новому пустому GameObject. Он будет управлять записью и перемоткой игры.

Чтобы это сработало, мы запишем движение персонажа игрока. После нажатия кнопки перемотки мы будем изменять координаты персонажа. Для начала создадим переменную, хранящую персонажа:

public GameObject player;

Назначим объект игрока в получившийся слот TimeController, чтобы он мог иметь доступ к игроку и его данным.



Затем нужно создать массив для хранения данных игрока:

public ArrayList playerPositions;

void Start()
{
    playerPositions = new ArrayList();
}

Теперь нам нужно непрерывно записывать положение игрока. У нас будет сохранённое положение игрока в последнем кадре, позиция в которой находился игрок 6 кадров назад и позиция, где был игрок 8 секунд назад (или любое назначенное вами время записи). Когда мы нажмём клавишу воспроизведения, то будем возвращаться обратно по массиву положений и назначать их кадр за кадром, в результате создав функцию перемотки времени.

Для начала давайте сохраним данные:

void FixedUpdate()
{
    playerPositions.Add (player.transform.position);
}

В функции FixedUpdate() мы записываем данные. Используется FixedUpdate(), потому что она выполняется с постоянной частотой 50 циклов в секунду (или любое выбранное значение), что позволяет нам записывать данные с фиксированным интервалом. Функция Update() же выполняется с той частотой, которую обеспечить процессор, что усложнило бы нам работу.

Этот код будет каждый кадр сохранять в массив положение игрока. Теперь нам нужно применить его!

Мы добавим проверку нажатия кнопки перемотки. Для этого нам необходимо булева переменная:

public bool isReversing = false;

И проверка в функции Update():

void Update()
{
    if(Input.GetKey(KeyCode.Space))
    {
        isReversing = true;
    }
    else
    {
        isReversing = false;
    }
}

Чтобы игра выполнялась в обратную сторону, нам нужно применить данные вместо записи. Новый код записи и применения положения игрока должен выглядеть так:

void FixedUpdate()
{
    if(!isReversing)
    {
        playerPositions.Add (player.transform.position);
    }
    else
    {
        player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
        playerPositions.RemoveAt(playerPositions.Count - 1);
    }
}

А весь скрипт TimeController будет выглядеть следующим образом:

using UnityEngine;
using System.Collections;

public class TimeController: MonoBehaviour
{
    public GameObject player;
    public ArrayList playerPositions;
    public bool isReversing = false;

    void Start()
    {
        playerPositions = new ArrayList();
    }

    void Update()
    {
        if(Input.GetKey(KeyCode.Space))
        {
            isReversing = true;
        }
        else
        {
            isReversing = false;
        }
    }
	
    void FixedUpdate()
    {
        if(!isReversing)
        {
            playerPositions.Add (player.transform.position);
        }
        else
        {
            player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
            playerPositions.RemoveAt(playerPositions.Count - 1);
        }
    }
}

Кроме того, не забудьте добавить в класс player проверку того, выполняется ли в TimeController перемотка, чтобы выполнять движение только если оно не воспроизводится. В противном случае поведение может стать странным:

using UnityEngine;
using System.Collections;

public class Player: MonoBehaviour
{
    private TimeController timeController;

    void Start()
    {
        timeController = FindObjectOfType(typeof(TimeController)) as TimeController;
    }
    
    void Update()
    {
        if(!timeController.isReversing)
        {
    	    transform.Translate (Vector3.forward * 3.0f * Time.deltaTime * Input.GetAxis ("Vertical"));
    	    transform.Rotate (Vector3.up * 200.0f * Time.deltaTime * Input.GetAxis ("Horizontal"));
        }
    }
}

Эти новые строки будут при запуске автоматически находить в сцене объект TimeController и проверять его в процессе выполнения. Мы можем управлять персонажем только тогда, когда перемотка не выполняется.

Теперь мы можем перемещаться по миру и перематывать движение назад клавишей «пробел». Можете скачать пакет по ссылке в конце статьи и открыть TimeRewindingFunctionality01, чтобы проверить работу!

Но постойте, почему наш простой игрок-кубик продолжает смотреть в последнем направлении, в котором мы его оставили? Потому что мы не догадались записывать поворот объекта!

Для этого нам нужен ещё один создаваемый в начале массив, чтобы сохранять и применять данные.

using UnityEngine;
using System.Collections;

public class TimeController: MonoBehaviour
{
    public GameObject player;
    public ArrayList playerPositions;
    public ArrayList playerRotations;
    public bool isReversing = false;
    
    void Start()
    {
        playerPositions = new ArrayList();
        playerRotations = new ArrayList();
    }
    
    void Update()
    {
        if(Input.GetKey(KeyCode.Space))
        {
            isReversing = true;
        }
        else
        {
            isReversing = false;
        }
    }
    
    void FixedUpdate()
    {
        if(!isReversing)
        {
            playerPositions.Add (player.transform.position);
            playerRotations.Add (player.transform.localEulerAngles);
        }
        else
        {
            player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
            playerPositions.RemoveAt(playerPositions.Count - 1);
    
            player.transform.localEulerAngles = (Vector3) playerRotations[playerRotations.Count - 1];
            playerRotations.RemoveAt(playerRotations.Count - 1);
        }
    }
}

Попробуйте запустить! TimeRewindingFunctionality02 — это усовершенствованная версия. Теперь наш игрок-куб может двигаться назад во времени и будет выглядеть точно так, как выглядел в соответствующий момент.

Заключение


Мы создали простой прототип игры с уже вполне рабочей системой перемотки времени, но она ещё далека от совершенства. Далее мы сделаем её гораздо более стабильной и универсальной, а также добавим интересные эффекты.

Вот что нам ещё предстоит сделать:

  • Записывать только каждый 12-й кадр и интерполировать состояния между записанными кадрами, чтобы объём данных не был слишком огромным
  • Записывать только последние 75 положений и поворотов игрока, чтобы массив не стал слишком громоздким и игра не вылетала

Кроме того, мы подумаем, как расширить эту систему, чтобы она действовала не только для игрока:

  • Как записывать не только одного игрока
  • Добавим эффект, сообщающий о перемотке (типа размытия VHS-сигнала)
  • Используем для хранения положения и поворота игрока собственный класс, а не массивы

Архив проекта Unity



Итак, мы создали простую игру, в которой можно перематывать время до предыдущей точки. Теперь мы можем усовершенствовать эту функцию и сделать её использование гораздо интереснее.

Запись меньшего объёма данных и интерполяция


На данный момент мы записываем положения и повороты игрока 50 раз в секунду. Такой объём данных быстро станет неподъёмным, и это будет особенно заметно в более сложных играх, а также на слабых мобильных устройствах.

Вместо этого мы можем выполнять запись только 4 раза в секунду и интерполировать положение и поворот между этими ключевыми кадрами. Таким образом мы сэкономим 92% производительности, а результаты будут на вид неотличимы от записей с 50 кадрами в секунду, потому что они воспроизводятся за доли секунды.

Начнём с записи ключевых кадров через каждые x кадров. Для этого нам сначала нужны новые переменные:

public int keyframe = 5;
private int frameCounter = 0;

Переменная keyframe — это кадр в методе FixedUpdate, в который мы будем записывать данные игрока. В настоящий момент ей присвоено значение 5, то есть данные будут записываться на каждом пятом цикле выполнения метода FixedUpdate. Поскольку FixedUpdate выполняется 50 раз в секунду, то за секунду будет записываться 10 кадров. Переменная frameCounter будет использоваться как счётчик кадров до следующего ключевого кадра.

Теперь изменим блок записи в функции FixedUpdate, чтобы он выглядел вот так:

if(!isReversing)
{
    if(frameCounter < keyframe)
    {
        frameCounter += 1;
    }
    else
    {
        frameCounter = 0;
        playerPositions.Add (player.transform.position);
        playerRotations.Add (player.transform.localEulerAngles);
    }
}

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

Для начала нам нужна ещё одна переменная frameCounter, чтобы не записывать данные, а воспроизводить их.

private int reverseCounter = 0;

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

void FixedUpdate()
{
    if(!isReversing)
    {
        if(frameCounter < keyframe)
        {
            frameCounter += 1;
        }
        else
        {
            frameCounter = 0;
            playerPositions.Add (player.transform.position);
            playerRotations.Add (player.transform.localEulerAngles);
        }
    }
    else
    {
        if(reverseCounter > 0)
        {
            reverseCounter -= 1;
        }
        else
        {
            player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
            playerPositions.RemoveAt(playerPositions.Count - 1);

            player.transform.localEulerAngles = (Vector3) playerRotations[playerRotations.Count - 1];
            playerRotations.RemoveAt(playerRotations.Count - 1);
            
            reverseCounter = keyframe;
        }
    }
}

Теперь при перемотке времени игрок будет перескакивать к предыдущим положениям в реальном времени!

Но мы хотели не совсем этого. Нам нужно интерполировать положения между этими ключевыми кадрами, что будет немного сложнее. Во-первых, нам потребуются четыре переменные:

private Vector3 currentPosition;
private Vector3 previousPosition;
private Vector3 currentRotation;
private Vector3 previousRotation;

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

Затем нам понадобится эта функция:

void RestorePositions()
{
    int lastIndex = keyframes.Count - 1;
    int secondToLastIndex = keyframes.Count - 2;

    if(secondToLastIndex >= 0)
    { 
        currentPosition  = (Vector3) playerPositions[lastIndex];
        previousPosition = (Vector3) playerPositions[secondToLastIndex];
        playerPositions.RemoveAt(lastIndex);
        
        currentRotation  = (Vector3) playerRotations[lastIndex];
        previousRotation = (Vector3) playerRotations[secondToLastIndex];
        playerRotations.RemoveAt(lastIndex);
    }
}

Она присваивает соответствующую информацию переменным положения и поворота, между которыми мы будем выполнять интерполяцию. Мы будем делать это в отдельной функции, потому что мы вызываем её для двух разных точек.

Блок восстановления данных будет выглядеть так:

if(reverseCounter > 0)
{
    reverseCounter -= 1;
}
else
{
    reverseCounter = keyframe;
    RestorePositions();
}

if(firstRun)
{
    firstRun = false;
    RestorePositions();
}

float interpolation = (float) reverseCounter / (float) keyframe;
player.transform.position = Vector3.Lerp(previousPosition, currentPosition, interpolation);
player.transform.localEulerAngles = Vector3.Lerp(previousRotation, currentRotation, interpolation);

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

if(firstRun)
{
    firstRun = false;
    RestorePositions();
}

Чтобы он работал, нам также понадобится переменная firstRun:

private bool firstRun = true;

И для сброса при отпускании клавиши «пробел»:

if(Input.GetKey(KeyCode.Space))
{
    isReversing = true;
}
else
{
    isReversing = false;
    firstRun = true;
}

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

Интерполяция выполняется функцией Lerp, которой мы передаём текущее и предыдущее положение (или поворот). Затем вычисляется коэффициент интерполяции, который может иметь значения от 0 до 1. Затем игрок помещается между двумя сохранёнными точками, например, в 40% на пути к последнему ключевому кадру.

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

Таким образом мы значительно снизили сложность схемы перемотки времени и сделали её гораздо более стабильной.

Запись только фиксированного количества кадров


Сильно уменьшив количество сохраняемых кадров, теперь мы можем сделать так, чтобы система не сохраняла слишком много данных.

Сейчас это просто куча записанных в массив данных, не рассчитанная на долговременное использование. С ростом массива он становится всё более громоздким и доступ занимает больше времени, а вся система оказывается нестабильной.

Чтобы исправить это, мы можем добавить код, проверяющий, не разросся ли массив больше определённого размера. Если мы будем знать, сколько кадров в секунду мы сохраняем, то можем определить, сколько секунд перематываемого времени нужно хранить, чтобы это не мешало игре и не увеличивало её сложность. В довольно сложной Prince of Persia время перемотки ограничено примерно 15 секундами, а в более технически простой игре Braid перемотка может быть бесконечной.

if(playerPositions.Count > 128)
{
    playerPositions.RemoveAt(0);
    playerRotations.RemoveAt(0);
}

Когда массив превзойдёт определённый размер, мы будем удалять его первый элемент. Поэтому он всегда будет хранить столько данных, сколько может перемотать игрок, и не будет мешать эффективности. Вставим этот код в функцию FixedUpdate после кода записи и воспроизведения.

Использование собственного класса для хранения данных игрока


Пока мы записываем положение и поворот игрока в два отдельных массива. Хотя это и работает, нам нужно постоянно помнить, что мы записываем и считываем данных из двух мест одновременно, что может привести к проблемам в будущем. Однако мы можем создать отдельный класс для хранения всех этих данных, а в перспективе — и других (если это будет необходимо для проекта).

Код собственного класса, который будет использоваться как контейнер для данных, выглядит следующим образом:

public class Keyframe
{
    public Vector3 position;
    public Vector3 rotation;

    public Keyframe(Vector3 position, Vector3 rotation)
    {
        this.position = position;
        this.rotation = rotation;
    }
}

Можно добавить его в файл TimeController.cs file перед началом объявления классов. Он создаёт контейнер для сохранения положения и поворота игрока. Конструктор позволяет создать его напрямую со всех необходимой информацией.

Остальную часть алгоритма нужно адаптировать под работу с новой системой. В методе Start необходимо инициализировать массив:

keyframes = new ArrayList();

И вместо:

playerPositions.Add (player.transform.position);
playerRotations.Add (player.transform.localEulerAngles);

мы можем сохранять непосредственно в объект Keyframe:

keyframes.Add(new Keyframe(player.transform.position, player.transform.localEulerAngles));

Здесь мы добавляем положение и поворот игрока в один объект, который затем добавляется в единый массив, что значительно снижает сложность алгоритма.

Добавление эффекта размывания, обозначающего включенную перемотку


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

Давайте сделаем что-то в стиле Prince of Persia добавив немного размывания.


Перемотка времени в Prince of Persia: The Forgotten Sands

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

Для использования базовых эффектов их сначала нужно импортировать. Для этого зайдём в Assets > Import Package > Effects и импортируем всё, что нам предлагают.



Визуальные эффекты можно применять непосредственно к камере. Перейдите в Components > Image Effects и добавьте эффекты Blur и Bloom. Их сочетание создаёт хороший эффект, к которому мы стремимся.



Это базовые настройки. Можете настроить их в соответствии со своим проектом.

Если попробовать запустить игру сейчас, то она будет использовать эффект постоянно.



Теперь нам нужно научиться включать и отключать его. Для этого необходимо импортировать в TimeController эффекты изображения. Добавляем их в самое начало:

using UnityStandardAssets.ImageEffects;

Чтобы получить доступ к камере из TimeController, добавим эту переменную:

private Camera camera;

И присвоим ей значение в функции Start:

camera = Camera.main;

Затем добавим этот код для включения эффектов при перемотке времени:

void Update()
{
    if(Input.GetKey(KeyCode.Space))
    {
        isReversing = true;
        camera.GetComponent().enabled = true;
        camera.GetComponent().enabled = true;
    }
    else
    {
        isReversing = false;
        firstRun = true;
        camera.GetComponent().enabled = false;
        camera.GetComponent().enabled = false;
    }
}

При нажатии клавиши «пробел» теперь вы не только будете перематывать время в сцене, но и активируете эффект перемотки камеры, сообщая игроку о происходящем.

Весь код TimeController должен выглядеть следующим образом:

using UnityEngine;
using System.Collections;
using UnityStandardAssets.ImageEffects;

public class Keyframe
{
    public Vector3 position;
    public Vector3 rotation;

    public Keyframe(Vector3 position, Vector3 rotation)
    {
        this.position = position;
        this.rotation = rotation;
    }
}

public class TimeController: MonoBehaviour
{
    public GameObject player;
    public ArrayList keyframes;
    public bool isReversing = false;
    
    public int keyframe = 5;
    private int frameCounter = 0;
    private int reverseCounter = 0;
    
    private Vector3 currentPosition;
    private Vector3 previousPosition;
    private Vector3 currentRotation;
    private Vector3 previousRotation;
    
    private Camera camera;
    
    private bool firstRun = true;
    
    void Start()
    {
        keyframes = new ArrayList();
        camera = Camera.main;
    }
    
    void Update()
    {
        if(Input.GetKey(KeyCode.Space))
        {
            isReversing = true;
            camera.GetComponent().enabled = true;
            camera.GetComponent().enabled = true;
        }
        else
        {
            isReversing = false;
            firstRun = true;
            camera.GetComponent().enabled = false;
            camera.GetComponent().enabled = false;
        }
    }
    
    void FixedUpdate()
    {
        if(!isReversing)
        {
            if(frameCounter < keyframe)
            {
                frameCounter += 1;
            }
            else
            {
                frameCounter = 0;
                keyframes.Add(new Keyframe(player.transform.position, player.transform.localEulerAngles));
            }
        }
        else
        {
            if(reverseCounter > 0)
            {
                reverseCounter -= 1;
            }
            else
            {
                reverseCounter = keyframe;
                RestorePositions();
            }
    
            if(firstRun)
            {
                firstRun = false;
                RestorePositions();
            }
    
            float interpolation = (float) reverseCounter / (float) keyframe;
            player.transform.position = Vector3.Lerp(previousPosition, currentPosition, interpolation);
            player.transform.localEulerAngles = Vector3.Lerp(previousRotation, currentRotation, interpolation);
        }
    
        if(keyframes.Count > 128)
        {
            keyframes.RemoveAt(0);
        }
    }
    
    void RestorePositions()
    {
        int lastIndex = keyframes.Count - 1;
        int secondToLastIndex = keyframes.Count - 2;
    
        if(secondToLastIndex >= 0)
        {
            currentPosition  = (keyframes[lastIndex] as Keyframe).position;
            previousPosition = (keyframes[secondToLastIndex] as Keyframe).position;
    
            currentRotation  = (keyframes[lastIndex] as Keyframe).rotation;
            previousRotation = (keyframes[secondToLastIndex] as Keyframe).rotation;
    
            keyframes.RemoveAt(lastIndex);
        }
    }
}

Скачайте пакет с проектом и попробуйте поэкспериментировать с ним.

Подводим итог


Наша игра с перемоткой времени стала гораздо лучше. Алгоритм значительно усовершенствован, потребляет на 90% меньше вычислительной мощности и при этом намного стабильнее. Мы добавили интересный эффект, сообщающий игроку о том, что выполняется перемотка времени.

Настало время сделать на его основе настоящую игру!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333594/


Метки:  

С/С++ на Linux в Visual Studio Code для начинающих

Среда, 19 Июля 2017 г. 09:37 + в цитатник
Давайте начистоту, мало кто использует отладчик GDB на Linux в консольном варианте. Но что, если добавить в него красивый интерфейс? Под катом вы найдёте пошаговую инструкцию отладки кода С/С++ на Linux в Visual Studio Code.



Передаю слово автору.

Относительно недавно я переехал на Linux. Разрабатывать на Windows, конечно, удобнее и приятнее, но и здесь я нашел эффективный способ легко и быстро отлаживать код на С/С++, не прибегая к таким методам как «printf-стайл отладки» и так далее.

Итак приступим. Писать в sublime (или gedit/kate/emacs), а запускать в терминале — так себе решение, ошибку при работе с динамическим распределением памяти вряд ли найдёшь с первого раза. А если проект трудоёмкий? У меня есть более удобное решение. Да и ещё поддержка Git в редакторе, одни плюсы.

Сегодня мы поговорим про Visual Studio Code.

Установка


Ubuntu/Debian
  1. Качаем версию пакета VS Code с расширением .deb
  2. Переходим в папку, куда скачался пакет (cd ~/Загрузки или cd ~/Downloads)
  3. Пишем, где (имя пакета).deb — название файла, который вы только что скачали:
    sudo dpkg -i (имя пакета).deb
    	sudo apt-get install -f

OpenSUSE/SLE Based distrs
  1. Установим репозиторий:
    sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
    sudo sh -c 'echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/VScode\nenabled=1\ntype=rpm-md\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/zypp/repos.d/VScode.repo'
  2. Обновим пакеты и установим VS Code:
    sudo zypper refresh
    sudo zypper install code

Расширения для С/С++


Чтобы VS Code полностью сопровождал нас при работе с файлами С/С++, нужно установить расширение «cpptools». Также полезным будет поставить один из наборов сниппетов.



Настоятельно рекомендую включить автосохранение редактируемых файлов, это поможет нам в дальнейшем.



Идём дальше. Открываем любую папку (новую или нет, неважно).



У меня в этой папке уже есть пара файлов для работы с C/C++. Вы можете скопировать одну из своих наработок сюда или создать новый файл.



Осталось всего ничего. Настроить компиляцию в одну клавишу и научиться отлаживать без printf.

Шаг 1. Открываем файл .c/.cpp, который (обязательно) лежит в вашей папке.

Шаг 2. Нажимаем Ctrl+Shift+B. VS Code вам мягко намекнет, что он не знает как собирать ваш проект.


Шаг 3. Поэтому дальше настраиваем задачу сборки: выбираем «Настроить задачу сборки» -> «Others».

Шаг 4. Прописываем конфигурацию в соответствии с образцом. По сути мы пишем скрипт для консоли, так что всем кто имел дело с ней будет понятно дальнейшее. Прошу заметить, что для сборки исходников в системе должен стоять сам компилятор (gcc или другой, отличаться будет только значение поля command). Поэтому для компиляции .cpp, понадобится в поле command указать g++ или c++, а для .c gcc.

Шаг 5. В args прописываем аргументы, которые будут переданы на вход вашему компилятору. Напоминаю, что порядок должен быть примерно таким: -g, <имя файла>.

Внимание: Если в вашей программе используется несколько файлов с исходным кодом, то укажите их в разных аргументах через запятую. Также обязательным является ключ -g(а лучше даже -g3). Иначе вы не сможете отладить программу.

Если в проекте для сборки вы используете makefile, то в поле command введите make, а в качестве аргумента передайте директиву для сборки.


Шаг 6. Далее возвращаемся обратно к нашему исходнику. И нажимаем F5 и выбираем C++.



Шаг 7. Осталось только написать путь к файлу программы. По умолчанию это ${workspaceRoot}/a.out, но я в своем файле сборки указал флаг -o и переименовал файл скомпилированной программы, поэтому у меня путь до программы: ${workspaceRoot}/main.


Шаг 8. Всё, больше нам не нужно ничего для начала использования всех благ VS Code. Переходим к основному проекту.

Отладка


Для начала скомпилируем программу (нет, нет, убери терминал, теперь это делается по нажатию Ctrl+Shift+B).



Как вы видите в проводнике появился main, значит все в порядке и сборка прошла без ошибок. У меня не слишком большая программа, но выполняется она моментально. Одним словом, провал чистой воды, потому что отладка идет в отдельном терминале, который закрывается после того, как программа дошла в main() до "return 0;".


Пришло время для брейкпоинтов. Выберем строчку с "return 0;" и нажимаем F9.


Строчка, помеченная красной точкой слева — место, где остановится программа, при выполнении.

Далее нажимаем F5.



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



Удобненько. Также при остановке можно наводить мышкой на переменные и структуры в коде и смотреть их значения.


Также, если на каком-то этапе выполнения вам нужно посмотреть пошаговое выполнение той или иной операции, например в цикле, то поставьте брейкпоинт перед ней и нажмите F10 для выполнения текущей строчки без захода в подпрограмму и F11 с заходом.

Также есть случаи, когда считать выражение очень муторно вручную, но для отладки вам нужно знать, например, значение суммы трех элементов массива, или значение большого логического выражения. Для этого существуют контрольные значения. Все это и многое другое могут показать вам Контрольные значения (или «watch»).



Важно:
  1. Для каждой папки вам нужно отдельно настроить файлы сборки и путь к программе.
  2. VS Code не решит ваших проблем, но поможет быстрее с ними разобраться. Причем в разы.
  3. После каждого изменения программы, ее нужно компилировать заново, нажимая Ctrl+Shift+B.

Полезные шорткаты можно посмотреть здесь.

Об авторе



Максимилиан Спиридонов — разработчик C#, студент МАИ, Microsoft Student Partner. В профессиональную разработку на .NET пришёл ещё в школе. Около года работал с реальными проектами на WPF(MVVM)+C#, MySQL, более 4-х лет разрабатывал на C#. Основная сфера интересов сейчас — это мобильная разработка на Xamarin. Также, по воле случая в сфере интересов оказались С/С++ и Linux.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333680/


Метки:  

Сказ о проектном менеджере в банке и как он решил проблемы с удаленным подрядчиком

Среда, 19 Июля 2017 г. 08:21 + в цитатник
Жил был проектный менеджер. Работал он в банке. Поручили сделать ему внешний продукт для банка этого. Нужно было менеджеру найти удаленную команду разработки, которая сделает его ему. Команду разработки он нашел, ТЗ написал. Начали они работать вместе. Работа в первое время шла хорошо. Спринты закрывались в срок, качество не страдало.

А потом с подрядчиками что-то случилось. Толи они поменяли менеджера, толи разработчиков перекинули на другой проект и оставили ему джунов, толи еще что-то. Процессы стали выполняться медленнее, сроки продалбыватся, качество стало хуже. Стал думать менеджер, что же случилось и как бы ему решить свою проблему? Пошел он к старому приятелю своему, у которого компания занимается ИТ аутсорсингом. Рассказал он о проблеме своей. И приятель вспомнил, что недавно он писал документ для своей команды, где расписал о том, как коммуницировать с заказчиком, чтобы это было эффективно и качество не страдало. Поделился он документом с менеджером. А менеджер прочитал его и с минимальными правками отправил письмо подрядчику.


Коллеги, здравствуйте

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

  1. Встать на сторону заказчика. Это значит, что нужно одевать на себя шапку заказчика и будучи в его шкуре представить мотивы его требований и задач. Мотивы могут быть очевидные, а могут быть и скрытые. Если чувствуете, что в задаче нет дополнительной информации для полного понимания – надо о ней спрашивать и фиксировать в исходном документе о задаче, который размещен на гугл диске. Все должно быть прозрачно ясно.
    • для пээмов: если в процессе получения задачи или юзер стори вы чувствуете спорные моменты или что-то упускаете, то постарайтесь получить всю информацию, чтобы быть с заказчиком on the same page. Заказчик может говорить одно, подразумевать другое, а ожидать третье.
    • для остальных: для ребят разработчиков/дизайнеров тоже полезно перед началом работы над задачей представить себя заказчиком и понять его мотивы. Это такой простой и недолгий мысленный эксперимент и он поможет вам глубоко и ясно понять задачу. Если же вы получили задачу и она, в принципе, понятна, но вы не видите общей картины и не чувствуете как она в целом повлияет на процесс – обязательно сообщите об этом пээму.
  2. Честность, уважение и сдержанность.
    • честность: своевременно говорите о проблемах и о отставаниях, чтобы заказчик на своей стороне успел мобилизоваться. От ошибок никто не застрахован, но их замалчивание расценивается как не профессионализм.
    • уважение: заказчик может быть со своими тараканами в голове, может иногда тупить. Но раз уж он работает с вами, это что-то значит.
    • сдержанность: старайтесь сдерживать свои эмоции в общении и в переписке.
  3. Коммуникация! Коммуникация! Коммуникация! (хотелось бы сразу заметить, что под коммуникацией ни в коем разе не подозревается пустая болтовня и бездумные переговоры). Ниже приведу выдержки из книги Remote, так как мы с вами работаем удаленно:
    • Почаще рассказывайте клиенту, что сделано по проекту. Это лучший способ избавить его от вполне естественной тревоги. Послушайте, он же платит вам приличные деньги, и некоторое беспокойство, которое он ощущает с момента расставания с авансом, вполне понятно. Так что показывайте ему то, за что он платит. Когда клиенты регулярно видят результаты ваших усилий, они гораздо лучше себя чувствуют. Но для того чтобы избежать постоянных созвонов, НУЖНО четко и структурированно вести работу над задачами в нашем инструменте – Трак:
    • Задача должна быть оформлена полностью по шаблону (что нужно сделать, как это сделать, критерии готовности и прочее)
    • У задачи должны быть проставлены все пункты (milestone, планируемый срок исполнения, компоненты, номер релиза, дата и прочее)
    • У задачи должен быть виден прогресс, то есть если разработчик проставил задачу в статус inProgress и работает над ней, то я ожидаю увидеть, что он по ней сделал в течении дня (это минимум 2 комментария к задаче в день, которые понятно раскроют суть сделанного)
    • Будьте подчеркнуто доступны для общения. Поскольку у нас нет возможности встречаться лично, лучше вовремя перезванивать, отвечать на электронную почту, отзываться в мессенджерах и так далее. Это азы деловой этики, и их важность десятикратно усиливается в случае удаленной работы. Если вы работаете удаленно, клиенты более подозрительно относятся к оставшимся без ответа звонкам и «потерянной» почте. Будьте на связи, это в ваших интересах. Для того чтобы плыть в одном русле теперь каждый день у нас с вами в 9:30 будет видеоконференция (это будет стенд-ап не больше чем на 10 минут, чтобы сверить часы, услышать проблемы и понять куда движемся). Присутствие всей команды обязательно.
    • Подключайте заказчика к работе, пусть он видит весь процесс. Пусть чувствует, что это и его проект тоже. Да, вас наняли из-за вашего опыта, но и у него опыта хватает. Показывайте полусырой, еле двигающийся прототип или сервис, лучше на начальном этапе понять что не так и потратить меньше ресурсов и сил на переделку. Я как заказчик понимаю, что такое прототип и на какие вещи не стоит обращать внимание, здесь важно базовое понимание работы, а детали это детали.



В результате дела пошли хорошо. И причем команда девелоперов была довольна этим регламентом больше, чем сам автор письма. Без четких правил и дисциплины любой процесс взаимодействия разваливается. Хорошо бы эти правила командам разработчиков иметь внутри себя и придерживаться их, а не ждать пока это сделает кастомер.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333674/


Метки:  

Книга «Python. Уроки»

Среда, 19 Июля 2017 г. 07:59 + в цитатник
Добрый день, коллеги!
Я являюсь одним из авторов блога devpractice.ru. В конце апреля 2017 года нами была написана завершающая статья из цикла «Уроки по языку Python», всего их получилось 15 штук. Спустя какое-то время, у нас появилась идея объединить весь накопленный материал. Так появилось первое издание книги «Python. Уроки». Эту книгу мы решили распространять бесплатно и предлагаем вам ознакомиться с ней.
Скачать «Python. Уроки» вы можете непосредственно с сайта. В книге основной акцент сделан на обучение базовым вещам (установка и запуск программ, типы данных, условные операторы и циклы, структуры данных, функции, исключения), темы, связанные с ООП, итераторами и генераторами даны в ознакомительном объеме.

Краткое содержание книги:
Урок 1. Установка
Урок 2. Запуск программ на Python
Урок 3. Типы и модель данных
Урок 4. Арифметические операции
Урок 5. Условные операторы и циклы
Урок 6. Работа с IPython и Jupyter Notebook
Урок 7. Работа со списками (list)
Урок 8. Кортежи (tuple)
Урок 9. Словари (dict)
Урок 10. Функции в Python
Урок 11. Работа с исключениями
Урок 12. Ввод-вывод данных. Работа с файлами
Урок 13. Модули и пакеты
Урок 14. Классы и объекты
Урок 15. Итераторы и генераторы

Будем рады любой обратной связи!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333672/


Метки:  

Как я был обманут, связавшись с дизайнером Ramin Nasibov (Berlin, Germany)

Среда, 19 Июля 2017 г. 02:21 + в цитатник


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

12 июня через Twitter я получил сообщение с предложением о сотрудничестве:


Are looking for a freelance designer and social media manager? Professional Branding and Social Media Design. Porfolio: nasibov.me If you have any questions please email me: ramin@nasibov.me


Я нуждался в обновлении изображений на моем сайте vialatm.com. Познакомившись с его представительством в соц. сетях, я обратился с вопросом о стоимости работ. Практически сразу я получил предложение обсудить все в Skype митинге. Через 30 минут мы провели переговоры в Skype. Оказалось что дизайнер свободно говорит на русском. Мы пришли к соглашению, что в течении недели он подготовит оригинальные изображения для моего сайта. Он запросил оплату в размере 50% от общей стоимости работ. Я предлагал провести оплату через PayPal, но он настоял на оплате через Payoneer.


Через два часа я получил запрос на оплату работ и сразу произвел оплату:




После этого общаться с ним стало весьма проблематично. То он не отвечал потому, что был далеко от компьютера, то он не появлялся на оговоренных Skype митингах из-за проблем с интернетом.


22 июня, в связи с отсутствием каких-либо результатов и неудачных попыток связаться и пообщаться с ним, я попросил его вернуть авансовый платеж.


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


Полное молчание. Я был вынужден приобрести дизайн сайта в другой компании и купить изображения на iStock.


25 июня я направил жалобу в платежную систему Payoneer. И вот после этого он объявился. Через Skype от него было получено сообщение:


Hello Andrey. As a result of the action of the group by the scammer and the burglars, I lost access to my own social networks, mail and Skype. Unfortunately you had to communicate with scammers on my behalf. As a result, not only you, but also other users were deceived. The criminals professionally mimicked my content in social networks and in contacting clients requested advances. At the moment, with the help of a lawyer and right services, I was able to return access to my social networks and Skype. After gaining access back I was shocked by the correspondence and the number of cheated people. At the moment, I draft an application to the relevant authorities and contact the deceived customers. Since the fraudsters in a certain way managed to get money, I have to work for clients for free and without any fees. I’m really sorry Andrey. Are you interested in receiving your order?


Вкратце суть его в том, что его акаунтами завладели мошенники, общался я с мошенниками, с помощью адвокатов он вернул доступ к своим социальным сетям и прочая чушь!:


Все это время, в Twitter он публиковал свои новые работы, а в Facebook делился фотографиями (кошки, кресла, ...). А я все свои последние сообщения ему дублировал по эл. почте, в Twitter и Skype.


28 июня он предложил в течении 1-2 дней выполнить работы.


Но я отказался и потребовал возвращения денег (в течении двух недель после оплаты я даже никакого эскиза не получил, и он откликнулся только после моего обращения в Payoneer).
После этого он окончательно исчез.


Я еще раз решил взглянуть на работы этого дизайнера и мое внимание привлекло публикация в Twitter — работы для компании «Coffeepolitan» https://twitter.com/RaminNasibov Никакого труда не составило найти подобную картинку с картой мира на кофе на сайте — Shutterstoc




Чашка и деревянный фон на картинках отличаются (но чашек и фона из деревяшек найти вообще труда не составляет), а вот картинки карты мира на кофе совпадают до мелочей.


Очень интересно какое изображение является оригиналом, а какое копией?


Между прочим этот дизайнер имеет два сайта http://nasibov.me/ и http://pixelin.me/ с impressum


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

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333668/


Метки:  

Доработки шаблонизатора DoT.js

Вторник, 18 Июля 2017 г. 23:44 + в цитатник
Время зоопарка шаблонизаторов миновало, теперь вокруг бегают динозаврики MVC, а в них используются встроенные шаблонизаторы и билдеры компонентов. Но для замены старых менее удобных шаблонизаторов в Knockout и Backbone иногда нужны они, в основном, остановившиеся в развитии на уровне около 2014 года.

Так случилось и с DoT.js. Поначалу заброшенный авторами примерно на год в 2013-м, он получил их внимание ненадолго, поднявшись с версии 1.0.1 до 1.1.1, и снова был заброшен (или стабилизирован, смотря как рассуждать). В связи с этим ещё в 2013 году понадобилось (делать клон DoT.js), а теперь — и апгрейдить его.

Он — такой же быстрый, как и встроенный _.template() в Underscore/Lodash, но с улучшенным синтаксисом, при котором необходимость писать JS в шаблонах встречается нечасто, а в Underscor-овском — нужна всегда. Этим скобкам со скриптами даже придумали специальный термин: javascript encapulated sections (JES), и от них, в основном, избавились.

Что получаем дополнительно?


1. Структура шаблонизатора была переработана (в 2013-м, ссылка оттуда), чтобы лучше читалась и уменьшилось число декодирований функций;
2. Тесты показали, что быстродействие в среднем не изменилось (колебания -3% — +10% в зависимости от параметров);
3. Добавлена команда работы по структуре, аналогично работе по массиву;
4. 4-й параметр — фильтр элементов структуры или массива;
5. Кое-где замедления вследствие обхода багов скомпенсированы оптимизацией кода и регекспов;
6. Глобальное имя «doT» способно меняться на другое в настройках (у оригинала — нет);
7. Наведён порядок в нумерации версий и версионировании глобальных функций encodeHTML() — инстансов, принятых здесь для оптимизации.

Для справки по нумерации версий



В npm копия версии 1.1.1 один-в-один в package.json названа как 1.1.2, но в файле — остался номер 1.1.1; в ветке 2.0 в репо — то же самое с необновлением номера 1.1.1 и есть всего 1 отличие в var rw = unescape(...). В общем, всё сделано для неразберихи. Поэтому считаем, что самая новая версия — 1.1.1, в которой учтём отличие из ветки 2.0. Ветка 2.0 своего звания не заслуживает.


По случаю, удобно сделать документацию (есть от авторов), с инструментом для её проверки. (Что в Сети нашлось читабельного — ссылки внизу.) Если кратко:

• он сохранил «исконный» синтаксис Underscore _.template(), в котором внутри скобок "{{ ... }}" можем писать любой JS-код, включая незакрытые фигурные и операторные скобки, а снаружи скобок — HTML-фрагменты текста.
• скобки переопределить можно, переопределив в настройках все регекспы с ними (обычно не нужно);
• имя старшего элемента структуры 'it' тоже переопределить можно, как и 4 логических настройки поведения;
• поддерживается AMD, commonJS и просто глобальное имя его ('doT');
• кроме базового универсального синтаксиса, имеет ряд команд, подобных стилю Mustache/Handelbars;
• как и они, и _.template(), имеет 2 этапа шаблонизации (каррирования параметров) — в функцию и затем в HTML (или другой) код;
• не заточен строго под HTML, но привязан к JS, поэтому его сфера — браузеры и NodeJS;
• не намного больше по объёму, чем исходный код _.template() — 3.4 К в сжатом не зипованном виде.

(Для экспериментов с кодом нового или старого шаблонизатора можно воспользоваться старым примером в http://jsfiddle.net/6KU9Y/2/ (но далее есть js-фиддл лучше). Построена и в репозитории удобная страница тестирования 2 движков на общем шаблоне и данных — в test/index.html. По умолчанию она сравнивает одинаковость результатов в развёрнутой и минифицированной версиях файлов. И второй движок может работать лишь с клоном, т.к. у него возможно глобальное имя, отличное от 'doT'. Вместо минифицированной можно поставить клон, а на первом месте, например, оригинальный DoT.js 1.1.1 и смотреть различия в парсинге.)
image

В файле второго движка нужно перед открытием страницы установить имя globalName:'doTmin'.

Впрочем, и в онлайне то же самое несложно: https://jsfiddle.net/spmbt/v3yvpbsu/22/embedded/#Result или с фреймами и редактированием кода, у кого большие экраны: https://jsfiddle.net/spmbt/v3yvpbsu/22/. Файлы подключать не надо, лишь копипастить содержимое 2 версий DoT.js, а во второй, удобнее это делать в клоне — подправить имя doT на doTmin (даже если не minimized). По умолчанию выставлены DoT.js 1.1.1 оригинал и DoT12.js 1.2.1 — клон.

Бенчмарк (3-я кнопка) устроен по короткому промежутку времени, поэтому зависит от параллельных процессов, и о скорости синхронных алгоритмов можно судить при усреденении по большому числу измерений (среднее накапливается и позволяет немного судить о скоростях). Встроенный в проект бенчмарк есть, но он сложный, через Ноду, а делает то же самое (по группе тестовых файлов).

В страницу тестов встроены чекбокс автообновления и перехват ошибок, что позволяет проводить тесты по той версии, которая останется исправной. Так, легко сравнить скорости команд '~' и '@' по массиву. Вторая тоже работать может, но значительно медленнее — на 10-15% (проверяется кнопкой «Bench»). Это связано с необходимостью использовать более медленный цикл for-in во втором случае. Тем не менее, без for-in для структур не обойтись, чтобы не иметь необходимости подготовки массивов из структур для оригинальной версии, не имеющей команды '@'.

Сразу традиционно заметим, что 2-е вычисление (расчёт по компилированному шаблону) идёт в сотню раз быстрее (для коротких выражений), чем полная компиляция каждый раз (1-е число, по в 100 раз меньшему числу измерений). На скриншоте "Comp1e3: 99.22ms" означает: «1000 полных компиляций проведены за время 99.22ms». "Run5e5: 69.08 ms" в 5-й строчке означает: «50 тысяч быстрых генераций HTML по шаблону проведены за усреднённое время 69.08ms на каждые 10 тысяч генераций».

Добавлен спадающий список примеров, из которого можно извлечь типичные примеры применения команд DoT.js и протестировать их в 2 версиях скрипта. Изменения примеров временно сохраняются и к ним можно вернуться, повторно выбрав прежний пункт примера, если страница браузера не была перезагружена.

О командах — подробнее


В начале пункта в кавычках будет приведено имя, которым в коде DoT12.js названо регулярное выражение, отвечающее за данную функцию (команду) шаблонизации.

Команда "valEncEval"


Один регексп под этим именем объединяет 3 прежних команды, имеющие сходный синтаксис.

{{ часть выражения и операторов JS }}
Универсальное наследие Underscore. Больше ничего не надо, оно самодостаточно, чтобы описать всё (пробелы у скобок не обязательны). Но читать… Читать больше 40 строк строго не рекомендуется. Незакрытые скобки JS перемежаются с закрытыми такими же фигурными скобками шаблонизатора. За ними идут незакрытые теги HTML. Это — нормальная практика, работает отлично, машина понимает. В то же время, Mustache/Handlebars уже читабельны.

{{= выражение JS }}
Значение выражения выкладывается в окружающий поток HTML, с сохранением тегов HTML и невзирая на незакрытые теговые скобки. Так, встретившийся "
"
в значении выражения, если будет выложен в страницу браузера, будет вести себя не как текст, а как тег — приведёт к переносу строки по правилам HTML.

{{! выражение JS }}
То же, но возвращающее текст в выдачу с «обезопашиванием» кода HTML: html-теги и html-кодированные (&...;) символы превращаются в текст;

Команда "conditional"


{{? if-выражение }} then-шаблон {{?}}
Условное включение шаблона (пробелы у скобок не обязательны);

{{? if-выражение }} then-шаблон {{?? [if-else-выражение]}} [if-]else-шаблон {{?}}
ветвление и условные цепочки текстов шаблонов.

Выражение — любое, содержащее глобальные переменные или структуру с именем 'it', в которую передаётся параметр при шаблонизации.

if-else легко получить, инвертируя if-выражение. Синтаксис не стали усложнять, видимо, ради скорости.

Команда "use"


{{# выражение, выдающее строку }}
Аналог препроцессора (макрокоманд) — в строку можно вставить вначале любой фрагмент текста, включая непарные скобки шаблонов. Компилироваться будет результат. Например, через {{# ...}} можно внести в шаблон текст другого шаблона через переменную. Но задумана команда была для более простых и конкретных дел, в паре с командой «define».

Команда "define"


Появилась с версии 1.0. Поначалу решалось не в тексте шаблонов, а в списке параметров после настроек (3-й параметр в doT.template(шаблон, настройки, параметры)).

Формат определения переменной для «use»-команд в команде «define»:
{{## def.defin1 :что_угодно_до_скобки#}}
или {{## def.defin1 =что_угодно_до_скобки#}} - имеет другой смысл (функция)

Переменные могут быть с точками и $, в них определяется любая строка. Есть ряд хитростей.

Точки в имени (стиль кого-то копировали).

Если первые 4 символа — 'def.', они удаляются.

Если через двоеточие — записываются пары def[code] = {arg: param, text: v};

Через равенство определяется функция 'def' (удобно посмотреть на тестовой странице в списке примеров).

Можно в одном месте определить макрос, чтобы 2 и более раз использовать. Как все макросы — сомнительно с точки зрения качества кода. Если понадобятся макросы — это значит — понадобилось перед кем-то выгородиться, уменьшив размер текстов грязным способом, потому что вовремя не продумано, как правильно с точки зрения проекта его уменьшить. И на хранение переменных тратятся ресурсы скрипта.

Некоторый плюс есть в том, что разделяются окружения и места работы скрипта, как с парой команд use-useParams.

И есть семантическая разница в том, что скрипты выполняются на лету, в момент связывания шаблона с данными, а define и use — на шаг раньше, при связывании функции шаблона с шаблоном. Одним словом — это макросы.

Команда "defineParams"



{{##foo="bar"#}}
Так параметры определяются, накапливаясь во внутренней структруре, а затем используются много раз после.

Команда "useParams"

Использование ранее определённых параметров.

{{#def.foo}}
Ничто не мешает взять, и определить параметры в JS:

{{ операторы JS }}
, кроме нежелания смешивать окружения шаблона и остального JS. Тут будет теряться подсветка синтаксиса и анализ кода в IDE и редакторах. Поэтому больше доводов за то, чтобы не писать JS в текстах шаблонов.

Команда "iterate"


Объединение 2 команд с разными скоростями и возможностями.
While-шаблон {{~...}} — это пробежка по массиву циклом while. (Выбирали, очевидно, из всего и выбрали самое быстрое на тот момент в браузерах.) Работает быстрее на 10-15%, чем её альтернатива {{@...}} на for-in-шаблоне, которая может пробегать по массиву или по струкутуре. 4-й параметр — фильтрация элементов по выражению. В оригинальной версии поддерживается только массив и без фильтрации. Не устраивает — всегда есть "{{ ... }}" (писать удобно, читать — нет, как Perl или машинный код).

{{~ it : value : index : filter-expression }} while-шаблон {{~}}

, где it — слово 'it' (или другое), означающее первый аргумент, или глобальное имя массива, или выражение, возвращающее массив; value — любое имя, например, 'v' или 'value' без кавычек, которое будет использоваться в for-шаблоне на месте подставляемого значения элемента массива, например, в выражении {{= value+1}}; index — аналогично, любое имя, определяющее индекс элемента массива.

Да, параметры указаны «навыворот» (сначала value, потом index), но так сложилось у них, менять не будем и здесь, и в следующей похожей команде. Логика в том, что последний (и вообще последние) index можно опускать, если не нужен в шаблоне, вместе с двоеточием.

Опускать можно в клоне и другие параметры, оставляя двоеточия. В оригинале — нельзя: хотя бы букву, но писать надо. Первый параметр по умолчанию — 'it' (а точнее — templateSettings.varname), у остальных тоже есть умолчания, но они очень технические, незапоминаемые.

В сумме, всё сделано так, чтобы и компиляция, и исполнение шаблонов были максимально быстрыми, без лишних действий и красивостей. Скорее всего, можно ещё что-нибудь улучшить, а некоторые улучшения исполнения ухудшат время компиляции, поэтому во всём — разумный баланс.

{{@ it : value : index : expression }} for-in-шаблон {{@}}

Пробежка по структуре, по первому её уровню. Выполняется в браузере медленнее (% на 10-15), чем в массиве, нужно учитывать особенности порядка выдачи ключей-чисел и остальных ключей (числа идут вначале, потом все остальные ключи, кроме старых версий IE типа 8-й и ниже, Оперы и старых Fx того же прошлого времени. Где числа шли в общем ряду с другими.). Причём, порядок ни один стандарт не гарантирует, но он есть. Использовать его или проверять, или полагаться на массивы — дело разработчика. Самые авторитетные скажут, что доверять на 100% нельзя и будут правы. Такая же история и в Python, и в JSON.

В то же время, использования порядка, в котором пришёл JSON или была определена структура, сокращает объём кода процедур. 4-м параметром добавлено выражение — условие фильтра. Если false — элемент не выводится. (Если нет условия, сохраняется паразитное условие "if(1)"? это — плата за баланс.)

Примеры сокращений параметров



{{@::i}} for-in-шаблон {{@}}
— это просто проход по всем элементам структуры.

Пример:
{{@::i}} {{=it[i]}} {{@}}
— всё равно, что
{{@:v}} {{=v}} {{@}}


Вместо шаблонов — можно использовать массивы в "@"-командах. Но наоборот, в "~"-командах (в массивах) — структуры — нельзя, ради совместимости с оригиналом.

… Через лет 7 всё это пропадёт и умрёт под грузом новых великолепных фреймворков, где вопросы по-своему и по-другому будут решены. forEach, filter, reduce, JSX — убийцы шаблонов уже на пороге и одной ногой каждый по эту сторону двери. А управлять разметками экрана сможет даже шимпанзе. А пока — спешите поковыряться в костылях, пока они окончательно не стали историй, как К155ЛА3.

Что там за настройки и зачем нужны



varname:	'it',

Эта настройка — довольно понятная. В выражениях шаблонов применяется имя вместо arguments[0] (первого параметра промежуточной скомпилированной функции). arguments[0] далеко не всегда применишь из-за вложенных функций, а имя — почти всегда, если нет конфликтов. Вот если есть конфликты — имя можно сменить, причём, не только прямо в коде библиотечного шаблонизатора, что моветон, но и во 2-м параметре doT.template(), в локальных настройках текущей команды. (Есть ещё способ «статической» смены настроек, добравшись к ним по window.doT.templateSettings.)

Есть и другие специфические имена у этого шаблонизатора, которые могут вызвать конфликты. Их легко увидеть на странице тестов, нажав кнопку «Show function» на странице тестирования test/index.html. Это имена: out, arr1, arr2, ..., ll (две малые L), v, i (при проходе по массивам. Они экранируют такие же внешние имена. Но it — на особом счету, без неё — никуда, поэтому вынесена в настройки.

strip: true,

Выкидывание лишних пробелов, табов и переносов строк. Если форматирование выходного текста не важно, используем true. Функция и шаблон будут короче, а скорость компиляции, как ни покажется странным, чуть ниже (1-2%); исполнение — по скорости неотличимо. Т.е. для ускорения компиляции нужно ставить strip: false.

append: true,

Стиль добавления кусочков HTML-кода и данных в переменную out — суммированием в цепочке или операторами присваивания. Последнее удлиняет текст функции, поэтому, если особо не нужно — выбираем true. (Видимо, когда-то это было вопросом — что выбрать и что быстрее работает.)

log: true,

Не используется. Или забыли удалить, или нужно где-то в соседних скриптах типа NodeJS — express.

selfcontained: false,

Здесь скрывается небольшая история оптимизации. Функция doT.template(...) может быть подготовлена в одном общем окружении (_globals) и тут же исполнена как doT.template(...)(...), а может — отдельно (прийти по ajax или из файла). В последнем случае нужно true (удлиняет функцию doT.template(...)), а обычно, в первом случае — false. Тогда не приходится в ней генерировать лишнего, а подсчитанное сохраняется в _globals._encodeHTML, генерируемой из _globals.doT.encodeHTMLSource(), но не всегда, а лишь при наличии команд {{! выражение}} в шаблонах.

Другими словами, selfcontained = true — значит, что функцию шаблона doT.template() будут использовать отдельно от doT.js, поэтому она должна содержать в себе всё для выполнения шаблонизации. Всё — это значит лишь особый случай кодирования HTML-символов командами {{!}}. Если они есть, в функцию нужно включить определение функции кодирования — строку doT.encHtmlStr при её создании (так сделано в клоне 1.2.1, а в оригинале функция encodeHTMLSource преобразуется в строку).

В версии 1.1.1 оригинала есть недоработка — алгоритм всегда «засовывает» код функции в шаблон, без сжатия, даже если selfcontained = false, это пришлось исправить. Ещё эта функция занимается связыванием параметра doNotSkipEncoded постоянно, хотя это нужно только при создании функции шаблона.

Затем, в оригинальном движке есть проблема конфликта версий, потому что они используют глобальный объект (window, globals) для оптимизации использования функции кодирования HTML. Её решили в клоне 1.2.1 тем, что глобальное имя функции кодирования выбрали зависящим от имени движка и версии. Получилось примерно так:

var encHt = '_'+dS.globalName + doT.version.replace(/\./g,'').
...
encHtmlStr:'var encodeHTML=typeof '+ encHt +'!="undefined"?'+ encHt +':function(c){return((c||"")+"").replace('
  + (dS.doNotSkipEncoded ?'/[&<>"'\\/]/g':'/&(?!#?\\w+;)|[<>"'/]/g')
  +',function(s){return{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}[s]||s})};'

Получаем строку для вставки в функцию шаблона, но если selfcontained = false и есть {{! выражение}}, то ограничиваемся выполнением её в глобальном объекте, чтобы из него использовать encodeHTML().

doNotSkipEncoded: false,

Аргумент doT.encodeHTMLSource(). Работает для функций {{! выражение}} — выдачи безопасного (без исполняемых тегов) HTML-кода. Если они есть в любом шаблоне окружения, первый раз определяется функция _globals._encodeHTML генерации безопасных символов для экономии повторных её вызовов. Сделано для решения таких багов: github.com/olado/doT/issues/106. Если true, то не кодируются все коды вида "&....;", и главный результат — некодирование амперсенда в '&' в таких выражениях.

Заключение



Для скорости компиляции, очевидно нужны такие параметры, которые требуют меньше действий: по возможности, selfcontained = true. Остальные части doT оптимизированы хорошо, выбраны в среднем самые быстрые решения. Скорость версий зависит от конкретного вида шаблонов, поэтому утверждение о скорости может быть лишь усреднённым по кругу задач.

В целом, клон компилирует шаблон в функцию несколько медленнее из-за увеличенного объёма анализа кода, который нужно делать для решения некоторых багов. Например, удлинена функция unescape(). Если из неё убрать 2 последних replace, скорость увеличится на 3% (Chrome v.61 Canary), но будут некоторые баги.

Если же не обращать внимание на единицы процентов, то шаблонизатор doT.js — один из наиболее быстрых и, в то же время, компактный. ES6 и даже новые методы массивов не использует — написан не в ту эпоху. Это даёт плюс в том, что поддерживается всеми браузерами (должен работать и в IE8). В ie11 протестирован. На тестовой странице test.index.html performance.now() полифиллится.

Гитхаб DoT12.js (клон оригинала), DoT.js.
JSFiddle для экспериментов с шаблонизатором (код 2013 года) и шаблонами (изначально внесён клон Dot; в других соседних номерах фиддлов читатели могли оставить результаты своих экспериментов, которые они могут документировать и оставить ссылку в комментарии; проверить работу своей версии без сохранения — нажать кнопку «Run»).
Статья по клону DoT.js 2013 года с тестами производительности.
• Сравнение версий, тесты и накопительные бенчмарки для DoT — DoT12: https://jsfiddle.net/spmbt/v3yvpbsu/22/embedded/#Result• Сравнение версий, тесты и накопительные бенчмарки для DoT — DoT12: https://jsfiddle.net/spmbt/v3yvpbsu/22/embedded/#Result или с фреймами и редактированием кода: https://jsfiddle.net/spmbt/v3yvpbsu/22/

* Closure Compiler — сжимает немного лучше в Advanced mode, чем Uglify;
* Using doT.js (хорошая подборка примеров, 2012)
* Using doT.js Способы записать цепочки if-else;
* Документация с песочницами, от авторов, подробно, с поясняющими примерами по ссылкам.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333662/


Метки:  

Стоимость хранения данных: «игра с нулевой суммой»

Вторник, 18 Июля 2017 г. 20:38 + в цитатник
На прошлой неделе наши коллеги из Backblaze поделились результатами собственного расследования проблемы стабильного снижения стоимости хранения данных. Мы решили оттолкнуться от их заметки и обсудить ситуацию здесь, на Хабре.

/ Flickr / William Hook / CC

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

Собственный данные компании Backblaze, которая приобрела более 75 тысяч дисков, говорят об интересном феномене, который заметен на объеме в 4 ТБ. Дело в том, что увеличение емкости диска перестало означать снижение стоимости в пересчете на гигабайт. В приведенном сравнении для моделей на 2 и 8 ТБ стоимость хранения одного гигабайта оказалась практически одинаковой (разница в 0,005 доллара).

Кривая, отражающая снижение стоимости одного гигабайта для различных моделей жестких дисков постепенно стабилизируется. Если с 2009 по 2011 мы могли наблюдать падение на 45% (с 0,11 до 0,05 доллара), то в рамках 2015-2017 наблюдалось снижение стоимости всего на цент (до 0,028 доллара). Для обывателя такой разброс не имеет какого-либо значения, но для IaaS-провайдера один цент может «вылиться» в несколько сотен тысяч долларов экономии, что уже ощутимо.

Такие эксперты как Мэтью Коморовски (Matthew Komorowski) отмечают не только замедление темпов снижения стоимости хранения гигабайта данных, но и другие критерии, влияющие в том числе и на ценовую политику производителей. Это — скорость, надежность и доступность. Именно они сейчас выходят на первый план. Причина — рост популярности стриминговых сервисов и облачных хранилищ данных.

Здесь стоит отметить и такие сферы как IoT и Big Data. Регулированием первой уже начали заниматься на уровне специализированной Ассоциации участников рынка интернета вещей, ко второй — только планируют найти подход.

Все это — дело уже самого ближайшего времени, значимость данных отраслей и специфика не оставляют сомнений в постепенном изменении критериев, к которым «привык» рынок хранения данных

Это — не новость для аналитиков, которые еще в 2011 году включили «умные» сенсоры и медгаджеты в число вспомогательных драйверов, которые «потянут» за собой индустрию хранения данных. (Понятное дело, что на почту и видео отвели 90% от прогнозируемого объема данных, которые нужно будет хранить на момент 2020 года.)

Помимо смены вектора ценностей (от борьбы за доступность к вере в надежность и безопасность) мы будем наблюдать постепенное внедрение новых технологий хранения данных. Конечно, они будут проходит похожие стадии развития, но каждая в своем темпе.

Решения вроде гелиевых жестких дисков и SMR-технологий могут послужить хорошими «мостиками» на пути к системам хранения на базе ДНК. Вопрос в том, как этот процесс будет развиваться с точки зрения ценообразования на «переходные» решения.

P.S. Близкие темы, которые мы рассматриваем в нашем блоге:


P.P.S. И небольшой опрос по следам нашей краткой новостной заметки:
Стоимость хранения одного гигабайта: можем ли мы ожидать «заметное» снижение?

Проголосовал 1 человек. Воздержавшихся нет.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333658/


Метки:  

[Перевод] Work-stealing планировщик в Go

Вторник, 18 Июля 2017 г. 19:57 + в цитатник

Задача планировщика в Go — распределять запущенные горутины между потоками ОС, которые могут исполняться одним или большим количеством процессоров. В многопоточных вычислениях, возникли две парадигмы в планировании: делиться задачами (work sharing) и красть задачи (work stealing).


  • Work-sharing: Когда процессор генерирует новый поток, он пытается мигрировать их на другие процессоры, в надежде, что они попадут к простаивающему или недостаточно нагруженному процессору.
  • Work-stealing: Недостаточно нагруженный процессор активно ищет потоки других процессоров и "крадет" некоторые из них.

Миграция потоков происходит реже при work stealing подходе, чем при work sharing. Когда все процессоры заняты, потоки не мигрируют. Как только появляется простаивающий процессор, рассматривается вариант миграции.


В Go начиная с версии 1.1 планировщик реализован по схеме work stealing и был написан Дмитрием Вьюковым. Эта статья подробно объясняет устройство work stealing планировщиков и как он устроен в Go.


Основы планирования


Планировщик Go выполнен по M:N схеме и может использовать несколько процессоров. В любой момент M горутин должны быть распределены между N потоками ОС, которые бегут на максимум GOMAXPROCS процессорах. В Go планировщике используется следующая терминология для горутин, потоков и процессоров:


  • G: горутина
  • M: поток ОС (M от Machine)
  • P: процессор

Далее у нас есть две очереди, специфичные для P. Каждый M должен быть назначен к своему P. P-ы могут не иметь M, если они заблокированы или ожидают окончания системного вызова. В любой момент может быть максимум GOMAXPROCS процессоров — P. В любой момент только один M может исполняться на каждый P. Больше M может создаваться планировщиком, если это требуется.



Каждый цикл планирования заключается в поиске горутины, которая готова к тому, чтобы быть запущенной и её исполнения. При каждом цикле поиск присходит в следующем порядке:


runtime.schedule() {
    // только 1/61 от всего времени, проверить глобальную очередь G
    // если не найдено, проверить локальную очередь
    // если не найдено, то:
    //   попытаться украсть у других P
    //   если не вышло, проверить глобальную очередь
    //   если всё равно не вышло,  поллить (poll) сеть
}

Как только готовая к исполнению G найдена, она исполняется, пока не будет заблокирована.


Заметка: Может показаться, что глобальная очередь имеет преимущество перед локальной, но регулярная проверка глобальной очереди критична для избежания M использования только горутин из локальной очереди.


"Кража" (Stealing)


Когда новая G создается или существующая G становится готовой к исполнению, она помещается в локальную очередь готовых к исполнению горутин текущего P. Когда P заканчивается исполнение G, он пытается вытащить (pop) G из своей очереди. Если список пуст, P выбирает случайным образом другой процессор (P) и пытается украсть половину горутин из его очереди.



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


Spinning потоки


Планировщик всегда хочет распределить как можно больше готовых к исполнению горутин на много M, чтобы использовать все процессоры, но, в тоже время, мы должны уметь приостанавливать (park) сильно прожорливые процессы, чтобы сохранять ресурсы CPU и энергию. И при этом, планировщик должен также уметь масштабироваться для задач, которые действительно требуют много вычислительной мощности процессора и большую производительность.


Постоянное упреждение (preemption) одновременно и дорогое и проблематичное для высоко-производительных программ, где производительность критичней всего. Горутины не должны постоянно прыгать между потоками ОС, поэтому это приводит к повышенной задержки (latency). В добавок ко всему, когда вызываются системные вызовы, поток должен быть постоянно блокироваться и разблокироваться. Это дорого и приводит к большим накладным расходам.


Чтобы уменьшить эти прыжки горутин туда-сюда, планировщик Go реализует так называемые зацикленные потоки (spinning threads). Эти поток используют чуть больше процессорной мощности, но уменьшают упреждение потоков. Поток считается зациклен, если:


  • M назначенный на P ищет горутину, которую бы можно было запустить
  • M не назначенный на P, ищет доступные P.
  • планировщик также запускает дополнительный поток и зацикливает его, когда готовит новую горутину и есть простаивающий P и нет других зацикленных потоков

В любой момент времени может быть максимум GOMAXPROCS зацикленных M. Когда зацикленный поток находит работу, он выходит из зацикленного состояния.


Простаивающие поток, назначенные на какой-либо P не блокируются, если есть другие M, не назначенные на P. Если создается новая горутина или блокируется M, планировщик проверяет и гарантирует, что есть хотя бы один зацикленный M. Это гарантирует, что все горутины могут быть запущены, если есть возможность и позволяет избежать излишних блокировок/разблокировок M.


Выводы


Планировщик Go делает много всего для избежания избыточного упреждения потоков, распределяя их по недоиспользованным процессорам методом "кражи", и также реализацией "зацикленных" потоков, чтобы избежать частых переходов из блокирующего в неблокирующее состояние и обратно.


События планирования можно отслеживать с помощью execution tracer-а. Вы можете детально докопаться до всего, что происходит внутри планировщика, особенно если считаете, что в вашем случае происходит не эффективное использование процессоров.


Ссылки



Если у вас есть предложения или комментарии, пишите @rakyll.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333654/


Метки:  

[Из песочницы] Рынок 5D. Проекционные системы

Вторник, 18 Июля 2017 г. 19:32 + в цитатник
На рынке 5D платформ мы уже более 5 лет. За это время у меня накопился солидный багаж знаний, которыми решил поделиться. В первой части я хочу рассказать о проекционных системах, применяемых в этой отрасли, а так же об адаптации нашего ПО под них. Какие решения мы применяли и почему. Я сознательно не зазываю товарный знак, чтобы не сочли, что пост – это просто реклама очередной программы.
Итак. 5D – это прежде всего кинотеатр со стерео контентом. Ведь звуковые или тактильные ощущения для большинства людей не так важны, как видеоряд.

На рынке сейчас используются следующие технологии:


  1. 2-х проекторная система с линейной или круговой поляризацией. Главный минус – частое прогорание поляризационных фильтров.

  2. Стерео “с самодельным” эмиттером, где для синхронизации используется 3-din разъём профессиональных видеокарт nvidia. Главный минус – видеокарту с этим разъёмом сейчас практически не достать.

  3. Nvidia 3D vision, где стандартный эмиттер “взломан” и сигнал синхронизации передаётся на другой, ведь стандартный очень слабый и не стабильный на длинном проводе. Есть производители, которые могут ставить только 301 драйвер, так как дальше NVIDIA улучшила защиту. Но, например, мы решили этот вопрос принципиально по-другому, поэтому нам не страшны эти обновления защиты.

  4. RedPoint синхронизация на основе переходника на VGA кабеле. Где в каждом не чётном кадре вверху ставится маркер в виде красной точки, что бы переходник распознал, где кадр чётный, где нет. Основной минус – это VGA со всем вытекающим качеством картинки.

  5. Различные мультипроекторные решения на основе п.1, п.3 или моно.

И экраны тоже разные:


  1. Обычный прямоугольный экран различных пропорций (как правило, по максимуму для зала, реже придерживаясь 16:9 или 4:3).

  2. Прямоугольный основной экран и по бокам 2 не больших экранчика, тоже плоские.

  3. Цилиндрический экран с примерно равным расстоянием от любой точки по одной горизонтали до проектора(-ов).

  4. Различные экраны сложных форм: сферы, неровные стены музеев и т.д.

И стала задача, чтобы рендер работал на всех системах, ОС от win XP до 10 и т.д. Причём, чаще всего, это именно старое железо и windows XP. Написать сам рендер была не проблема, я до этого разрабатывал много крутых штук для ProgDVB, в том числе и его, но тут стала проблема сведения многопроекторных систем. Ведь практически невозможно повесить 2 разных проектора, заставив их светить в одну точку. Для этого раньше приходилось использовались специальные дорогие юстировочные платформы, которые надо было накручивать в течении долгого времени где-то под потолком в неудобной позе, и, так как это механика, то от любого мало-мальски сильного хлопка двери проекторы могли снова “разъехаться”.

Да и с однопроекторными системами тоже не всё так гладко. Сами проекторы хоть и умеют геометрию подстраивать, делают это слишком ступенчато.

Поэтому была взята простая сетка с настройки ТВ канала:




Которую мышкой можно исказить примерно таким вот образом:



Чтобы экране оба проектора начинали светить соответствующими пикселами в одну точку.
Но вручную свести 2 проектора на плоском экране не сложно. Наше же ПО работает и на более сложных системах. Взять, например, 6-ти проекционную систему с цилиндрическим экраном. И так как экран цилиндрический – для каждой из 6 частей “сетки” нужно не просто линейное искажение, а гораздо более сложный алгоритм, который вручную сделать крайне тяжело и долго.

image
Шестрипроекторная система

Поэтому мною был разработан оптический модуль для автоматической настройки, естественно подстраиваемый под разные экраны и освещение:



Для создания эффекта “не разрывности” “соседних” проекторов используется градиентный переход, затухающий с коэффициентом натурального логарифма (естественно, через простейший обсчёт на пиксельном шейдере линейно заданного цвета в данной точке). Т.е. одна точка имеет цвет (1,1,1), вторая (0,0,0). В результате фрагмент кода шейдера

float cc=log(color)*kj;
float4 c2=rgb*exp(cc);
return c2;

Где kj – подбираемый каждый раз параметр, для каждой конкретной проекционной системы и экрана, который зависит, прежде всего, от того, на сколько чёрный цвет у проектора реально чёрный.

Внизу различные настройки и реальная картинка с камеры, вверху программа сама распознаёт экран и вписывает в него как можно точнее настроечную картинку.



И затем остаётся лишь запустить пересчёт. Таким образом сопоставить, опять же, с помощью камеры положение на экране внутри этой настроечной сетки и то, что выводит проектор. То есть подсвечивать отдельные пиксели на проекторе и смотреть где они будут на камере. Но подсвечивать каждый n пиксель – это долго. Для того, что бы пересчёт не затягивать, я вывожу сначала вертикальные линии, затем горизонтальные с определённым шагом. И не забываем, что камера в условиях плохого освещения – штука очень инертная. Поэтому надо ещё правильно подобрать задержку между выводом линии, и ее сканированием.

Немного технических деталей (Delphi). Самая важная функция – это вычисление методом “лесного пожара” области экрана на камере. Пользователь тыкает мышкой или (обычно) пальцем в тачскрин, этим самым задавая отправную точку. Тут важно правильно подобрать освещение для лучшего контраста экран-не-экран.

Подготовка данных
procedure TCam_Geometry_frm.CalcPixelRegion(x,y:integer);
var
   StartP:TPoint;
  I: Integer;
  J: Integer;
  StaPo,EnPo:integer;
begin
   StartP.X := x * InternalBitmap.Width  div Image1.Width;
   StartP.Y := y * InternalBitmap.Height  div Image1.Height;
   SetLength(CheckingMask,InternalBitmap.Height);
   for I := 0 to InternalBitmap.Height - 1 do
   begin
      SetLength(CheckingMask[i],InternalBitmap.Width);
      for J := 0 to InternalBitmap.Width-1 do
      begin
         CheckingMask[i][j].IsCheckPoint := false;
         CheckingMask[i][j].IsPointChecked := false;
         CheckingMask[i][j].typ := 0;
         CheckingMask[i][j].texX := -1;
         CheckingMask[i][j].texY := -1;
      end;
   end;
   SetLength(TempFireBuf,InternalBitmap.Width * InternalBitmap.Height * 4);
   StaPo := 0;
   EnPo := 1;
   TempFireBuf[0].XPos := StartP.X;
   TempFireBuf[0].YPos := StartP.Y;
   CheckingMask[StartP.Y][StartP.X].IsPointChecked := true;
   CheckingMask[StartP.Y][StartP.X].IsCheckPoint := true;
   while StaPo <> EnPo do
   begin
      if (abs(InternalPic.GetRED(TempFireBuf[StaPo].XPos, TempFireBuf[StaPo].YPos)-
      InternalPic.GetRED(TempFireBuf[TempFireBuf[StaPo].pripos].XPos, TempFireBuf[TempFireBuf[StaPo].pripos].YPos)) 0 then
        begin
           if not CheckingMask[TempFireBuf[StaPo].YPos][TempFireBuf[StaPo].XPos-1].IsPointChecked then
           begin
              TempFireBuf[EnPo].XPos := TempFireBuf[StaPo].XPos-1;
              TempFireBuf[EnPo].YPos := TempFireBuf[StaPo].YPos;
              TempFireBuf[EnPo].pripos := StaPo;
              CheckingMask[TempFireBuf[EnPo].YPos][TempFireBuf[EnPo].XPos].IsPointChecked := true;
              inc(EnPo);
           end;
        end;
        if TempFireBuf[StaPo].XPos < InternalBitmap.Width - 1 then
        begin
           if not CheckingMask[TempFireBuf[StaPo].YPos][TempFireBuf[StaPo].XPos+1].IsPointChecked then
           begin
              TempFireBuf[EnPo].XPos := TempFireBuf[StaPo].XPos+1;
              TempFireBuf[EnPo].YPos := TempFireBuf[StaPo].YPos;
              TempFireBuf[EnPo].pripos := StaPo;
              CheckingMask[TempFireBuf[EnPo].YPos][TempFireBuf[EnPo].XPos].IsPointChecked := true;
              inc(EnPo);
           end;
        end;
        if TempFireBuf[StaPo].YPos > 0 then
        begin
           if not CheckingMask[TempFireBuf[StaPo].YPos-1][TempFireBuf[StaPo].XPos].IsPointChecked then
           begin
              TempFireBuf[EnPo].XPos := TempFireBuf[StaPo].XPos;
              TempFireBuf[EnPo].YPos := TempFireBuf[StaPo].YPos-1;
              TempFireBuf[EnPo].pripos := StaPo;
              CheckingMask[TempFireBuf[EnPo].YPos][TempFireBuf[EnPo].XPos].IsPointChecked := true;
              inc(EnPo);
           end;
        end;
        if (TempFireBuf[StaPo].YPos < 5) or (TempFireBuf[StaPo].YPos < 5) then
        begin
           ShowMessage('Область выделения подошла опасно к краю. Пордолжение не возможно.');
           exit;
        end;

        if TempFireBuf[StaPo].YPos < InternalBitmap.Height - 1 then
        begin
           if not CheckingMask[TempFireBuf[StaPo].YPos+1][TempFireBuf[StaPo].XPos].IsPointChecked then
           begin
              TempFireBuf[EnPo].XPos := TempFireBuf[StaPo].XPos;
              TempFireBuf[EnPo].YPos := TempFireBuf[StaPo].YPos+1;
              TempFireBuf[EnPo].pripos := StaPo;
              CheckingMask[TempFireBuf[EnPo].YPos][TempFireBuf[EnPo].XPos].IsPointChecked := true;
              inc(EnPo);
           end;
        end;
      end;
      inc(StaPo);
   end;
   SetLength(TempFireBuf,0);
end;

Затем просто этот набор пикселей превращаем в регион, в котором далее и будем искать уже линии.
procedure TCam_Geometry_frm.CreateFrame;
var
   nn:array [1..10] of integer;
   i,j,k,l,tmp:integer;
   rasts:array [1..4]of extended;
   rad:extended;
begin
   for I := 1 to 10 do
      nn[i] := GetMinY(i);
   for k := 0 to 5 do
   for I := 11 to InternalPic.PicX - 1 do
   begin
      if (nn[1] > 0) and (nn[5] > 0) and (nn[10] > 0) and (abs(nn[10]-nn[1])< 7) then
      begin
          tmp := 0;
          for l := 1 to 10 do
             tmp := tmp + nn[l];
          tmp := tmp div 10;
          while nn[5] < tmp do begin
              CheckingMask[nn[5]][i-6].IsCheckPoint := false;
              inc(nn[5]);
          end;
          while nn[5] > tmp do begin
              CheckingMask[nn[5]][i-6].IsCheckPoint := true;
              dec(nn[5]);
          end;
      end;
      for j := 2 to 10 do
         nn[j-1] := nn[j];
      nn[10] := GetMinY(i);
   end;
   for I := 1 to 10 do
      nn[i] := GetMaxY(i);
   for k := 0 to 5 do
   for I := 11 to InternalPic.PicX - 1 do
   begin
      if (nn[1] > 0) and (nn[5] > 0) and (nn[10] > 0) and (abs(nn[10]-nn[1])< 7) then
      begin
          tmp := 0;
          for l := 1 to 10 do
             tmp := tmp + nn[l];
          tmp := tmp div 10;
          while nn[5] <= tmp do begin
              CheckingMask[nn[5]][i-6].IsCheckPoint := false;
              inc(nn[5]);
          end;
          while nn[5] > tmp do begin
              CheckingMask[nn[5]][i-6].IsCheckPoint := true;
              dec(nn[5]);
          end;
      end;
      for j := 2 to 10 do
         nn[j-1] := nn[j];
      nn[10] := GetMaxY(i);
   end;
   rasts[1] := 0;rasts[2] := 0;rasts[3] := 0;rasts[4] := 0;
   Center.X := 0;Center.Y := 0;
   k := 0;
   for I := 11 to InternalPic.PicY - 1 do
      for J := 11 to InternalPic.PicX - 1 do
         if CheckingMask[i][j].IsCheckPoint then
         begin
            Center.X := Center.X + J;
            Center.Y := Center.Y + I;
            inc(k);
         end;
   Center.X := Center.X div k;
   Center.Y := Center.Y div k;
   for I := 11 to InternalPic.PicY - 1 do
      for J := 11 to InternalPic.PicX - 1 do
      begin
         if CheckingMask[i][j].IsCheckPoint then
         begin
            rad := (J-Center.X)*(J-Center.X)+(I-Center.Y)*(I-Center.Y);
            if i < Center.Y then
            begin
               if j < Center.X then
               begin
                  if (rasts[1] < rad) then
                  begin
                     rasts[1] := rad;
                     X1Y1.X := J;
                     X1Y1.Y := I;
                  end;
               end
               else
               begin
                  if (rasts[2] < rad) then
                  begin
                     rasts[2] := rad;
                     X2Y1.X := J;
                     X2Y1.Y := I;
                  end;
               end;
            end
            else
            begin
               if j < Center.X then
               begin
                  if (rasts[3] < rad) then
                  begin
                     rasts[3] := rad;
                     X1Y2.X := J;
                     X1Y2.Y := I;
                  end;
               end
               else
               begin
                  if (rasts[4] < rad) then
                  begin
                     rasts[4] := rad;
                     X2Y2.X := J;
                     X2Y2.Y := I;
                  end;
               end;
            end;
         end;
      end;

   LeftSetkaSide.IsHorisontOnScreen := false;
   LeftSetkaSide.CoordVal := 0;
   LeftSetkaSide.IsHorisontVals := false;
   LeftSetkaSide.x[1] := X1Y1.X;
   LeftSetkaSide.y[1] := X1Y1.Y;
   LeftSetkaSide.x[2] := X1Y2.X;
   LeftSetkaSide.y[2] := X1Y2.Y;

   LeftSetkaSide.y[3] := (LeftSetkaSide.y[1]+LeftSetkaSide.y[2]) / 2;
   LeftSetkaSide.x[3] := GetMinX(Round(LeftSetkaSide.y[3]));

   LeftSetkaSide.y[4] := (LeftSetkaSide.y[1] + LeftSetkaSide.y[3]) / 2;
   LeftSetkaSide.x[4] := GetMinX(Round(LeftSetkaSide.y[4]));
   LeftSetkaSide.y[5] := (LeftSetkaSide.y[2] + LeftSetkaSide.y[3]) / 2;
   LeftSetkaSide.x[5] := GetMinX(Round(LeftSetkaSide.y[5]));

   RightSetkaSide.IsHorisontOnScreen := false;
   RightSetkaSide.CoordVal := 0;
   RightSetkaSide.IsHorisontVals := false;
   RightSetkaSide.x[1] := X2Y1.X;
   RightSetkaSide.y[1] := X2Y1.Y;
   RightSetkaSide.x[2] := X2Y2.X;
   RightSetkaSide.y[2] := X2Y2.Y;

   RightSetkaSide.y[3] := (RightSetkaSide.y[1]+RightSetkaSide.y[2]) / 2;
   RightSetkaSide.x[3] := GetMaxX(Round(RightSetkaSide.y[3]));

   RightSetkaSide.y[4] := (RightSetkaSide.y[1] + RightSetkaSide.y[3]) / 2;
   RightSetkaSide.x[4] := GetMaxX(Round(RightSetkaSide.y[4]));
   RightSetkaSide.y[5] := (RightSetkaSide.y[2] + RightSetkaSide.y[3]) / 2;
   RightSetkaSide.x[5] := GetMaxX(Round(RightSetkaSide.y[5]));

   UpSetkaSide.IsHorisontOnScreen := true;
   UpSetkaSide.CoordVal := 0;
   UpSetkaSide.IsHorisontVals := false;
   UpSetkaSide.x[1] := X1Y1.X;
   UpSetkaSide.y[1] := X1Y1.Y;
   UpSetkaSide.x[2] := X2Y1.X;
   UpSetkaSide.y[2] := X2Y1.Y;

   UpSetkaSide.x[3] := (UpSetkaSide.x[1]+UpSetkaSide.x[2]) / 2;
   UpSetkaSide.y[3] := GetMinY(Round(UpSetkaSide.x[3]));

   UpSetkaSide.x[4] := (UpSetkaSide.x[1]+UpSetkaSide.x[3]) / 2;
   UpSetkaSide.y[4] := GetMinY(Round(UpSetkaSide.x[4]));
   UpSetkaSide.x[5] := (UpSetkaSide.x[2]+UpSetkaSide.x[3]) / 2;
   UpSetkaSide.y[5] := GetMinY(Round(UpSetkaSide.x[5]));

   DownSetkaSide.IsHorisontOnScreen := true;
   DownSetkaSide.CoordVal := 0;
   DownSetkaSide.IsHorisontVals := false;
   DownSetkaSide.x[1] := X1Y2.X;
   DownSetkaSide.y[1] := X1Y2.Y;
   DownSetkaSide.x[2] := X2Y2.X;
   DownSetkaSide.y[2] := X2Y2.Y;

   DownSetkaSide.x[3] := (DownSetkaSide.x[1]+DownSetkaSide.x[2]) / 2;
   DownSetkaSide.y[3] := GetMaxY(Round(DownSetkaSide.x[3]));

   DownSetkaSide.x[4] := (DownSetkaSide.x[1]+DownSetkaSide.x[3]) / 2;
   DownSetkaSide.y[4] := GetMaxY(Round(DownSetkaSide.x[4]));
   DownSetkaSide.x[5] := (DownSetkaSide.x[2]+DownSetkaSide.x[3]) / 2;
   DownSetkaSide.y[5] := GetMaxY(Round(DownSetkaSide.x[5]));
end;


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

Основной алгоритм обсчёта
procedure TCam_Geometry_frm.AddLograngeKoeffs(n:integer;byX:boolean;coord:integer);
var
  I, J: integer;
  possx,possy,ccou:integer;
  srX1,srY1:extended;
  lfid:integer;
  foundPoints:arrpo;
  Center:TPoint;
  Clct,Clct2,Clct3,last:TPoint;
  dy,sry,ddy,y:extended;
//  CheAr:array of array of boolean;
begin
   possx := 0;
   possy := 0;
   ccou := 0;
   SetLength(foundPoints,0);
   for I := 0 to Length(ProjSetka[n]) - 1 do
      for J := 0 to Length(ProjSetka[n][i]) - 1 do
      begin
         if (byX and (ProjSetka[n][i][j].ProjX = coord) and IsPossHere(n,j,i,byX,20, 20,srX1,srY1))or
            ((not byX) and (ProjSetka[n][i][j].ProjY = coord) and IsPossHere(n,j,i,byX,20, 20,srX1,srY1))then
         begin
            possx := possx + j;
            possy := possy + i;
            inc(ccou);
            SetLength(foundPoints,ccou);
            foundPoints[ccou-1].X := J;
            foundPoints[ccou-1].Y := I;
         end;
      end;
   if ccou < 10 then
   begin
      possx := -3;
      exit;
   end;

   possx := possx div ccou;
   possy := possy div ccou;
   Center.X := possx; Center.Y := possy;
   lfid := length(LograngeFuncs[n]);
   SetLength(LograngeFuncs[n],length(LograngeFuncs[n])+1);
   LograngeFuncs[n][lfid].IsHorisontOnScreen := false;
   LograngeFuncs[n][lfid].CoordVal := coord;
   LograngeFuncs[n][lfid].IsHorisontVals := byX;

   i := GetMinLengthFromArr(foundPoints,Center);
    if i < 0 then
    begin
       ShowMessage('Не нашли ни одной точки для интерполяции Лагранжа!');
       exit;
    end;
   IsPossHere(n,foundPoints[i].X,foundPoints[i].Y,byX,20, 20,srX1,srY1);
   LograngeFuncs[n][lfid].x[1] := srX1;
   LograngeFuncs[n][lfid].Y[1] := srY1;

   foundPoints[i].X := -1;

   i := GetMaxLengthFromArr(foundPoints,Center);
   IsPossHere(n,foundPoints[i].X,foundPoints[i].Y,byX,20, 20,srX1,srY1);
   LograngeFuncs[n][lfid].x[5] := srX1;
   LograngeFuncs[n][lfid].Y[5] := srY1;

   foundPoints[i].X := -1;
   Clct.X := round(srX1);
   Clct.Y := round(srY1);

   i := GetMaxLengthFromArr(foundPoints,Center);
   while abs(GetAngleFrom3Points(Center,Clct,foundPoints[i])) < Pi / 2 do
   begin
      foundPoints[i].X := -1;
      i := GetMaxLengthFromArr(foundPoints,Center);
      if i < 0 then
      begin
         ShowMessage('Не нашли точки для интерполяции Лагранжа!');
         exit;
      end;
   end;
   IsPossHere(n,foundPoints[i].X,foundPoints[i].Y,byX,20, 20,srX1,srY1);
   LograngeFuncs[n][lfid].x[4] := srX1;
   LograngeFuncs[n][lfid].Y[4] := srY1;
   Clct2.X := round(srX1);
   Clct2.Y := round(srY1);
   LograngeFuncs[n][lfid].x[2] := -1;
   LograngeFuncs[n][lfid].x[3] := -1;
   while (LograngeFuncs[n][lfid].x[2] < 0) or (LograngeFuncs[n][lfid].x[3] < 0) do
   begin
      i := GetNearestFromArr(foundPoints,Center,min(GetLengthBW2P(Center,Clct),GetLengthBW2P(Center,Clct2)) div 2);
      if LograngeFuncs[n][lfid].x[2] < 0 then
      begin
         IsPossHere(n,foundPoints[i].X,foundPoints[i].Y,byX,20, 20,srX1,srY1);
         LograngeFuncs[n][lfid].x[2] := srX1;
         LograngeFuncs[n][lfid].Y[2] := srY1;
         foundPoints[i].X := -1;
         Clct3.X := round(srX1);
         Clct3.Y := round(srY1);
      end
      else
      begin
         if i < 0 then
         begin
            LograngeFuncs[n][lfid].x[3] := last.X;
            LograngeFuncs[n][lfid].Y[3] := last.Y;
         end
         else
         if abs(GetAngleFrom3Points(Center,Clct3,foundPoints[i])) > Pi / 2 then
         begin
            IsPossHere(n,foundPoints[i].X,foundPoints[i].Y,byX,20, 20,srX1,srY1);
            LograngeFuncs[n][lfid].x[3] := srX1;
            LograngeFuncs[n][lfid].Y[3] := srY1;
         end;
      end;
      if i >= 0 then
      begin
         last := foundPoints[i];
         foundPoints[i].X := -1;
      end;
   end;
   if abs(LograngeFuncs[n][lfid].x[1]-LograngeFuncs[n][lfid].x[5]) > abs(LograngeFuncs[n][lfid].y[1]-LograngeFuncs[n][lfid].y[5]) then
   begin
      LograngeFuncs[n][lfid].IsHorisontOnScreen := true;
   end
   else
      LograngeFuncs[n][lfid].IsHorisontOnScreen := false;
   if LograngeFuncs[n][lfid].IsHorisontOnScreen then
   begin
      sry := 0;
      for I := 1 to 5 do
         sry := sry + LograngeFuncs[n][lfid].y[i];
      sry := sry / 5;
      dy := 0;
      for I := 1 to 5 do
         if dy < abs(sry - LograngeFuncs[n][lfid].y[i]) then
            dy := abs(sry - LograngeFuncs[n][lfid].y[i]);
      dy := dy * 3 + 5;
      for I := 10 to 1000 do
      begin
         y := CalcPointByPolinom(n,lfid,i,-1);
         if (y > 0) and(dy < abs(sry - y)) then
         begin
            SetLength(LograngeFuncs[n],length(LograngeFuncs[n])-1);
            exit;
         end;
      end;
   end
   else
   begin
      sry := 0;
      for I := 1 to 5 do
         sry := sry + LograngeFuncs[n][lfid].x[i];
      sry := sry / 5;
      dy := 0;
      for I := 1 to 5 do
         if dy < abs(sry - LograngeFuncs[n][lfid].x[i]) then
            dy := abs(sry - LograngeFuncs[n][lfid].x[i]);
      dy := dy * 3+5;
      for I := 10 to 1000 do
      begin
         y := CalcPointByPolinom(n,lfid,-1,i);
         if (y > 0) and(dy < abs(sry - y)) then
         begin
            SetLength(LograngeFuncs[n],length(LograngeFuncs[n])-1);
            exit;
         end;
      end;
   end;
end;

Применяется вот так:
procedure TCam_Geometry_frm.sButton3Click(Sender: TObject);
var
  I, couu: Integer;
  geom_frms:array of Tcam_geomery_lines_ouput_frm;
  j,l: Integer;
  k, pos: Integer;
begin
   if not sButton1.Enabled then begin FlagStop:=true;exit;end;
   FlagStop:=false;
   SetLength(geom_frms,g_MonitorsCount);
   SetLength(ProjSetka,g_MonitorsCount);
   SetLength(LograngeFuncs,g_MonitorsCount);
   for I := 0 to g_MonitorsCount-1 do
   begin
      geom_frms[i] := Tcam_geomery_lines_ouput_frm.Create(self);
      geom_frms[i].PosX := g_MonitorsSetup[i+1].ScreenPosition.x;
      geom_frms[i].PosY := g_MonitorsSetup[i+1].ScreenPosition.y;
      Application.ProcessMessages;
      SetLength(ProjSetka[i],length(CheckingMask));
      SetLength(LograngeFuncs[i],0);
      for J := 0 to length(CheckingMask)-1 do
      begin
         SetLength(ProjSetka[i][j],length(CheckingMask[j]));
         for k := 0 to length(CheckingMask[j]) - 1 do
         begin
            ProjSetka[i][j][k].ProjX := -1;
            ProjSetka[i][j][k].ProjY:= -1;
         end;
      end;
   end;
   sButton2.Enabled := false;
   sButton1.Enabled := false;
   sButton17.Enabled := false;
   sButton4.Enabled := false;
   sButton5.Enabled := false;

   for I := 0 to g_MonitorsCount-1 do
   begin
      geom_frms[i].Show;
      geom_frms[i].SetBlack;
   end;

     for L := 0 to 40 do
     begin
        Application.ProcessMessages;
        Sleep(20);
     end;

   GetBitmapFromCam(blackBitmap);
   InitPicBuffer(blackPic,blackBitmap.Width,blackBitmap.Height);
   CopyToPic(blackBitmap,0,0,blackPic);

   for I := 0 to g_MonitorsCount-1 do
   begin
     for L := 0 to 70 do
     begin
        Application.ProcessMessages;
        Sleep(20);
     end;

      GetBitmapFromCam(blackBitmap);
      CopyToPic(blackBitmap,0,0,blackPic);

      couu := 16;
      if FlagStop then break;
      for j := 0 to couu do
      begin
         pos := j*geom_frms[i].Width div couu;
         if pos < 4 then pos := 4;
         if pos >= geom_frms[i].Width - 4 then pos := geom_frms[i].Width - 4;

         geom_frms[i].PaintLine(pos,0,pos,geom_frms[i].Height);
         for L := 0 to 70 do
         begin
            Application.ProcessMessages;
            Sleep(20);
         end;
         if not SaveProjLineCoords(i,pos,-1) then FlagStop := true;
         AddLograngeKoeffs(i,true,pos);
         pos := j*geom_frms[i].Height div couu;
         if pos < 4 then pos := 4;
         if pos >= geom_frms[i].Height - 4 then pos := geom_frms[i].Height - 4;
         geom_frms[i].PaintLine(0,pos,geom_frms[i].Width,pos);
         for L := 0 to 70 do
         begin
            Application.ProcessMessages;
            Sleep(20);
         end;
         if not SaveProjLineCoords(i,-1,pos) then FlagStop := true;
         AddLograngeKoeffs(i,false,pos);
         if FlagStop then break;
      end;
      geom_frms[i].SetBlack;
//      geom_frms[i].hide;

      SaveProjSsetka(i);
   end;
   if not FlagStop then
      SetCaptSetkaWidthToOne;
   if not FlagStop then
      CreateProjSetka;

   for I := 0 to g_MonitorsCount-1 do
   begin
      geom_frms[i].Free;
   end;
   if not FlagStop then
      SaveGeometry;
   sButton2.Enabled := true;
   sButton1.Enabled := true;
   sButton17.Enabled := true;
   sButton4.Enabled := true;
   sButton5.Enabled := true;
end;


Всё. Каждый пиксель проектора (из тех, которые возможно) сопоставлен пикселю на экране.

Теперь можно насладиться результатом.



Изображение двоится из-за стерео картинки. В очках всё гораздо интереснее. Пересветы сведения хорошо заметны на камере, так как она сбоку. С платформы, да ещё и в очках эффект минимален.

Другая часть ролика, где эффект 3D минимален и можно оценить именно сведение.



И ещё пара важных замечаний:

Во-первых, обязательно вывод на каждый проектор – это свой поток, со своим кэшем кадров и синхронизацией с vsync. Иначе у вас будет всё или тормозить или рвать картинку. Особенно если проекторов под 12.

Во-вторых, если вы растягиваете картинку 4:3 предположим к 16:9, но картинка мультяшная, и пропорции предметов не очень понятны, больших проблем не будет. Но если вы растянете на цилиндрический экран, всё будет вообще не в пропорции, так как там соотношения 21:9, 27:9 и т.д. Но если показывать в пропорции правильной, то останется крутить 10-12 роликов, которые создавались именно под такой экран, а про остальные забыть.
Выход есть. С помощью так называемого Super zoom можно центральную часть кадра оставлять практически без искажений, а края растягивать. Периферическому зрению пропорции не так важны, а эффект погружения возрастает сильно. В этом методе, конечно, есть много своих минусов, но плюсов больше.

Ожидая вопрос про язык программирования, интерфейс написан на Delphi, весь рендер и управление платформами – на C++.

P.S.: Если тема 5D будет интересна, могу продолжить рассказ о различных протоколах различных платформ или об адаптации готовых unity роликов виртуальной реальности для этой отрасли. Или что-нибудь ещё интересное. В общем, жду комментариев/вопросов.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333652/


Метки:  

Что нового в IntelliJ IDEA 2017.2

Вторник, 18 Июля 2017 г. 18:41 + в цитатник
Привет, Хабр!

Мы рады сообщить о выходе крупного обновления IDE – IntelliJ IDEA 2017.2. Поскольку далеко не все следят за новостями в нашем официальном блоге (а также потому, что многим удобнее будет прочитать об этом на русском), в этом посте я расскажу что ждать от новой версии IDE. Кто спешит попробовать обновление самостоятельно, качайте с сайта JetBrains, либо из Toolbox App. Все остальные, добро пожаловать под кат.




Java


Автодополнение остается одной из самых востребованных функций IDE и поэтому продолжает улучшаться от релиза к релизу. В этот раз, улучшения коснулись функции Smart Completion.

Во-первых, IDE научилась подсказывать выражения использующие Builder паттерн:



Во-вторых, подсказки содержащие цепочки вызовов, ранее доступные по второму вызову Smart Completion, теперь показываются при первом вызове, а кроме того сортируются на основе их популярности проекте:



Последнее улучшение пока доступно только после компиляции проекта компилятором IDE (иначе говоря, функция пока недоступна, если компиляция делегируется Gradle).

Анализ потока выполнения кода (control flow analysis) также стал умнее и теперь находит еще большее множество потенциальных ошибок в коде, незаметных с первого взгляда.

Во-первых, IDE замечает условия, противоречащие или повторяющие друг друга. Напрямую такой код не является причиной ошибки, однако часто он свидетельствует о ней косвенно.



Кроме того, IDE теперь научилась понимать контракт многих методов, и использовать это для нахождения логических проблем в коде. Например, если метод Collection#isEmpty() возвращает true, это означает, что итерация по этой коллекции не будет выполняться, а вызов метода List#get(int index > 0) приведет к ошибке.



Новые инспекции включают: Replace with Set.of, Add Thread#onSpinWait() (обе требуют Java 9), а также Extract set from comparison chain.

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



Java 9 продолжает медленно но неумолимо двигаться вперед. Поэтому в обновлении мы добавили диаграммы модулей, которые, надеемся, сделают работу с модулями еще удобнее:



Окно Run для Spring Boot теперь отображают информацию о состоянии запущенных приложений, полученную с помощью Endpoints: Beans, Health и Mappings.



Отладчик теперь позволяет удобно фильтровать массивы и коллекции в окне Variables, Evaluate Expression, Watches, Inspect и других.



Kotlin 1.1.3


Кроме поддержки более свежей версии языка, IDE получила подсказки имен параметров методов (Parameter Hints, знакомые по Java и Groovy), подсказки выведенных типов (Type Hints), семантическую подсветку (также знакомую ранее по Java и другим языкам), а также подсветку TODO элементов.

Подсказки выведенных типов – принципиально новая функция, делающая код более читаемым.



Groovy 2.5.0


Поддержка Groovy 2.5.0 главным образом включает поддержку новых аннотаций: groovy.transform.builder.Builder#includeSuperProperties, groovy.transform.AutoImplement, и groovy.lang.Delegate для методов.

Контроль версий


Окно инструментов Log для Git предоставляет две новые функции: Revert Commit (откат изменений), а также Reword Commit. Последняя позволяет изменить описание неотправленного изменения.



Кроме этого, IDE получила возможность автоматического форматирования описаний изменений согласно настройкам (Version Control | Commit Dialog): blank line between the subject and the body, limit body line, и limit subject line. При редактировании описания изменения в Commit диалоге стали доступны инспекция и функция Reformat:



Пользовательский интерфейс


Область редактора внутри попапа Find in Path получила левую панель с элементами навигации и индикаторами локальных изменений. Попап отображает прогресс поиска. Результаты поиска с одних и тех же строк объединяются в один результат.



Элементы интерфейса на Window 10 стали выглядеть нативно.



В случае нескольких мониторов с высоким разрешением (HiDPI), теперь IDE правильно определяет разрешение: в зависимости от того, на котором мониторе элемент отображается (ранее всегда использовалось разрешение главного монитора).

Размер шрифтов теперь также автоматически корректируется в зависимости от разрешения.

JavaScript и TypeScript


В TypeScript-коде, как и в Java, теперь показываются имена параметров. Также стало доступно действие Expression Type (ранее знакомое по Java):



Действие Rearrange Code, знакомое тем, кто работал с Java-кодом в IntelliJ IDEA, стало доступно для JavaScript и TypeScript. Оно позволяет упорядочить код внутри классов.



С помощью нового Move Symbol рефакторинга можно безопасно переносить классы, методы и переменные из одного ES6-модуля в другой. IDE автоматически обновит все импорты и экспорты.



В новой версии мы улучшили поддержку Angular Material, — библиотеки UI-компонентов для Angular. Теперь IDE понимает и подсказывает специальные атрибуты из Angular Material.



При тестировании JavaScript-приложений с Mocha теперь можно узнать, насколько хорошо код покрыт тестами. А при работе с Karma теперь можно запускать каждый тест отдельно.



Автодополнение и навигация в JavaScript-коде теперь учитывает конфигурацию проекта (например, aliases), описанную в webpack.config.js.



Производительность


Любой модуль теперь можно указать как “незагруженный” (Unloaded). В этом случае IDE исключит его из индексации и компиляции.



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



Кроме модулей, теперь также стало возможно исключить файлы из индексации по выражению. Это может быть полезно, если вы хотите исключить файлы определенного типа, в именах которых повторяются определенные паттерны (например, *_min.js).

Процесс индексации теперь можно приостанавливать. Это может быть полезно, когда нужно срочно высвободить ресурсы CPU.

Вкратце, это все важные изменения. Подробнее об изменениях можно узнать на странице What’s New. Там же можно скачать обновление.

Буду рад ответить на любые вопросы.

Программируйте с удовольствием!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333648/


Метки:  

[Перевод] Применение принципа poka-yoke в программировании на примере PHP

Вторник, 18 Июля 2017 г. 18:16 + в цитатник


Всем привет! Я Алексей Грезов, разработчик Server Team Badoo. Мы в Badoo всегда стараемся сделать так, чтобы наш код было легко поддерживать, развивать и переиспользовать, ведь от этих параметров зависит, насколько быстро и качественно мы сможем реализовать какую-либо фичу. Одним из способов достижения этой цели является написание такого кода, который просто не позволит совершить ошибку. Максимально строгий интерфейс не даст ошибиться с порядком его вызова. Минимальное количество внутренних состояний гарантирует ожидаемость результатов. На днях я увидел статью, в которой как раз описывается, как применение этих методов упрощает жизнь разработчикам. Итак, предлагаю вашему вниманию перевод статьи про принцип "poka-yoke".


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


Эти ошибки могут быть обнаружены при тестировании, но есть реальный шанс, что они-таки проскользнут в продакшн. И даже если они будут выявлены, может потребоваться довольно много времени, чтобы откатить код и исправить его.


Итак, как мы можем предотвратить это? С помощью принципа «poka-yoke».


Что такое poka-yoke?


Poka-yoke – японский термин, который переводится на английский примерно как «mistake-proofing» (защита от ошибки), а в русском варианте более известен, как «защита от дурака». Это понятие возникло в бережливом производстве, где оно относится к любому механизму, который помогает оператору оборудования избежать ошибок.


Помимо производства, poka-yoke часто используется в бытовой электронике. Возьмём, к примеру, SIM-карту, которая благодаря своей асимметричной форме может быть вставлена в адаптер только правильной стороной.



Противоположным примером (без использования принципа poka-yoke) является порт PS/2, имеющий одинаковую форму разъёма и для клавиатуры, и для мыши. Их можно отличить только по цвету и поэтому легко перепутать.



Ещё концепция poka-yoke может использоваться в программировании. Идея в том, чтобы сделать публичные интерфейсы нашего кода как можно более простыми и понятными и генерировать ошибки, как только код будет использоваться неправильно. Это может показаться очевидным, но на самом деле мы часто сталкиваемся с кодом, в котором этого нет.


Обратите внимание, что poka-yoke не предназначен для предотвращения преднамеренного злоупотребления. Цель лишь в том, чтобы избежать случайных ошибок, а не в защите кода от злонамеренного использования. Так или иначе, пока кто-то имеет доступ к вашему коду, он всегда сможет обойти предохранители, если действительно этого захочет.


Прежде чем обсуждать конкретные меры, позволяющие сделать код более защищённым от ошибок, важно знать, что механизмы poka-yoke можно разделить на две категории:


  • предотвращение ошибок
  • обнаружение ошибок.

Механизмы предотвращения ошибок полезны для исключения ошибок на раннем этапе. Максимально упростив интерфейсы и поведение, мы добиваемся того, чтобы никто не мог случайно использовать наш код неправильно (вспомните пример с SIM-картой).


С другой стороны, механизмы обнаружения ошибок находятся вне нашего кода. Они контролируют наши приложения, чтобы отслеживать возможные ошибки и предупреждать нас о них. Примером может быть программное обеспечение, которое определяет, имеет ли устройство, подключённое к порту PS/2, правильный тип, и, если нет, сообщает пользователю, почему оно не работает. Такое ПО не могло бы предотвратить ошибку, поскольку разъёмы одинаковые, но оно может обнаружить её и сообщить об этом.


Далее мы рассмотрим несколько методов, которые можно использовать как для предотвращения, так и для обнаружения ошибок в наших приложениях. Но имейте в виду, что этот список является лишь отправной точкой. В зависимости от конкретного приложения могут быть приняты дополнительные меры, чтобы сделать код более защищённым от ошибок. Кроме того, важно убедиться в целесообразности внедрения poka-yoke в ваш проект: в зависимости от сложности и размера вашего приложения некоторые меры могут оказаться слишком дорогостоящими по сравнению с потенциальной стоимостью ошибок. Поэтому вам и вашей команде решать, какие меры подходят вам лучше всего.


Примеры предотвращения ошибок


Объявление типов


Ранее известное как Type Hinting в PHP 5, объявление типов – это простой способ защиты от ошибок при вызове функций и методов в PHP 7. Назначив аргументам функции определённые типы, становится сложнее нарушать порядок аргументов при вызове этой функции.


Например, давайте рассмотрим уведомление, которое мы можем отправить пользователю:


https://habrahabr.ru/post/333592/


Метки:  

Обучение в онлайн-магистратуре как вариант профессионального и карьерного роста

Вторник, 18 Июля 2017 г. 17:46 + в цитатник


Так получалось, что мы рассказываем о наших проектах, но мало уделяем внимания содержанию наших нововведений. Сегодня мы постараемся рассказать об онлайн-магистратуре по современной комбинаторике через призму только что запущенного проекта — Лаборатории прикладных исследований МФТИ — Сбербанк.

Многие наши студенты выбирают онлайн-магистратуру именно по причине возможности совмещения работы и учебы. Но мы, по сути, уже сейчас готовы дать нашим студентам возможность трудоустроиться и продолжать работать над проектом. который они сами выбрали в процессе учебы. То есть мы говорим даже не о совмещении, а о включении работы в образовательный процесс. В частности, несколько наших студентов начинают работу в Лаборатории прикладных исследований МФТИ — Сбербанк.

Это реальный проект, запущенный буквально в этом году, перед сотрудниками (а это уже сейчас более 30 человек) которого поставлены важнейшие для деятельности любого крупного банка задачи.

Сотрудники Лаборатории разделены на несколько групп, каждую из которых возглавляют опытные специалисты: эксперты в области машинного обучения, теории графов и т.п.

Какие задачи стоят перед Лабораторией:
  • выявление рисков на основе анализа базы клиентов Сбербанка, а также прогнозирование возможного фрода;
  • другие задачи, решаемые с помощью математического моделирования;
  • задачи, связанные с кибербезопасностью деятельности банка: биометрия, работа с большими данными (криптография), современные виды шифрования.

Студенты онлайн-магистратуры имеют возможность не только на практике отрабатывать полученные знания, но и стать сотрудниками Лаборатории и развиваться в выбранной области как профессионально, так и карьерно.

Более того, хотя обучения в онлайн-магистратуре, как мы уже говорили, платное, работа на проектах Лаборатории официально оплачивается.

Ниже мы приведем мнение одного нашего студента, выбравшего Лабораторию в качестве практики. Мы надеемся, что это поможет вам в принятии решения о поступлении именно в онлайн-магистратуру МФТИ. И на сякий случай напоминаем, что прием в онлайн-магистратуру заканчивается. Заявку вы можете оставить на сайте.

Меня зовут Роман Пилюгин, учусь в онлайн -магистратуре «Современная комбинаторика» МФТИ. Мой научный руководитель — Тренин Сергей Алексеевич. Мне очень повезло попасть к нему, он мне и предложил присоединится к организующейся лаборатории МФТИ-Сбертех. К этому моменту у меня появилось возможность посещать МФТИ по будням два раза в неделю без проблем с основным местом работы и я ею воспользовался. Поскольку только оформил документы в МФТИ, то пока рассказать о том как работает лаборатория не могу, но определенные ожидания у меня есть. Как я понимаю, помимо теоретических исследований тут есть четкий фокус на практическую сторону задач, решаются технические и технологические проблемы. Для меня это возможность применить полученные знания на практике и прикоснуться к задачам, взятым из жизни, с их сложными формулировками и подчас эвристическим подходом. К тому же, это редкая возможность поработать в одной команде с людьми, которые идут в авангарде науки в России. Думаю это будет бесценный опыт для меня.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333638/


Метки:  

[Из песочницы] Emacs + удобный менеджер окон и буферов

Вторник, 18 Июля 2017 г. 17:32 + в цитатник
Привет, хабражители!

Недавно, около года назад, я начал увлекаться емаксом. Спасибо за это товарищу по работе, который много чего рассказал и влюбил меня в емакс.

Но, не хватало мне очень хорошего и удобного менеджера буферов, я начал искать и нашел emacs-purpose.

Это очень удобная система построения своей конфигурации буферов и их расположения на странице. Что интересно, что она подразумевает что у каждого буфера есть предназначение и соответственно целевое место в твоем layoutе. На основании этого extensionа даже возможно сделать свой собственный ide в emacs очень легко. Итак, давайте рассмотрим несколько шагов по построению своей версии IDE используя этот движок.

Для затравки, вот то как выглядит мой интерфейс емакса.

image

Мы будем строить свою версию IDE в несколько шагов:

  1. построение своей структуры расположения буферов
  2. назначение каждому буферу списка режимов, которые будут отображаться в этом буфере.
  3. упрощение переключения между буферами.
  4. другие улучшения юзабилити для нашего IDE.

1. Построение своей структуры расположения буферов


Это очень просто!

В файле window-purpose-x.el нужно поменять определение переменной purpose-x-code1—window-layout.
В идеале это можно сделать также поместив layout отдельно в папку ~/.emacs.d/layouts/ и затем подгрузив layout следующей функцией.

(purpose-load-window-layout-file "~/.emacs.d/layouts/full-ide.window-layout")

но пока почему то это не работает, отправил Pull request с новым layout автору (разбираемся почему не работает).

Пока это не пофикшено в основном репозитории, то мне пришлось поправить window-purpose-x.el. Вообще судя по тому что я понял, там проблема с новыми типами purposeов, которые я ввожу в layout, проблема по всей видимости в функции:
pull request

Вот моя конфигурация, которая выглядит так (рисунок был сверху):

elisp код
(nil
    (0 0 152 35)
    (t
     (0 0 29 35)
     (:purpose dired :purpose-dedicated t :width 0.16 :height 0.5 :edges
               (0.0 0.0 0.19333333333333333 0.5))
     (:purpose buffers :purpose-dedicated t :width 0.16 :height 0.4722222222222222 :edges
               (0.0 0.5 0.19333333333333333 0.9722222222222222)))
    (t
     (29 0 125 35)
    (:purpose edit :purpose-dedicated t :width 0.6 :height 0.85 :edges
              (0.19333333333333333 0.0 0.8266666666666667 0.85))
    (:purpose misc :purpose-dedicated t :width 0.6 :height 0.1 :edges
              (0.19333333333333333 0.8722222222222222 0.8266666666666667 0.9722222222222222))
    )
    (t
     (125 0 152 35)
    (:purpose ilist :purpose-dedicated t :width 0.15333333333333332 :height 0.6 :edges
              (0.82666666666666667 0.0 0.9722222222222222 0.6))
    (:purpose todo :purpose-dedicated t :width 0.15333333333333332 :height 0.372222222 :edges
              (0.8266666666666667 0.6 0.9722222222222222 0.9722222222222222))

     )
    )

А вообще достаточно этот текст будет поместить в ~/.emacs.d/layouts/full-ide.window-layout и добавить такую вещь в наш init.el

(defun load-purpose-mode ()
  (interactive)
  (purpose-load-window-layout-file "~/.emacs.d/layouts/full-ide.window-layout")
  (purpose-x-code1-setup)
)
(global-set-key (kbd "M-L") 'load-purpose-mode)

по нажатию Alt+Shift+L у меня загружается эта конфигурация. Кстати, подсказка: можно сделать кучу конфигураций. Например, если вы переходите в режим анализа изменений в гите, то можно сделать конфигурацию специально для анализа состояния репозитория.

Постепенно мы будем дополнять нашу функцию load-purpose-mode другими вкусностями.

2. Назначение каждому буферу списка режимов, которые будут отображаться в этом буфере


Для своего IDE я определил следующим режимам отображаться в буферах с определенными purposами.
misc purpose todo purpose edit purpose
inferior-python-mode org-mode css-mode
python-inferior-mode yaml-mode
gdb-inferior-io-mode conf-unix-mode
fundamental-mode *magit*
compilation-mode
shell-mode
eshell-mode
term-mode
Сделать это очень просто (один из вариантов):

(add-to-list 'purpose-user-mode-purposes
             '(YOUR_MODE . PURPOSE))

Для magit все немного сложней (из документации emacs-purpose):

(defvar purpose-x-magit-single-conf
    (purpose-conf "magit-single"
      :regexp-purposes '(("^\\*magit" . misc)))
      "Configuration that gives each magit major mode the same purpose.")
(purpose-x-magit-single-on)

После всех ваших изменений не забудьте выполнить следующую команду в вашем init.el:

(purpose-compile-user-configuration)

Тогда ваши изменения применятся.

3. Упрощение переключения между буферами


Много буферов? Но вроде бы они разделены по назначениям, но как между ними переключаться? Благодаря этой библиотеке – это достаточно легко.

Вот мой список команд для переключения на буферы с разными назначениями:

elisp код
;; helper для получения первого буфера с определенным purpose. нужна для прямого переключения на нужный буфер. (ie, на специальный purpose буфер)
(defun get-only-one-buffer-with-purpose (purpose)
  "Get buffers wih purpose"
  (buffer-name (nth 0 (purpose-buffers-with-purpose purpose)))
  )
;; переключитья на dired buffer, к сожалению, на каждую открытую директорию создается буфер, это значит что по прошествии нескольких часов у вас может быть открыто очень много dired буферов. Здесь есть один недостаток. А может можно проще ? Я не нашел как переключить просто на активный буфер с конкретным purpose. Итак, ты сможешь выбрать на какой буфер переключиться
(define-key purpose-mode-map (kbd "C-c C-f")
  (lambda () (interactive) (purpose-switch-buffer-with-some-purpose 'dired))
  )
;; переключиться на буфер со списком открытых файлов. Здесь все просто супер. Он один. Поэтому нет проблемы.
(define-key purpose-mode-map (kbd "C-c C-l")
  (lambda () (interactive) (purpose-switch-buffer (get-only-one-buffer-with-purpose 'buffers)))
  )
;; переключиться на один из файлов для редактирования. Та же проблемы что и с dired
(define-key purpose-mode-map (kbd "C-c C-c")
  (lambda () (interactive) (purpose-switch-buffer-with-some-purpose 'edit))
  )
;; переключиться на буфер с списком определений (функций, классов и тд и тп) в открытом файле для редактирования. Здесь все просто супер. Он один. Поэтому нет проблемы.
(define-key purpose-mode-map (kbd "C-c C-d")
  (lambda () (interactive)  (purpose-switch-buffer (get-only-one-buffer-with-purpose 'ilist)))
  )
;; переключиться на буфер с списком todo вещей. Об этом расскажу чуть позже. Полезная вещь. И он тоже один
(define-key purpose-mode-map (kbd "C-c C-t")
  (lambda () (interactive)  (purpose-switch-buffer (get-only-one-buffer-with-purpose 'todo)))
  )

4. Другие улучшения юзабилити для нашего IDE


Лично для меня важно еще видеть список todo в проекте, напоминает о каких-то важных вещах.

Поэтому я сделал специально буфер с purpose todo. И открываю в нем файл написанный для org-mode, в котором содержится список todo конкретного проекта.
Генерируется этот файлик автоматически при активации window-purpose layoutа

В функцию load-purpose-mode (приводилась выше) в конец нужно добавить (todo-mode-get-buffer-create) и вот кусочек elispа отвечающего за этот процесс

elisp код
(defconst todo-mode-buffer-name "*CodeTodo*"
  "Name of the buffer that is used to display todo entries.")

;; когда процесс создания todo файла закончился, выполнится эта функция, фактически эта функция откроет файл в read only mode с списком @todo. Можно будет перейти к месту @todo и посмотреть что там за вопрос не решен.
(defun on-org-mode-todo-file-built (process event)
  (find-file (concat (getenv "PWD") "/todo.org"))
  (call-interactively 'read-only-mode)
  )

;; функция отвечающая за генерацию todo файла для org-mode
(defun build-org-mode-file-for-todo ()
  (start-process "Building todo things" "*CodeTodo*" "bash" "-ic" "source ~/.bashrc; collecttodotags")
  (set-process-sentinel (get-process "Building todo things") 'on-org-mode-todo-file-built)
  )

;; создание буффера если его еще нет, запуск функций приведенных выше
(defun todo-mode-get-buffer-create ()
    "Return the todo-mode buffer.
If it doesn't exist, create it."
    (or (get-buffer todo-mode-buffer-name)
        (let ((buffer (get-buffer-create todo-mode-buffer-name)))
          (with-current-buffer buffer
            (org-mode)
            (build-org-mode-file-for-todo)
            (pop-to-buffer todo-mode-buffer-name))
          buffer)))

Команда collectotodotags это алиас на башевскую команду:

alias collecttodotags="find `pwd` -type d \( -name .git -o -name myworkenv -o -name node_modules \) -prune -o -type f \( -name todo.org \) -prune -o -type f -print -exec grep -n '@todo' '{}' \; | create_org_mode_todo_file.py > ./todo.org"

Эта команда находит список всех файлов с упоминанием места, где есть todo в коде и посылает список этих файлов с todo на вход следующему питоновскому файлу:
github

Причем обратите внимание, как можно указать, что какое-то todo важнее другого
github

Чем @todooo имеет больше «o» в конце, тем оно важней. Тут не совсем уверен как лучше было бы сделать, может кто подскажет?

И да, последняя вкусность. Смысл misc purposа в том, чтобы отображать там все промежуточные вещи, поиски и тд и тп, но буфер маленький, поэтому я добавил следующие shortcuts:

(global-set-key "\C-c+" (lambda () (interactive) (enlarge-window +20)))
(global-set-key "\C-c_" (lambda () (interactive) (enlarge-window -20)))

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

И в целом как вам этот emacs extension?
Спасибо за внимание!

P.S. Если интересна тема, то могу продолжить и рассказать другие моменты касательно моего конфига emacsа
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333640/


Метки:  

[Перевод] XBRL: Просто о сложном - 1. Введение

Вторник, 18 Июля 2017 г. 17:17 + в цитатник

От переводчика


В 2015 году Центральный Банк РФ запустил проект перехода некредитных финансовых организаций (НФО) на электронный формат представления отчетных данных в формате XBRL с 01.01.2018. Сроки уже подходят, а НФО только начинают осознавать масштабы грядущих изменений. Качественных материалов про XBRL на русском языке достаточно мало (могу разве что рекомендовать книгу XBRL для чайников, перевод которой был инициирован ЦБ, правда выполнен не в лучшем виде). Хочу восполнить этот пробел и предлагаю вашему вниманию свою адаптацию неплохой брошюры XBRL in Plain English от компании Batavia, которая рассказывает об основах XBRL.


Перевод веду от лица автора, немного дополняю текст полезными ссылками. Стараюсь придерживаться терминологии ЦБ РФ со ссылкой на оригинальные термины. Начну с первых глав, и если тема будет вам интересна, завершу перевод. Комментируйте, задавайте вопросы - расскажу все, что знаю.


Роман Удальцов




1. Введение


В этой главе представлена сама книга и основные понятия XBRL


1.1. Что ожидать


Если вы начали читать эту книгу, значит вы уже слышали о новом способе формирования бизнес-отчетности - XBRL. Если вы взглянули на спецификацию XBRL, то знаете, что она представляет собой 158-страничный документ, полный формальных определений. Такой документ нужен для корректного определения XBRL. Его можно считать расслабляющим чтивом на ночь для математиков. Но не для нас, нормальных людей.


Для нас, нормальных людей, эта книга передает суть спецификации XBRL простым русским языком. Она должна дать вам хорошее понимание того, что такое XBRL и как его можно использовать. В основном, книга фокусируется на представленной в спецификации функциональности XBRL.


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


Такими блоками будет обозначаться более глубокое погружение в детали (где это действительно необходимо)

Я также не буду дискутировать на тему таких базовых технических стандартов как XML, XML Schema, XLink, XPath, XPointer и т.д. Если вам не очень знакомы эти технологии, загляните на сайт W3C (World Wide Web Consortium) за списком рекомендованной литературы или в любую хорошую книжку по XML.


Эта книга основывается на спецификации XBRL 2.1 от 20.02.2013 с исправлениями от 25.04.2005. Если вдруг встретятся расхождения между книгой и официальной спецификацией, скромность требует от меня предположить, что это я ошибся, а авторы спецификации сделали все правильно. Я бы рекомендовал вам сделать аналогичное предположение.


За неимением богатых возможностей форматирования в Markdown и HFM, такими же блоками будут обозначаться примеры

1.2. Представляю вам XBRL


XRBL расшифровывается как Extensible Business Reporting Language (расширяемый язык деловой отчетности), что само по себе неплохо описывает суть: это язык отчетности, используемый в бизнесе. И он расширяемый. Все просто, да? Ну, может быть, потребуется немного больше объяснений.


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


Давайте прыгнем сразу в середину:… Business Reporting ...


1.2.1. Business Reporting (Деловая отчетность)


Мы все знаем, что бизнес формирует кучу отчетности:


  • налоговые декларации
  • ежегодные отчеты
  • внутренние показатели продаж
  • ...

Каждый отчет - это данные, представляющие собой набор фактов про содержимое отчета, таких как:


  • отчетный период
  • годовой доход
  • количество клиентов
  • количество продаж
  • инвентарные номера
  • ...

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


Звучит громоздко, но так оно и есть. Дальше - хуже… Разные заинтересованные лица требуют данные в разной форме, при этом содержащиеся в них факты могут быть одинаковыми. Для этого составитель отчета вынужден заполнять одни и те же факты в разные формы.


XBRL предлагает способ улучить процесс создания, распространения и использования данных в бизнес-отчетах. Он определяет электронный формат для отчетности, позволяющий компьютерам автоматически создавать, валидировать и обрабатывать отчетность. Он также определяет способ обеспечения единого смыслового значения передаваемых бизнес-фактов. Составитель отчета мог бы просто сделать один отчет со всеми фактами и передать его получателю, который выбирал бы нужные ему факты и представлял бы в любой удобной ему форме. Определение единого смыслового значения фактов гарантирует, что каждый получатель отчета интерпретирует полученные факты одинаково.


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


Стандарт XBRL также использует подобное разделение:


  • Определение того, что должно или может содержаться в отчете, описывается так называемой таксономией (taxonomy) - она определяет концепты (concept) в сфере бизнеса, по которым формируется отчетность.
  • Сами отчетные данные называются отчетом XBRL (instance document). Он содержит передаваемые получателю факты. Отчет ссылается на таксономию для придания фактам смысла. Каждый из фактов в пределах отчета связан с соответствующим концептом в таксономии.

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

Форма отчета выглядит следующим образом:

image

Отчетная форма может быть однозначно определена по своему уникальному идентификатору ФД-01. Она состоит из следующих элементов – наименование компании, отчетный период, количество сотрудников на начало и конец отчетного периода. Также, требуется разбить количество сотрудников на мужчин и женщин и распределить их по нескольким возрастным группам.

Пример заполненного отчета может выглядеть следующим образом:

image

Несложно заметить, что количество сотрудников увеличилось, но в компании работает как минимум один человек с недостатком математических навыков. В таком простом примере вряд ли кто-то посчитает 27 + 15 как 41, но в более сложных отчетах такие ошибки весьма вероятны, если все делается вручную.

1.2.2. Extensible (Расширяемый)


Другая предпосылка XBRL заключается в том, что он расширяемый. Возвращаясь к старым добрым временам, давайте рассмотрим сценарий, в котором расширяемость была бы полезна.


Предположим, что Европейский Союз определяет требования к отчетности для любого бизнеса в рамках ЕС.


  1. Такое требование, скорее всего, будет указано на английском, но большинство компаний хотели бы иметь отчетную форму в своем собственном языке, поскольку перевод бизнес-терминов может быть очень сложным.
  2. Возможно, в некоторых странах правительство уже выдвигает такие требования к отчетности, причем с некоторыми специфичными для данной страны дополнениями. Чтобы избежать необходимости готовить две разные формы с пересекающимися требованиями, обе формы могут быть объединены в общую форму для конкретной страны. Но это все равно потребует создания новой версии основной формы.

XBRL позволяет поддерживать такие требования. ЕС создаст одну таксономию для определения требований к отчетности. Перевод технических концептов в таксономии на понятные пользователю термины содержится в так называемой базе ярлыков (label linkbase). Каждый язык внутри ЕС может иметь свою собственную базу ярлыков или можно создать одну общую базу, содержащую ярлыки для каждого языка. Обратите внимание, что при этом фактическое определение концептов не требуется повторять для каждого языка.


Страна, желающая расширить таксономию ЕС, просто создаст свою собственную таксономию, которая будет ссылаться на таксономию ЕС в части общих концептов. Этой стране достаточно будет доопределить только специфичные концепты, не входящие в таксономию ЕС.


1.2.3. Language (Язык)


‘L’ в XBRL обозначает Язык. Язык XBRL обеспечивает способ выражения таксономий и отчетов XBRL в едином однозначном формате, что является необходимым требованием для обработки информации компьютером.


Язык XBRL основан на таких мировых стандартах как XML и соответствующих им спецификациях. В следующих главах об этом будет рассказано более подробно.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333636/


Метки:  

Что такое SMT и как оно работает в приложениях — плюсы и минусы

Вторник, 18 Июля 2017 г. 17:02 + в цитатник

Метки:  

WebStorm 2017.2 – что нового в поддержке JavaScript, TypeScript, Angular и Sass и работе с ESLint, Karma и Mocha

Вторник, 18 Июля 2017 г. 16:51 + в цитатник
На этой неделе вышло большое обновление WebStorm. В этом посте мы расскажем подробнее о том, какие улучшения ждут JavaScript-разработчиков в WebStorm 2017.2 и других IDE JetBrains версии 2017.2.

Вы можете прочитать подробнее о WebStorm 2017.2 и скачать его на нашем сайте.

Улучшения в поддержке JavaScript и TypeScript


Move symbol


Новый рефакторинг Move symbol переносит классы, глобальные функции и переменные из одного JavaScript или TypeScript файла в другой. IDE автоматически добавит экспорт и обновит ES6-импорты в файлах, где используется этот символ.

Чтобы вызвать этот рефакторинг, поставьте курсор на символ в ES6-модуле и нажмите F6.



Улучшенная поддержка JavaScript в проектах с webpack


Автодополнение кода и навигация в JavaScript-файлах стали точнее в проектах, использующих webpack aliases и modules. WebStorm запускает webpack.config.js в корне проекта и использует результаты для построения более точной модели проекта. В результате WebStorm правильно поймет символы и пути в импортах.



Кстати, ES6-импорты в JavaScript-файлах теперь добавляются автоматически при автодополнении символов, объявленных и явно заэкспортированных в вашем проекте.

Параметры и выведенные типы в TypeScript


В TypeScript-файлах имена параметров отображаются прямо в редакторе, чтобы было проще читать код. По умолчанию, подсказки отображаются только для параметров, являющихся literals или function expressions. Настроить отображение подсказок можно в настройках: Editor | General | Appearance – Show parameter name hints.



Нажмите cmd или Ctrl и наведите на символ в TypeScript-коде, и WebStorm покажет информацию о его типе с учетом текущего контекста. Это помогает быстрее находить проблемы, связанные с использованием типов.

Форматирование кода


Импорт стилей форматирования из .eslintrc


Если вы используете ESLint для проверки стиля форматирования JavaScript-кода, WebStorm предложит применить правила, описанные в .eslintrc или в поле eslintConfig в package.json, к проектным настройкам форматирования JavaScript. Когда вы откроете JavaScript-файл в таком проекте, вы увидите нотификацию “Apply code style from ESLint?”.

Мы нашли соответствие между 37 правилами ESLint и настройками форматирования в WebStorm. Например: indent, curly или no-trailing-spaces. Стоит иметь в виду, что WebStorm не всегда поддерживает сложные опции в правилах и не поддерживает правила из плагинов и пресетов, добавленных через поле extends.

Главная цель этой интеграции – сделать так, чтобы переформатирование кода с помощью IDE не ломало верно отформатированный код с точки зрения ESLint и помогало писать новый код, максимально соответствующий правилам из .eslintrc.

Rearrange code


С новыми настройками стиля Arrangement теперь можно настроить порядок блоков в JavaScript- и TypeScript-классах и применить их с помощью действия Rearrange code. Его можно вызвать через Find action (Cmd/Ctrl-Shift-A) или выполнять при переформатировании кода. Для этого вызовите расширенный диалог форматирования, нажав Alt-Shift-Cmd-L на macOS или Ctrl+Alt+Shift+L на Windows and Linux, и отметьте галочкой Rearrange Code.



С новыми настройками Code Style | Blank lines можно указать необходимое число пустых строк после импортов, классов, методов и функций.

Форматирование JSX


Если вы следуете стилю Airbnb для React, поставьте значение When multiline для новых опций New line before first attribute и New line after last attribute в настройках стиля форматирования HTML (эти правила применяются к JSX-тегам).

Тестирование кода


Запуск Karma-тестов


Karma-тесты теперь можно запускать и отлаживать по одному. Нажмите на иконку в редакторе слева от теста или тест-сьюта и выберите Run или Debug. После того, как тест выполнится, иконка покажет его статус.



Далее вы можете запускать сохраненную или автоматически созданную Karma-конфигурацию через попап Run… (Ctrl-Alt-R на macOS или Alt+Shift+F10 на Windows и Linux).

Покрытие кода тестами для Mocha


При тестировании с Mocha теперь можно узнать, насколько хорошо код покрыт тестами.

Для этого нужно сперва установить nyc (новый командный интерфейс для Istanbul), а потом запустить Mocha Run with coverage… с помощью иконки запуска тестов в редакторе или через Mocha-конфигурацию.

Отчет появится в отдельном окне в WebStorm и покажет сколько файлов покрыто тестами и сколько процентов строк в них покрыто тестами. Из отчета можно быстро перейти в самому файлу. Слева в редакторе строки, покрытие тестами, будут отмечены зеленым цветом, а непокрытые строки – красным.



Stylesheets


Улучшения в поддержке Sass и SCSS


Автодополнение имен классов в HTML-файлах теперь работает для Sass- и SCSS-селекторов, созданных через &.



К таким селекторам, а также к nested-селекторам, можно быстро перейти из HTML-файла с помощью Cmd-click/Ctrl+click.

Angular


Поддержка Angular Material


Angular Material – это библиотека UI-компонентов для Angular. Теперь WebStorm лучше понимает и подсказывает специальные атрибуты из Angular Material в Angular-шаблонах.



Создание метода из Angular-шаблона


Если вы использовали новый метод в Angular-шаблоне, но еще не объявили его в классе компонента, нажмите Alt-Enter на его имени и выберите Create method, чтобы его создать.

Нотификация о npm install


При работе с JavaScript-проектами часто приходится устанавливать зависимости через npm и легко забыть, что нужно обновить зависимости при обновлении из Git или переключении на другой branch. WebStorm напомнит, что нужно запустить npm install или npm update, когда вы открыли проект или если package.json обновился.

Помимо этого в WebStorm 2017.2 вы можете:

  • Показывать файлы с одинаковыми именами, но разными расширениями из одной директории сгруппированными. Изменить правила группировки можно через File nesting в настройках Project view (иконка с шестеренкой).
  • Использовать улучшенную поддержку React stateless-компонентов и автодополнение props.
  • Использовать breadcrumbs внизу редактора в JavaScript- и TypeScript-файлах, чтобы быстрее навигировать по файлу.
  • Автоматически заменить module.exports на ES6-экспорт: нажмите Alt-Enter на выражении и выберите Convert to export или запустите Run inspection by name — “module.export is used instead of export”, чтобы внести изменения в нескольких файлах проекта.
  • Настроить и проверять формат сообщения о коммите в настройках Version Control | Commit Dialog.
  • Откатывать коммит в Git и изменять коммит-сообщение через контекстное меню коммита в Logs в окне Version Control.

Скачать WebStorm 2017.2 можно на сайте JetBrains.

Если у вас возникли проблемы с WebStorm, пожалуйста, сообщите об этом на нашем issue tracker.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333630/


Метки:  

Лидогенерация изнутри: от «сырого» трафика к реальному клиенту

Вторник, 18 Июля 2017 г. 16:51 + в цитатник
Со стороны процесс лидогенерации обманчиво прост: сбор трафика, фильтрация трафика, и конвертация пользователей, пришедших на сайт, в желаемый объем целевых лидов. Но еще до того момента, как с сайта заказчика начали приходить первые заявки, запускается технологический процесс, который мы подробно разберем в этом материале.

image

Первые шаги


Предварительный этап включает в себя оценку будущего проекта поставщиком услуг по лидогенерации, сбор информации и тестовые работы.

Рентабельность запуска проекта можно определить на основании полученных данных в течение одного рабочего дня.

Также поставщик услуг по лидогенерации проверяет технические параметры коммуникации, например, доступность каналов, выделенных для приема и обработки лидов. Здесь важны нюансы — для обеспечения качества связи переадресация звонков должна идти на стационарный, а не на мобильный телефон, а карточки клиентов, отправленные на e-mail, должны приходить в отдел продаж и не попадать в спам. Полезно согласовать конкретные дни и промежутки времени, в которые заказчик готов принимать и обрабатывать лиды.

Параллельно с проверкой идет подготовка и согласование скрипта, по которому будут валидироваться лиды усилиями call-центра компании-исполнителя. Скрипт в данном случае — это схема-сценарий разговора с абонентом. Она состоит из списка уточняющих вопросов, торгового предложения, которое нужно донести до пользователя, а также возможных действий при работе с возражениями. В процессе лидогенерации в скрипт можно внести дополнения, которые не были предусмотрены изначально.

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

Как мы собираем информацию о посетителях?


Есть множество инструментов веб-аналитики для сбора и обработки информации о посетителях сайта. Самые известные примеры — это Яндекс.Метрика и Google Analytics. Что именно использовать, каждая компания-лидогенератор решает для себя сама. Мы собираем информацию с помощью диспетчера тэгов и «пикселя», установленных на сайт заказчика.

Диспетчер тэгов — это система управления сбором информации с сайтов и посадочных страниц.

Технический отдел компании-исполнителя формирует «контейнер» (JavaScript-код) диспетчера тэгов, который встраивается на сайт и запускает сбор статистики о посетителях. В «контейнер» можно интегрировать любое количество «пикселей», не вмешиваясь в исходный код сайта и не влияя на скорость его загрузки. Самый известный пример диспетчера тэгов — Google Tag Manager.

Что такое «пиксель»? Это программный код, который фиксирует каждого посетителя сайта заказчика, присваивает ему уникальный идентификатор (cookie) и передает эту информацию владельцу пикселя (в данном случае — компании-лидогенератору) для последующей аналитики, например, в DMP-системе.

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

О фиксации данных посетителя сайта мы говорим с технической точки зрения. Под посетителем сервер понимает техническое устройство — персональный компьютер, мобильный телефон, планшет. Таким образом, посетитель описывается в системе как набор технических параметров устройства, которым он владеет. Параметрами являются операционная система, дата и время посещения, адрес, с которого посетитель пришел, данные о геолокации устройства и т.п. В интернет эти данные попадают через провайдера, к которому подключено устройство.

С момента установки программного кода на сайт заказчика начинают собираться cookies. Это большой объем «сырых» данных, для обработки которых удобно использовать аналитические системы.

В ИНСТАМ cookies поступают на платформу обработки данных DMP (более подробно о ней мы писали в предыдущей статье). Там информация сегментируется и сопоставляется с существующей базой, и на ее основе по определенным атрибутам выделяются лиды. Для лидогенерации интересны связки с номером телефона, а также с e-mail, именем и фамилией, которые пользователь вводил, например, при заполнении анкет на сайте.

Уже через 3-4 дня после установки «контейнера» накопленных данных достаточно для запуска полноценной кампании по лидогенерации, при условии, что на сайт из рекламных каналов поступает качественный трафик.

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

Сбор информации с сайта при помощи «пикселя» дает и определенную пищу для размышлений. Для процесса лидогенерации важен входящий трафик. И если трафика недостаточно для выделения оговоренного объема лидов, то можно отследить, какие рекламные каналы плохо работают (например, поисковая, мобильная, баннерная, контекстная реклама, email-маркетинг) и скорректировать их работу.

Обработка трафика


После официального запуска кампании начинается формирование целевой аудитории. Это процесс выделения целевого объема заявок из всего поступающего трафика с помощью различных инструментов. Выделение заявок — самая трудоемкая часть процесса лидогенерации, так как от точности выделения целевой аудитории зависят качество и конверсионность конечных лидов.

Если аудитория очень узкая (например, женщины, желающие купить коттедж с чистовой отделкой и мебелью в Пскове), то для ее выделения потребуется учесть гораздо больше параметров, чем для простой аудитории (потенциальных российских покупателей новой модели смартфонов Samsung).

Допустим, трафик из рекламных каналов на сайт генерируется в достаточном объеме. С сайта поступают данные в виде cookies-связок с номером телефона и прочими сведениями, которые пользователь ввел о себе. Этот объем информации практически невозможно обработать вручную, поэтому в дело вступают инструменты автоматизации и аналитики.

В частности, для того, чтобы сегментировать целевую аудиторию и выделить конкретные лиды, мы используем платформу обработки больших массивов данных — DMP. Поступающий трафик сопоставляется с уже имеющимися в системе данными, которые накоплены с сайта заказчика и из других источников, и по определенным критериям из него выделяется целевой объем.

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

Если входящего трафика недостаточно для достижения оговоренного объема лидов, то подключаются дополнительные инструменты аналитики. Так на основе данных DMP можно построить look-alike модель аудитории по сходным характеристикам-атрибутам (возраст, уровень дохода, места проживания и отдыха, социальное положение, географическое присутствие, предпочитаемые бренды, активность в соцсетях и т.п.).

Для выделения узкой целевой аудитории берутся данные нескольких посетителей сайта, подходящих под портрет целевых. Данные анализируются на наличие или отсутствие разных атрибутов. По нескольким наиболее релевантным атрибутам и строится look-alike модель, в которой пользователи похожи на выбранных, однако они на сайт заказчика не заходили и, возможно, даже не искали подобные товары или услуги. Это более «холодная» аудитория, и конверсия по аудитории look-alike получается ниже. К данной модели формирования аудитории стоит обращаться как к дополнительному инструменту.

Сформировав необходимый объем лидов, можно приступать к коммуникации с ними для проверки и отсева нецелевых заявок.

Способы коммуникации


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

Способы коммуникации делятся на пассивные и активные.

К пассивным относятся те способы, в которых контакт происходит опосредованно: e-mail рассылки, а также реклама — баннерная, контекстная, нативная, видео-реклама и т.д.

Активные методы коммуникации — это прямые коммуникации в реальном времени, например, персональные звонки и встречи. В разрезе лидогенерации мы говорим о звонках, так как их достаточно для валидации лида.

Преимущество активного подхода — управление временем реакции и интересом потенциального клиента, а также оперативная сортировка трафика. Без активной коммуникации сложно выделить точную долю эффективного трафика и процент «мусора», пока заказчик не отфильтрует полученный трафик собственными усилиями и с дополнительными затратами.

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

Личный разговор позволяет провести неоформленный лид по «правилу трёх вопросов»:

  1. интересовался ли пользователь данной областью (допустим, покупкой недвижимости),
  2. интересно ли ему конкретное предложение (покупка квартиры в определенном ЖК со скидкой),
  3. хочет ли он получить персональную консультацию менеджера.

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

Передача лидов заказчику


Готовый к разговору с продавцом, лид не будет ждать день или неделю — его нужно, что называется, брать теплым. Для этого еще на этапе разработки кампании утверждаются основные и запасные каналы, по которым заказчику будут передаваться лиды, а также входящий дневной или недельный объем звонков и конкретный временной промежуток для их перевода.

Закономерно возникает ответственность заказчика за прием и проведение лида по воронке продаж. И первое, с чего стоит начать — с налаженного канала коммуникации. Заказчику полезно выделять отдельный телефонный номер и напрямую принимать лиды в отдел продаж, а не держать готового к покупке клиента в очереди к секретарю или оператору собственного call-центра.

Что делать, если возникли технические проблемы с переводом звонка в реальном времени? В таком случае формируется карточка потенциального клиента с его именем, контактами и кратким описанием выявленного интереса. Карточка отправляется на e-mail заказчика.

Оптимальные объемы лидогенерации


Эффективность лидогенерационной кампании должна соответствовать эффективности работы отдела продаж заказчика. Нет никакого толка в поставке самых «горячих» и качественных лидов, если заказчик не справляется с их обработкой.

Приведем пример. Компания-застройщик заказывает 1000 лидов в месяц. При реализации такого объема на один рабочий день заказчика придется 45 заявок, которые менеджеры по продажам должны успеть обработать: принять звонок, в течение 8-10 минут добиться конкретной договоренности (записи клиента на просмотр квартиры или отправки ему коммерческого предложения), и работать с клиентом далее. 45 звонков — это 6 часов непрерывного разговора. Как правило, менеджеры отдела продаж не в состоянии уделять столько чистого времени на общение по телефону. Большую часть из 1000 лидов не удастся качественно обработать.

По нашим наблюдениям, заказчики в среднем готовы принимать от 100 до 200 заявок в месяц (до 9 заявок в день). Минимальный пороговый объем, который мы поставляем — 50 лидов. Это 2 лида в день, которые практически любая компания в состоянии перевести в разряд клиентов.

Из лида в клиенты: от чего зависит конверсия


Средний показатель конверсии из лида в продажу на рынке лидогенерации 8-10%. Этот показатель указан с учетом того, что

  • использовался активный подход к лидогенерации, и из общего трафика до 90% лидов приняты заказчиками как эффективные;
  • на принимающей стороне организована качественная обработка лидов — налажена работа менеджера или отдела продаж;
  • средний месячный объем лидов 150-200;
  • подсчет ведется с учетом жизненного цикла продажи продукта. Например, о конверсиях в продажу в сфере недвижимости можно говорить не раньше 3-4 месяца, так как продажа квартиры — небыстрый процесс.

Услуга лидогенерации изнутри — процесс, связанный с использованием целого арсенала программных продуктов, аналитических инструментов и техник. Сейчас управление бизнес-процессами онлайн связано с обработкой огромных массивов данных, и кампании по привлечению потенциальных клиентов без глубокой аналитики рискуют оказаться неэффективными. Поэтому выбор и использование IT-разработок, а также активных методов коммуникации в лидогенерации становится не прихотью, а условием качественной работы.

image
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333536/


Метки:  

Анализируем карьеру игроков NHL с помощью Survival Regression и Python

Вторник, 18 Июля 2017 г. 16:48 + в цитатник
Привет, Хабр! Сегодня рассмотрим один из подходов к оценке временного риска, который основан на кривой выживаемости и одноименной регрессии, и применим его к анализу продолжительности карьеры игроков НХЛ.

Когда у данного пациента произойдет рецидив? Когда наш клиент уйдет? Ответы на подобные вопросы можно найти с помощью анализа выживания, который может быть использован во всех областях, где исследуется временной промежуток от «рождения» до «смерти» объекта, либо аналогичные события: период от поступления оборудования до его выхода из строя, от начала использования услуг компании и до отказа от них и т.д. Чаще всего данные модели используются в медицине, где необходимо оценить риск летального исхода у больного, чем и обусловлено название модели, однако они также применимы в сфере производства, банковском и страховом секторах.

image


Очевидно, что среди рассматриваемых объектов всегда будут те, для которых «смерть» (выход из строя, наступление страхового случая) еще не произошла, и не учитывать их — распространенная ошибка среди аналитиков, поскольку в этом случае они не включают в выборку наиболее актуальную информацию и могут искусственно занизить временной интервал. Для решения данной проблемы был разработан анализ выживаемости, который оценивает потенциальную «продолжительность жизни» наблюдений с еще не наступившей «смертью».

Анализ карьеры игроков NHL


Данные и построение кривой выживаемости


Выше мы говорили о применении данного подхода в медицине и экономике, однако сейчас рассмотрим менее тривиальный пример — продолжительность карьеры игроков НХЛ. Все мы знаем, что хоккей — очень динамичная и непредсказуемая игра, где ты можешь, как Горди Хоу (26 сезонов) долгие годы играть на высочайшем уровне, а можешь из-за неудачного столкновения завершить карьеру после пары сезонов. Поэтому выясним: сколько сезонов может ожидать провести в НХЛ хоккеист и какие факторы влияют на продолжительность карьеры?

Посмотрим на уже очищенные и готовые для анализа данные:

df.head(3)

Name Position Points Balance Career_start Career_length Observed
Olli Jokinen F 419 -58 1997 18 1
Kevyn Adams F 72 -14 1997 11 1
Matt Pettinger F 99 -44 2000 10 1

Были собраны данные по 688 хоккеистам НХЛ, выступавших с 1979 года и сыгравших не менее 20 матчей, их позиции на поле, количеству набранных очков, пользе команде (±), началу карьеры в НХЛ и ее продолжительности. Столбец observed показывает, завершил ли игрок карьеру, то есть игроки со значением 0 все еще выступают в лиге.

Посмотрим на распределение игроков по количеству сыгранных сезонов:

Распределение похоже на логнормальное с медианой в 11 сезонов. Данная статистика учитывает лишь сыгранное до 2017 года количество сезонов действующими игроками, поэтому медианная оценка явно занижена.

Чтобы получить более точное значение, оценим функцию выживаемости с помощью библиотеки lifelines:

from lifelines import KaplanMeierFitter
kmf = KaplanMeierFitter()
kmf.fit(df.career_length, event_observed = df.observed)

Out:

Синтаксис библиотеки схож с scikit-learn и ее fit/predict: инициировав KaplanMeierFitter мы затем обучаем модель на данных. В качестве аргумента метод fit принимает временной интервал career_length и вектор observed.

Наконец, когда модель обучена, мы можем построить функцию выживаемости для игроков НХЛ:

kmf.survival_function_.plot()


Каждая точка графика — вероятность того, что игрок сыграет больше t сезонов. Как видно, рубеж в 6 сезонов преодолевают 80% игроков, в то время как совсем небольшому числу игроков удается сыграть больше 17 сезонов в НХЛ. Теперь более строго:

print(kmf.median_)
print(kmf.survival_function_.KM_estimate[20])
print(kmf.survival_function_.KM_estimate[5])

Out:13.0
0.0685611305647
0.888063315225

Таким образом, 50% игроков НХЛ сыграют больше 13 сезонов, что на 2 сезона больше чем первоначальная оценка. Шансы хоккеистов сыграть больше 5 и 20 сезонов равны 88,8% и 6,9% соответственно. Отличный стимул для молодых игроков!

Мы также можем вывести функцию выживаемости, вместе с доверительными интервалами для вероятности:

kmf.plot()


Аналогично построим функцию угрозы, используя процедуру Нельсона-Аалена:

from lifelines import NelsonAalenFitter
naf = NelsonAalenFitter()

naf.fit(df.career_length,event_observed=df.observed)
naf.plot()



Как видно на графике, в течение первых 10 лет риск у игрока НХЛ завершить карьеру в конце сезона крайне мал, однако после 10 сезона резко данный риск резко возрастает. Иначе говоря, проведя 10 сезонов в НХЛ, игрок будет все чаще задумываться о том, чтобы завершить карьеру.

Сравнение нападающих и защитников


Теперь сравним продолжительность карьеры защитника и нападающего НХЛ. Изначально предполагаем, что нападающие в среднем играют намного дольше, чем защитники, поскольку они чаще всего более популярны среди болельщиков, поэтому чаще получают многолетние контракты. Также с возрастом игроки становятся медленными, что больнее всего бьет по защитникам, которым все сложнее справляться с юркими нападающими.

ax = plt.subplot(111)

kmf.fit(df.career_length[df.Position == 'D'], event_observed=df.observed[df.Position == 'D'], 
        label="Defencemen")
kmf.plot(ax=ax, ci_force_lines=True)

kmf.fit(df.career_length[df.Position == 'F'], event_observed=df.observed[df.Position == 'F'], 
        label="Forwards")
kmf.plot(ax=ax, ci_force_lines=True)

plt.ylim(0,1);


Похоже, что продолжительность карьеры защитников чуть короче, чем у нападающих. Чтобы удостовериться в верности вывода проведем статистический тест на основе критерия хи-квадрат:

from lifelines.statistics import logrank_test

dem = (df["Position"] == "F")
L = df.career_length
O = df.observed

results = logrank_test(L[dem], L[~dem], O[dem], O[~dem], alpha=.90 )
results.print_summary()

Results
   t 0: -1
   test: logrank
   alpha: 0.9
   null distribution: chi squared
   df: 1

   __ p-value ___|__ test statistic __|____ test result ____|__ is significant __
         0.05006 |              3.840 |      Reject Null    |        True  

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

Регрессия выживаемости


Зачастую у нас также имеются и другие факторы, влияющие на продолжительность «жизни», которые мы бы хотели учесть. Для этого была разработана модель регрессии выживаемости, которая, как и классическая линейная регрессия имеет зависимую переменную и набор факторов.

Рассмотрим один из популярных подходов к оценке параметров регрессии — аддитивную модель Аалена, который в качестве зависимой переменной выбрал не сами временные интервалы, а рассчитанные на их основе значения функции угрозы $\lambda (t)$:

$\lambda(t) = b_{0}(t) + b_{1}(t)x_{1} + ... + b_{n}(t)x_{n}$


Перейдем к реализации модели. В качестве факторов модели примем количество очков, позицию игрока, дату начала карьеры и общую полезность команде (±):

from lifelines import AalenAdditiveFitter
import patsy # используем библиотеку patsy, чтобы создать design матрицу из факторов 
# -1 добавляет столбец из единиц к матрице, чтобы обучить константу модели
X = patsy.dmatrix('Position + Points + career_start + Balance -1', df, return_type='dataframe')
aaf = AalenAdditiveFitter(coef_penalizer=1.0, fit_intercept=True) # добавляем penalizer, который делает регрессию более стабильной, в случае мультиколлинеарности или малой выборки)

Теперь, когда все готово, обучим регрессию выживаемости:

X['L'] = df['career_length'] 
X['O'] = df['observed'] # добавляем career_length и observed к матрице факторов

aaf.fit(X, 'L', event_col='O')

Попробуем оценить, сколько еще сезонов отыграют в НХЛ 2 игрока: Дерек Степан, нападающий, который начал свою карьеру в 2010 году и набрал 310 очков с показателем полезности +109 и сравним его с менее успешным игроком НХЛ — Класом Дальбеком, который попал в НХЛ в 2014 году, набрал 11 очков с показателем полезности -12.

ix1 = (df['Position'] == 'F') & (df['Points'] == 360) & (df['career_start'] == 2010) & (df['Balance'] == +109)
ix2 = (df['Position'] == 'D') & (df['Points'] == 11) & (df['career_start'] == 2014) & (df['Balance'] == -12)

stepan = X.ix[ix1]
dahlbeck = X.ix[ix2]

ax = plt.subplot(2,1,1)

aaf.predict_cumulative_hazard(oshie).plot(ax=ax)
aaf.predict_cumulative_hazard(jones).plot(ax=ax)
plt.legend(['Stepan', 'Dahlbeck'])
plt.title('Функция угрозы')
ax = plt.subplot(2,1,2)

aaf.predict_survival_function(oshie).plot(ax=ax);
aaf.predict_survival_function(jones).plot(ax=ax);
plt.legend(['Stepan', 'Dahlbeck']);



Как и ожидалось, у более успешного игрока ожидаемая продолжительность карьеры выше. Так, у Степана вероятность сыграть больше 11 сезонов — 80%, в то время как у Дальбека — всего 55%. Также по кривой угрозы видно, что начиная с 13 сезона резко возрастает риск завершить карьеру на следующий сезон, причем у Дальбека он растет быстрее, чем у Степана.

Кросс-валидация


Чтобы более строго оценить качество регрессии используем встроенную в библиотеку lifelines процедуру кросс-валидации. При этом, работая с цензурированными данными, мы не можем использовать в качестве метрики качества среднеквадратическую ошибку и подобные критерии, поэтому в библиотеки используется concordance index или индекс согласия, который является обобщением AUC-метрики. Проведем 5-шаговую кросс-валидацию:

from lifelines.utils import k_fold_cross_validation

score = k_fold_cross_validation(aaf, X, 'L', event_col='O', k=5)
print (np.mean(score))
print (np.std(score))

Out:0.764216775131
0.0269169670161

Средняя точность по всем итерациям равна 76,4% с отклонением в 2,7%, что говорит о достаточно неплохом качестве алгоритма.

Ссылки


  1. Документация библиотеки lifelines
  2. J.Klein, M.Moeschberger. Survival Analysis. Techniques for censored and truncated data — книга с множеством примеров и датасетов, доступ к которым можно получить через пакет KMsurv в R.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/333628/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1055 1054 [1053] 1052 1051 ..
.. 1 Календарь