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

Поиск сообщений в 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 ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

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

[recovery mode] Вопросы и тестовые задания на позицию Junior PHP Developer

Воскресенье, 25 Июня 2017 г. 16:52 + в цитатник
image
Привет всем!
В последнее время побывал на многих собеседованиях, позиционируюсь я как Junior PHP Developer. До этого я нереально боялся всех собеседований, боялся завалить их, пытался готовиться к ним, гуглил примерно такого рода статьи, которую пишу сейчас (и находил не прямо так много информации). Здесь я хотел бы оставить список всех возможных вопросов, которые Вам могут задать на собеседовании с техническим специалистом, которые задавали мне в различных компаниях, а так же тестовые задания которые мне приходилось выполнять, чтобы добраться до технических собеседований. Помимо PHP сюда вставлю парочку вопросов по MySQL, конечно же. Поехали!

И так, в тех компаниях куда я позиционировался, самые частовстречающие вопросы:
  • Суперглобальные массивы PHP? Какие знаете? Зачем нужны, опишите их
  • Опишите язык PHP. Интерпретируемый? А как это? Чем отличается от компилируемого?
  • Что такое POST и GET? Отличия? Что знаешь помимо POST и GET?
  • Что означает двойное двоеточие в PHP — "::" ?
  • Что такое абстрактный класс? Отличия абстрактного класса от интерфейса?
  • Поддерживает ли PHP множественное наследование? Как его можно имитировать?
  • Расскажите о трейтах
  • Может ли быть класс абстрактным не имея абстрактных методов? (Имеется ввиду, не будет ли ошибки, если поставить классу abstract но не объявить ни одного абстрактного метода)
  • Что такое статический метод?
  • Расскажи про магические методы в PHP, какие знаешь
  • Чем отличается запись типа:
    $obj = $obj2;
    от
    $obj = clone $obj2
  • Что такое протокол? Протоколом какого уровня является HTTP? Возможно, чтобы одно устройство было и клиентом и сервером?
  • Расскажите об отличиях между == и ===
  • Какая типизация в PHP?
  • Знаешь что-то о замыканиях?
  • На каких трех принципах базируется ООП? Расскажи о них, а также приведи примеры
  • Я ввожу адрес любой адрес сайта, расскажи, что происходит внутри? Как мы попадаем на сайт?
  • Какой файл откроется по стандарту на сайте, index.html или index.php, и от чего это зависит?
  • Ок, допустим я хочу, чтобы мой скрипт работал долго. Где я могу поменять эти параметры?
    (Я отвечал, что в самом PHP прописать ini_set('max_execution_time', 0); а также в конфигурационном файле php.ini, на что спрашивают дальше — а еще? на уровне сервера как-то можно?)
  • Вот у тебя есть допустим два твоих сайта. Как можно сделать так, чтобы пользователи зарегистрированные на одном твоём сайте, могли попадать на другой твой сайт с этим же логином и паролем?
  • Почему выбрали именно PHP?
  • Как отключить вывод ошибок в PHP? Какие бывают ошибки?
  • Расскажи об отличиях между PHP 5.3, 5.4 и 5.5
  • Сколько типов данных PHP? Перечисли все типы данных
  • Отличия между require() и include()
  • Какие знаете встроенные функции по работе с массивами? А со строками?
  • Как получить первый элемент массива?
  • Расскажи о сессиях и куках. Где хранятся сессии? А можно их хранить в БД?
  • Вопрос с заковыркой: Как создать экземпляр абстрактного класса?
  • Как удалить объект/переменную вручную?
  • Как удалить файл с сервера?
  • Какие движки MySQL знаете? Отличия?
  • На какие параметры нужно ставить INDEX в таблице MySQL?
  • Можно ли повесить индекс UNIQUE на два поля?
  • Какие знаете индексы БД? Опишите каждый из них
  • Вот вам массив (дают ноутбук), отсортируйте без встроенных PHP функций вручную его
  • Какой командой в GIT можно загрузить Ваш код?
  • Если позиционируетесь на должность как разработчик на фреймворке, попросят сравнить Ваш фреймворк с другими популярными. Допустим, почему выбрали Laravel, а не Yii2?


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

  • Стартовая страница сайта должна представлять собой список всех публикаций пользователя во фрейме с полосой прокрутки. Публикации должны быть представлены не полным текстом, а заголовок и 300 символов с начала поста. В конце публикации должна быть ссылка на страницу с полным текстом Read more
  • На странице просмотра публикации необходимо отобразить блок поста, название и полный текст. Под ним находится блок комментариев. Комментарии добавляются с помощью ajax.
  • Валидацию полей необходимо провести и со стороны сервера и со стороны браузера.
  • Страница добавления/редактирования поста. При добавлении и редактировании использовать WYSIWYG редактор
  • На главной странице записи должны быть отсортированы в порядке LIFO
  • При выполнении задания использовать PHP 5.3+ и jQuery 1.7+


Тестовое задание второй компании:
Создайте на *название вашего фреймворка* доску объявлений.
Должен присутствовать функционал регистрации и авторизации пользователей. После того как пользователь зарегистрировался и авторизовался он может заполнить информацию о себе и загрузить свое фото, добавить объявления с фото. К профилю пользователя можно добавлять текстовые комментарии и ставить оценку (рейтинг от 1 до 5). На главной странице сайта отображаются 20 последних добавленных объявлений и присутствует пагинация.
Готовый функционал нужно выложить на Git и прислать нам.


Тестовое задание третьей компании:
Задание:

1. Создать справочник журналов, с возможностью CRUD. У каждого журнала должны быть:

1.1 Название. (Обязательное поле)
1.2 Короткое описание. (Необязательное поле)
1.3 Картинка. (jpg или png, не больше 2 Мб, должна сохраняться в отдельную папку и иметь уникальное имя файла)
1.4 Авторы (Обязательное поле, может быть несколько авторов у одного журнала, должна быть возможность выбирать из списка авторов, который создается отдельно).
1.5 Дата выпуска журнала.

2. Список авторов создается отдельно. Также должна быть возможность добавления, удаления и редактирования. У каждого автора должны быть:
2.1 Фамилия (Обязательное поле, не короче 3 символов)
2.2 Имя (Обязательное, не пустое)
2.3 Отчество (Необязательное)

3. На выходе получаем:
3.1 Просмотр отдельно страниц журналов и авторов.
3.2 На странице авторов:
3.2.1 Должна быть возможность увидеть список всех журналов определенного автора.
3.2.2 Сделать сортировку авторов по фамилии
3.3.3 Сделать сотрировку таблицы по дате выпуска журнала.
3.4 Работа с каждой отдельной страницей должна происходить без её перезагрузки с использованием jQuery (или Angularjs).
3.5 Сделать пагинацию по журналам и авторам
4. Рекомендуемое время выполнения задания — 4 часа.


Всем удачных подготовок и прохождения собеседований!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331610/


Метки:  

Дайджест интересных материалов для мобильного разработчика #209 (19 июня — 25 июня)

Воскресенье, 25 Июня 2017 г. 15:48 + в цитатник
В новом дайджесте плохой бэкенд, плохие магазины Apple и Google, плохие креативы, смерть гамбургера. Зато хорошие SDK, качественный код, самодостаточные контроллы, будущее еды и сельского хозяйства. Добро пожаловать!



Как написать максимально хреновый бэкенд для мобильного приложения

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

Google и Apple против инди-разработчиков

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

Усатый стрелок из двадцати трёх полигонов

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

Дайджест доступен и в виде рассылки. Подписаться вы можете тут.

iOS


Android


Windows


Разработка


Аналитика, маркетинг и монетизация


Устройства, IoT, AI



< Предыдущий дайджест. Если у вас есть другие интересные материалы или вы нашли ошибку — пришлите, пожалуйста, в почту.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331606/


[Из песочницы] Два в одном: как пользоваться Vim и Nano?

Воскресенье, 25 Июня 2017 г. 15:26 + в цитатник

Вступительное слово


Зачем нужна ещё одна, 1001я публикация на эту тему? Статей про vim и nano написано огромное количество, но как правило они касаются только одного из редакторов, либо представляют из себя общий обзор. Чтобы в одной были сжато описаны оба редактора, но при этом без углубления в дебри приведены все основные клавиши управления для полноценной работы, я не не нашёл. Поэтому, почитав найденные материалы, я начал их конспектировать, так и родилась эта статья.


Любой текстовый редактор можно освоить «методом тыка». Но только не vim.
Чем nano лучше vim?
Из nano можно выйти без reset'а! (с) Интернет


Изначально я не планировал писать об обоих редакторах, а хотел сделать краткую справку только по nano, но в процессе сбора информации накопились данные и по vim тоже. Тут я обнаружил, что Vim не так уж и страшен, если знать команды и концепцию его использования.

Что это и зачем нужно?


Когда недоступна графическая среда, например при работе по ssh, настройке системы до загрузки xserver, либо просто из любви к извращениям красноголазию начинаешь чувствовать себя ущербным инвалидом в консольном текстовом редакторе, клавиш управления которым не знаешь от слова совсем. Даже навигация по файлу и копипаста начинают представлять собой почти непреодолимую преграду. Просто потому, что в консоли не работают многие привычные комбинации клавиш, будучи задействованными под другие нужды ещё во время дискет 5.25" на 320Кб.

Я долгое время использовал редактор от Midnight Commander mcedit, немного пользовался nano, и совсем не пользовался vim. Вызубрить все эти комбинации за гранью возможного — даже потратив на это время, через какой-то период простоя они успешно забываются. До того, как я взялся за написание этой статьи, я не мог заставить себя осилить даже азы vim, он пугает уже на стадии запуска, «пытаешься из него сразу же выйти, и не можешь!» Это как вы сейчас зайдёте в интерпретатор ZX Spectrum BASIC48, то не зная азов даже простейшей программы PRINT «Hello World!» не напишите. Я не переставал задавать себе вопрос: «Интересно, и за что гуру vim так любят? Зачем тратить кучу времени на переобучение, и главное как не растерять навыки, для каких целей его постоянно использовать, этот vim?»

Ниже я опишу самые необходимые комбинации клавиш и приёмы работы в обоих редакторах.

Nano


После комбинации Alt-6, как команды «скопировать» я даже и не знаю что сказать
Наверное автор Nano был пианистом
© из хабра-комментариев

Основой мне послужила статья на хабре: "Nano: И всё-таки его придётся выучить", а также все комментарии к ней. Они не просто её дополнили, но и заставили посмотреть в сторону vim.

РосNano-концепция:

В терминах nano текст, отделённый пустыми строками называется «параграф». Все команды отдаются нажатием комбинаций клавиш — функциональными кнопками или комбинацией Ctrl+буква или Alt+буква. Если в каком-либо случае комбинация с Ctrl или Alt забронирована системой, следует использовать дублирующие комбинации с Esc.

Одинарное нажатие на Esc эмулирует клавишу Alt. Двойное Esc Esc эмулирует клавишу Ctrl. То есть вместо Alt+X можно набрать Esc X, вместо Ctrl+K можно набрать поочерёдно Esc Esc K.

Самое важное:

Alt+U (Esc U) — Undo, отмена последнего действия.
Alt+E (Esc E) — Redo, повтор отменённого действия.

F1 (Ctlr+G) — Показать справкуF2 — Выход с сохранением файлаCtrl+X — Вернуться к редактированию из справки, и прочих диалоговых окон, выход из программы.

Копирование и вставка текста:

Alt-A (Ctrl-6) — установить\снять маркер начала выделения текста (либо просто стрелки с shift)
Alt+6 (Esc 6) — копировать выделение текст в буфер обмена. Комбинация alt+6 у меня занята и не заработала.
Ctrl+K (или F9) — вырезать выделенный фрагмент в буфер обмена, при отсутствии выделения удалить строку.
Ctrl+U (или F10) — вставить фрагмент из буфера обмена
Ctrl+Shift+V — вставить из GUI буфера обмена
Ins — Вставка текста из другого файла, либо вывода команды.

Поиск и замена:

Ctrl+W — Поиск
Alt+W — Повторный поиск
Ctrl+\ — Замена. Помнит последний поиск как изменяемое по умолчанию.

Навигация:

Alt+\ (Ctrl+Home) — В начало файла
Alt+/ (Ctrl+End) — В конец файла
Ctrl+Y (PgUp)— На страницу вверх
Ctrl+V (PgDn) — На страницу вниз
Alt+S — Переключение между плавной прокруткой страниц и фиксированной

Дублирование курсорных клавиш, то чем так гордится vim, только врастапырку:
Ctrl+B — Влево на символ
Ctrl+F — Вправо на символ
Ctrl+N — Вниз на символ
Ctrl+P — Вверх на символ

Ctrl+Space — На слово вперёд, можно просто Ctrl+курсор. (курсор встаёт в начале слова)
Alt+Space (ESC Space) — На слово назад.
(у меня эта комбинация вызывает строку быстрого запуска KRunner в KDE, поэтому использую ESC Space)

Alt+- — Прокрутка вверх не меняя позиции курсора
Alt+= — Прокрутка вниз не меняя позиции курсора
Alt+7 (Esc 7) — К предыдущему блоку текста, либо просто Ctrl+курсор
Alt+8 (Esc 8) — К следующему блоку текста.
Alt+0 (Esc 0) — К следующему параграфу (до первой пустой строки вниз).
Alt+9 (Esc 9) — К предыдущему параграфу (до первой пустой строки вверх).

Alt+G — Переход на строку с указанным номером.

Alt+< — Переход к предыдущему открытому файлу (Если nano запущен с несколькими файлами)

Alt+> — Переход к следующему открытому файлу.

Ещё пишут про команду Alt+F, которая не то включает такую возможность, не то позволяет зачем-то иметь отдельные буфера обмена на каждый файл, но как она работает я не понял.

Редактирование текста:

Ctrl+D (Del) — стереть символ под курсором
Ctrl+H (BSp) — Стереть символ слева от курсора
Ctrl+M (Enter) — Вставка пустой строки.
Ctrl+I (Tab) — Вставка табуляции.
Alt+Shift+{ — Уменьшить отступ параграфа удалением табуляции или пробеловAlt+Shift+} — Увеличить отступ параграфа, вставкой табуляции. Если уже был отступ пробелами, добавит к нему табуляцию и подсветит красным.
Ctrl+] — Автодополнение слов, например команд в программе. Программисты оценят!
Alt+3 (Esc 3) — За(рас)комментировать символом # текущую строку, выделенный параграф.
Alt+J (F4) — Justify, выровнять текущий абзац

Разное:

Esc Esc 220 ­— Ввод символов по десятичному коду. Например 220 — ввод "U с умляутом.
Alt+V — Ввод ASCII последовательности комбинации или клавиши.
Alt+T — Trunkate. Стирает всё от курсора и до конца файла.
Ctrl+C (F11) — Разово выводит информацию по текущей строке, колонке, номеру символа.
Alt+D — Подсчитать количество слов, строк, символов в файле
Ctrl+L — Обновить экран

Настройки nano:

Alt+Shift+4 — Мягкий перенос длинных строк разрешить/запретитьAlt+K — переключает действие по Ctrl+K между удалением по умолчанию всей строки и удалением от курсора до конца строки.
Alt+Q — Переключиться с табуляций на ввод пробелов.
Alt+L — Включить и выключить жёсткий перенос строк.Alt+C — Переключает постоянный вывод инфо по строке по Ctrl+C
Alt+X — Показать\скрыть строку подсказки внизу экрана.
Alt+Shift+3 — Показывать номера строк.
Alt+P — Показывать пробелы символом "·"
Alt+Y — Показывать подсветку синтаксиса.
Alt+H (Esc H) — Включить умную клавишу Home (переводит курсор не в 1ю колонку, а на 1й символ строки)
Alt+I (Esc I) — Включить автоотступы (перевод строки на новую с таким же отступом, как у предыдущей)
Alt+B — Переключиться на создание резервных копий и обратно
Alt+M — Включить «поддержку мыши». лучше не использовать, ерунда!

Говорят, что nano по умолчанию не поддерживал отмену действий (undo). У меня в Manjaro всё поддерживает, но на всякий случай решение — запускать nano с ключом -u: nano -u filename.
Для включение подсветки синтаксиса в nano следует раскомментировать директивы include в файле /etc/nanorc.

На этом всё.

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

Раз | два | три



Vim, немного крипоты


«у vi есть два режима: бибикать и всё портить»
©
Холиварненько

Изначально Vim разрабатывался для аппаратного терминала ADM-3A, он слева на фото.

Приведу несколько цитат перед статьёй, которые как бы намекают читателю: "— Ужаснись и беги. Слышишь смертный? Беги! И не говори потом, что тебя не предупреждали..."

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

© https://ru.wikibooks.org/wiki/Vim
* * *

Ну, смотрите, человек запускает vim, курсором доползает до нужного места, стирает лишнее, начинает печатать, в какой-то момент доходит до буквы 'i'/'c' и т.д., после чего остаток введённого появляется на экране не там, где человек хотел. Он нажимает esc, q, у него начинается какая-то фигня, он нажимает несколько раз esc, esc, потом пишет :q, потом нажимает три раза Esc пишет :Q!, потом нажимает ещё пару раз Esc, пишет :q!, потом снова vim и уже редактировать. (q) Пример ада с vim

* * *
У меня кстати была обратная ситуация, когда я подошел к сотруднику подсказать что подправить в конфиге и в фаровском редакторе напечатал ему полстрочки букв j, прежде чем понял, что что-то не так, курсор двигается вправо, а не вниз и ещё и мусор какой-то появляется. (q) Байка о силе привычки

* * *
Судя по тому, что я сейчас прочел, пользователи… нет, скорее «активисты» vi — не просто придурки, а отборные мазохисты. Не знаю, может кто-то в этой статье видит преимущества vi, я же вижу убийственный гемморой, переусложняющий простой ввод кода до уровня секвенирования ДНК. (й) +15 голосов в хаброкомментах

* * *
Cейчас средства разделились на редакторы (набрать текст) и IDE (работать с проектом). Вим получается посередине, как утка. Но это уже не нужно. Если я хочу набрать текст — мне не нужен редактор который осваивать дольше чем IDE, мне нужен ee/nano. А если мне нужна работа с проектом — мне нужна нормальная IDE с большими возможностями и низким порогом вхождения, а не школа белого нидзя по освоению навороченного текстового редактора. (q) убийственный хаброкоммент

Тем не менее, мы попытаемся!


Следует запомнить, что Vim имеет два режима: командный и модальный. В первом вводятся команды, например просто введя двоеточие : можно стрелками вверх-вниз перебирать историю ранее введённых команд. Введя начало команды, по Ctrl+d можно увидеть список похожих команд, а по Tab дополнить сокращение до полного названия команды. Все команды, начинающиеся с : должны завершаться нажатием ENTER, остальные исполняются моментально. Во втором режиме правится текст. Вход в модальный режим по a, i. После правки фрагмента текста, всегда нажимайте Esc, чтобы вернуться в командный режим, и у вас никогда не возникнет вопроса, в каком режиме сейчас редактор.

Поясняющая цитата о вводе команд: Команды в vim задуманы комбинируемыми: 'd' значит «удалить», 'e' значит «переместить курсор в конец слова», поэтому 'de' завершенная команда, означающая «удалить все от текущего положения курсора до следующего конца слова» Команды регистрозависимы!

Например войдя в модальный режим (редактирование текста) по i, введя «True», и выйдя в командный по Esc, вы можете перемещаться по файлу со всей мощью команд навигации и нажимать '.' везде, где нужно повторить команду, то есть вставить слово «True» в текущую позицию курсора в данном случае. Учитывается весь введённый фрагмент с перемещениями курсора и стиранием символов. Так можно по точке автоматизировать ввод форм в одинаковые блоки.

Команды Vim:


Возможно, это только для меня стало внезапным открытием, но вся справка на русском языке по клавишам vim доступна внутри терминала в виде уроков по команде (в том числе по ней я делал этот конспект):

vimtutor ru



Перемещение:

hjkl — дублирование курсорных клавиш влево, вниз, вверх, вправо.
^ или 0 (нуль) — В начало строки
$ — В конец строки
e — В конец текущего, затем следующего, слова.w — К началу следующего слова.% — Перемещение в строке между скобками: (){}[]

Ввод числа перед оператором перемещения приведёт к его повторению заданное количество раз.
2w — Перемещение курсора вперёд к началу второго слова.
3e — Перемещение курсора вперёд к концу третьего слова.

Комбинирование команд упрощает навигацию:j$ —Вниз, в конец следующей строки.

Shift+g — Перейти в конец файла. Если перед этим ввести номер строки, то переход к ней.
507 Shift+g — Например вот так будет переход не в конец, а к строке номер 507
:45Enter — Переход к строке номер 45
mk — Пометить текущую позицию как k
‘k — Перейти к метке k

H, M, L — перемещение курсора на верхнюю, среднюю и нижнюю линию экрана.
zt, zz, zb — перемещение курсора вместе с текстом на верхнюю, среднюю и нижнюю линию экрана.
* — Переместить курсор на следующее слово, такое же, как под курсором
# — Переместить курсор на ПРЕДЫДУЩЕЕ слово, такое же, как под курсором

Вход в модальный режим редактирования текста:

i
— Insert, переход в режим вставки текста левее курсора
a — Append, переход в режим вставки текста правее курсора
A — App_end, переход в режим вставки текста в конец строки
R — Replace, переход в режим замены текста.
o — создать пустую строку под текущей и перейти к её редактированию.
O — создать пустую строку НАД текущей и перейти к её редактированию.

Редактирование:

u — UNDO, отмена последнего действия
U — UNDO, отмена изменений только в последней строке
Ctrl+r — Redo, повторить отменённое действие текущего модального сеанса редактирования.

Многие команды, изменяющие текст, состоят из оператора и объекта. Например ниже оператор удаления d комбинируется с объектом направления удаления:

dw — удалить от курсора до конца слова, включая последующий пробел.
de — удалить от курсора до конца слова, НЕ включая последующий пробел.
d$ — удалить от курсора до конца строки.
d^ — удалить от курсора до начала строки.
ce — удаляет слово и переводит в режим вставки.
c$ — удалить от курсора до конца строки и перейти в режим вставки (редактирования текста).

Ввод числа перед оператором приведёт к его повторению заданное количество раз.

d2w — Удаление двух слов подряд
x — Del, стереть символ под курсором.
dd — Удаление строки
D — Очистка строки от символов
r — замена символа под курсором следующим за r
После нажатия r+символ возвращается в командный режим, что делает неудобным использование для правки русского текста без адаптации командного режима vim к русской раскладке клавиатуры.

diw — удалить слово
ciw — удалить слово
c% — Вырезание выражения в скобках. (% этоShift+5)
ce WORD Esc — вводить на первом символе какого-либо слова, чтобы заменить его на WORD. После по точке так можно будет сделать с любым.

d$ или D (Shift+d) — удаляет строку после курсора
db — удалить слово ДО курсора
di( — удаляет то что в скобочках.
di" — то, что в кавычках
dw — Удаление участка текста от курсора до конца слова.
d5d — удалить 5 строк от курсора вниз
S — Удалить все до конца строки

Ctrl+a — Увеличить число под курсором на единицу
Ctrl+x — Уменьшить число под курсором на единицу
Vu — Перевести строку в нижний регистр
VU — Перевести строку в верхний регистр
g~~ — Инвертировать регистр

Поиск:

При достижении конца файла поиск будет продолжен с начала.

/ — Поиск текста по образцу вперёд
? — Поиск текста по образцу назад
n — Следующее найденное совпадение
NПредыдущее найденное совпадение
Ctrl+o — вернуться туда, откуда начали поиск
Ctrl+i — вернуться обратно к найденному после ctrl+o
% — Поиск парных скобок: (), [] или {}

Поиск и замена:

:s/было/стало — Поиск и замена только первого найденного совпадения в текущей строке
:s/было/стало/g — Поиск и замена всех найденных в текущей строке совпадений
:30,90s/было/стало/gc — поиск и замена с 30 по 90 строку по всей их длине, с подтверждением
:%s/было/стало/gc — поиск и замена во всём файле с подтверждением.

То есть :s/ — текущая строка, :30,90s/ — диапазон, :%s/ — весь файл.
g — во всей строке, c — с подтверждением, иначе только первое совпадение в строке, и без подтверждений.

Можно не писать каждый раз команду заново, достаточно ввести : двоеточие и стрелками курсора вверх-вниз перебирать ранее введённые варианты.

Выделение:

v — визуальный выбор, выделение фрагмента текста для операции над ним.
d — удаление выделенного фрагмента
y — копирование выделенного фрагмента
yw — копирование одного слова от позиции курсора до конца слова вместе с пробелом.
p — вставка скопированного фрагмента
vgU — выделить в визуальном режиме фрагмент и перевести его в верхний регистр.
vg~ — Выделить в визуальном режиме фрагмент и ИНВЕРТИРОВАТЬ его регистр.

Файловые операции:

:w — сохранить изменения в текущий файл
:w file.txt — сохранить в файл file.txt
:w! file.txt — сохранить и перезаписать file.txt, если существует.
:w~/file.txt — сохранить в файл file.txt в домашней папке
:e — Переоткрыть текущий файл.
:e file.txt — открыть файл file.txt.
:ene — Создать новый файл.
:ene file.txt — Создать файл file.txt.

Для сохранения фрагмента, выделить его по клавише v и сохранить с указанием имени как выше. Командная строка будет иметь вид подобный :'<,'>w file.txt
:r file.txt — вставить содержимое файла file.txt в позицию ниже курсора
:r!ls — вставить вывод внешней команды ls.
:! ls — выполнить любую внешнюю команду, в данном случае ls

Настройки:

:set ic — IgnoreCase, игнорирование регистра при поиске или замене
:set noic — вернуть регистрозависимый поиск или замену
:set hls — Hlsearch, подсвечивать найденное
:set nohls — НЕ подсвечивать найденное
:set is — Incsearch, инкрементный поиск — отображение частичных совпадений при поиске
:set nois — Отключить инкрементальный поиск, искать по enter.

Разное:

Ctrl+g — Показывает строку статуса с именем открытого файла, номером текущей строки.
Esc или Ctrl+C или Ctrl+[ — выход в командный режим.:e Enter — Выход. Именно с ДВОЕТОЧИЕМ! Я сам долго не мог выйти. Решил, что ':' это часть предложения, а не часть комбинации, которую нужно набрать и долго жал 'q'.
:e! Enter или :x или :wq — Выход без сохранения
ZQ ­— Моментальный выход без сохранения и подтверждения.

Настройки по умолчанию можно записать в конфигурационный файл ~/.vimrc
Например без этого я не начну писать в vim ни одной программы на Python:
set tabstop=4 # Длина табуляции
set expandtab # Замена табуляции пробелами

Окна:

Ctrl+W Ctrl+W — Переход между окнами, например при вызове справки по :help между ней и текстом.
:tab ball — Поместить все открытые файлы во вкладки
:tabnew — Создать новую вкладку
gt — Перейти на следующую вкладку

Полезные хитрости:

:%s#>[^<]\+>##g — Очистить текст от HTML-тегов
:%s/^\(.*\)\n\1$/\1/ — Удалить строки, повторяющиеся дважды

Приложение:


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

Lesson 1Lesson 2Lesson 3
Lesson 4Lesson 5Lesson 6
Lesson 7

Решение проблем:


Простое решение проблемы командного режима в русской раскладке, положить в файл ~/.vimrc с содержимым в одну строку:

set langmap=ФИСВУАПРШОЛДЬТЩЗЙКЫЕГМЦЧНЯЖ;
ABCDEFGHIJKLMNOPQRSTUVWXYZ:, фисвуапршолдьтщзйкыегмцчня;abcdefghijklmnopqrstuvwxyz

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

* * *

Вот эта пара строк сильно экономит время на копание в .vimrc
" ## Edit .vimrc ##
map ,v :vsp $MYVIMRC
map ,V :source $MYVIMRC
Первая маппит на ,v открытие .vimrc в вертикальном окошке, а вторая просит VIM пересчитать файл.

* * *

О разнице, какой клавишей выходить в командный режим: Ctlr-C просто переходит в normal mode. Если на выход из insert mode какими нибудь плагинами навешаны какие-нибудь хуки, то при нажатии Ctrl-C vim их проигнорирует, а при нажатии Esc или Ctrl-[ запустит. (й)

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

  1. Статьи на Хабре
  2. Почему, ну почему, эти #?@! придурки используют vi? (отличная статья! А комменты просто жгут)
  3. Vim и кириллица: парочка приёмов
  4. Аж целую книгу запилили по этому vim, я удивляюсь. Нашёл на файлопомойке тут
  5. Прямая ссылка на книгу: O'Reilly — Learning the vi and Vim Editors 7th Edition (перевод).pdf
  6. Vimtouch под Android. Если готовый в googleplay
  7. 100 команд vim, которые должен знать каждый
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331600/


Метки:  

Редактор уровней для три в ряд

Воскресенье, 25 Июня 2017 г. 15:24 + в цитатник


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

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



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

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

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

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

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

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

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

Выключите звуки и музыку по-умолчанию! Я серьезно, об этом всегда забывают.

Рассмотрим принципиальную схему устройства редактора.



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

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

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

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

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

Базовый редактор включает в себя несколько основных разделов:

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

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

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

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

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



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

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

Список уровней. Это структура вашего проекта, поэтому этот раздел должен позволять легко ее менять или вносить коррективы. Например, если в игре уже 500 с лишним уровней, держать их все одним списком будет неудобно. Необходимо сразу предусматривать возможность структурирования уровней по группам с удобной нумерацией. В играх, где существует несколько возможных миров со своими наборами уровней, так же необходимо создавать свой раздел. Рекомендация здесь будет только одна — переносить уровни из одного в раздела в другой необходимо за минимальное количество телодвижений. Drag`n`Drop будет одним из неплохих решений, но влечет за собой опасность случайного переноса контента между разделами. Делать сложный менеджмент с помощью многоуровневых меню более безопасный вариант, но однозначно не самый удобный;

Контроль версий. Возможность откатить изменения спасла не одну шкуру программиста, но в деле дизайна уровней это очень мощный инструмент для создания вариантов уровней, которые впоследствии можно использовать в игре. Как правило, очень помогает для уровней, которые являются критичными для проекта — обучение механикам, стартовые уровни игры и т.д. Возможность посмотреть на предыдущие варианты и, в случае чего, откатить изменения, бесценна. Основной проблемой будет создание удобного формата переноса версий между уровнями и корректное их именование. Вспомнить, что означает название версии “Copy of level New +” явно введет вас в ступор через месяц после отправки его на релиз, а называть каждый уровень вручную тоже не самое лучшее решение. Поэтому сразу утвердите наглядный способ идентификации версий. Например, номер по порядку, дата релиза, игровой режим и количество игровых ходов;

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

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

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



Элементы управления контентом, которые необходимы конкретно дизайнеру:

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

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

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

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

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

Обзора какого функционала вам не хватило в статье и в реальных проектах? Если у вас есть опыт разработки подобных систем, на какие вещи стоит обратить внимание и чего однозначно не стоит делать?
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331596/


Метки:  

[Из песочницы] Использование MapXtreme .Net

Воскресенье, 25 Июня 2017 г. 13:21 + в цитатник
Всем привет! Хочу с Вами поделиться своим опытом работы с таким SDK как MapXtreme .Net от фирмы Pitney Bowes. Как сказано у них на сайте:
MapInfo MapXtreme for .Net — это комплект разработчика программного обеспечения ГИС в среде Microsoft .Net, позволяющий встраивать картографические и ГИС функции в бизнес-приложения.

Установка


Первым шагом была настройка машины к работе. Устанавливаем MS VisualStudio 2015 с сайта разработчика (на MSVS2010 работает, ниже версии не пробовал).

Скачиваем пакет установки с сайта разработчика. На портале для скачивания доступна триальная версия 7.1 на 60 дней (версия 8 доступна для покупки, разница между ними ощутимая, но затрагивает архитектуру MapXtreme. Основные операции остались неизменными).

Так же необходимо скачать карту мира, иначе зачем нам вообще нужна ГИС, не так ли? Для MapXtreme подходит карта ADC WorldMap Version 7.1.

Ставить именно в таком порядке, так как MapXtreme требует .Net.

Документация


Библиотека очень большая с большим количеством функций, но чтобы добраться до них нужна документация, а с ней у MapXtreme проблемы. После установки в каталоге с SDK можно найти
MapXtreme Developers Reference
MapXtreme Version 8.0 Developer Guide

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

Кстати, пару слов о ней родимой:

image

Было 3-4 обращения к ним за помощью, и все разы мне говорили:
«Мы передали ваш запрос нашим разработчикам»
«Пришлите ваш исходный код нам, мы не понимаем вашу проблему» и т.д.

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

Разработка


Если вы установили все верно, то VisualStudio при создание нового проекта у вас будет доступен тип проекта MapXtreme (у меня версия 8.0, т.к. мы купили лицензию).

image

Выбираем Windows Form App. Если в ToolBox не появились новые элементы открываем пункт меню
Tools -> Choose ToolBox Items...

И в появившемся окне выбираем нужные нам инструменты:

image

Итак, мы все установили, настроили и готовы писать код MapXtreme предоставляет уже готовые компоненты для создания приложения. Вы можете сразу сделать панель инструментов MapXteme добавив на главную форму компонент ToolStrip, в котором хранятся необходимые нам кнопки.

image

Чтобы использовать выбранные нами кнопки свойству MapControl пропишем имя нашего элемента типа MapControl. У меня называется mapControl1, у вас оно может отличаться. Либо в конструкторе формы написать

private void Form1_Load(object sender, EventArgs e)
{
    addrectangleToolStripButton1.MapControl = mapControl1;
    ....
}

Если все сделано правильно, то у вас должно получится вот так:

image

«Я сделал все правильно, выбрал инструмент для рисования, но у меня ничего не выходит! Почему?»

Чтобы решить данную проблему необходимо выбрать слой на котором мы будем рисовать и сделать его изменяемым. По умолчанию все слои на mapControl неизменяемые.

Мы можем решить нашу проблему средствами MapXtreme нажав кнопку «Layer Control» и поставив галочки в нужных местах.

image

Или сделать это прямо в коде:

var layerTemp = mapControl1.Map.Layers["wldcty25"];            
LayerHelper.SetInsertable(layerTemp, true);//дает возможность рисовать на слое инструментами
LayerHelper.SetEditable(layerTemp, true);  //делает наш слой изменяемым

Отмечу, что рисовать можно только на «верхнем» слое, то есть чтобы сейчас нарисовать объект мы должны наш слой «wldcty25» переместить вверх:

image

Или написать в коде:

mapControl1.Map.Layers.Move(lastPosition, nextPosition);

В результате нарисован наш первый объект:

image

Как я говорил ранее любая настройка может быть задана через интерфейс. Однако, заказчик чаще всего привередливый и ему нужны более гибкие/доступные элементы настройки поэтому далее я не буду рассказывать о том как настроить что либо через компоненты MapXtreme. Я покажу как это сделать через код.

Начать хочу со стиля. Основным классом отвечающим за оформление выступает CompositeStyle. В MapXtreme мы можем задавать стиль на слой и на объект. Отмечу, что стиль слоя перекрывает стиль объекта.

SimpleLineStyle border;//стиль линейного объекта
border= new SimpleLineStyle(<ширина линии в единицах MapXtreme>, <код стиля линии>,  <цвет линии>)
SimpleInterior interior;//стиль внутренней области объекта
interior = new SimpleInterior(<код стиля>, <цвет переднего плана>,<цвет фона>)
AreaStyle polygon;//стиль который используя предыдущие два задает стиль для площадного объекта
polygon = new AreaStyle(border, interior);

SimpleLineStyle line;//стиль линейного объекта
line = new SimpleLineStyle(<ширина линии в единицах MapXtreme>, <код стиля линии>, <цвет линии>)

BasePointStyle point;//стиль точечного объекта
point = new SimpleVectorPointStyle(<код стиля>, <цвет точки>, <размер точки>)

CompositeStyle compStyle;//стиль применяемый к слою
compStyle = CompositeStyle(polygon, line, null, point);

Применим полученный стиль к слою:

FeatureOverrideStyleModifier fosm;
fosm = new FeatureOverrideStyleModifier(parStyleName, parStyleAlias, compStyle);
myLayer.Modifiers.Append(featureOverrideStyleModifier);

Применим полученный стиль к объекту:

Feature feature = null;
feature = new Feature(<класс описывающий наш объект>, compStyle);

Создание объекта


Для создания мультилинии или мультиполигона сначала создадим исходные данные:

//задаем первую часть мультиобъекта
DPoint[] pts1 = new DPoint[5];
pts1[0] = new DPoint(-20, 10);//произвольные координаты точек взятые для примера
pts1[1] = new DPoint(10, 15);
pts1[2] = new DPoint(15, -10);
pts1[3] = new DPoint(-10, -10);
pts1[4] = new DPoint(-20, 10);
//задаем вторую часть мультиобъекта
DPoint[] pts2 = new DPoint[5];
pts2[0] = new DPoint(-40, 50);
pts2[1] = new DPoint(60, 45);
pts2[2] = new DPoint(65, -40);
pts2[3] = new DPoint(25, 20);
pts2[4] = new DPoint(-40, 50);
//LineString создаем из массива элементов DPoint
LineString lineString1 = new LineString(coordSys, pts1);
LineString lineString2 = new LineString(coordSys, pts2);

Используя код выше создадим:

— мультиполигон

//Ring в заданной системе координат coordSys создается через LineString 
Ring ring1 = new Ring(coordSys, lineString1);
Ring ring2 = new Ring(coordSys, lineString2);
Ring[] rng1 = new Ring[2];
rng1[0] = ring2;
rng1[1] = ring1;
//MultiPolygon состоит из массива Ring
MultiPolygon multiPol = new MultiPolygon(coordSys, rng1);

— мультилинию

//Curve в заданной системе координат coordSys создается через LineString 
Curve curve4 = new Curve(coordSys, lineString1);
Curve curve5 = new Curve(coordSys, lineString2);
Curve[] crv = new Curve[2];
crv[0] = curve5;
crv[1] = curve4;
//MultiCurve состоит из массива Curve 
MultiCurve mc = new MultiCurve(coordSys, crv);

Работа с картографической проекцией


Получение:

CoordSys сoordSys= mapControl1.Map.GetDisplayCoordSys();

Создание и изменение текущей системы координат:

CoordSys cs = Session.Current.CoordSysFactory.CreateCoordSys("EPSG:3395", CodeSpace.Epsg); 
mapControl1.Map.SetDisplayCoordSys(cs);

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

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

mapControl1.Map.RasterReprojectionMethod = ReprojectionMethod.Always;    
/**
*ReprojectionMethod имеет три состояния
*        None = 0, //запрет на изменение растра
*        Always = 1,//изменять всегда
*        Optimized = 2//так и не понял зачем нужен, в моей практике он ничем не отличался от 
*        предыдущего
*/

Лично я работал только с GeoTiff. MapXtreme не работает напрямую с изображениями. Для того чтобы все было хорошо системе необходим *.tab файл, который будет содержать описание растра, систему координат, координаты углов.

Как делать *.tab для растра средствами MapXtreme я так и не нашел, интернет так же подсказки не дал и мне пришлось самому писать метод, который создавал бы этот файл:

private string CreateTabFile(List parImageCoordinate)//список координат углов
{
    NumberFormatInfo nfi = new CultureInfo("en-US", false).NumberFormat;
    var ext = m_fileName.Split(new Char[] { '.' });
    var fileExt = ext[ext.GetLength(0) - 1];
    string fileTabName = m_fileName.Substring(0, m_fileName.Length - fileExt.Length) + "TAB";
    StreamWriter sw = new StreamWriter(fileTabName);
    string header = "!table\n!version 300\n!charset WindowsCyrillic\n \n";
    sw.WriteLine(header);

    string definitionTables = "Definition Table\n  ";
    definitionTables += "File \"" + m_fileName + "\"\n  ";
    definitionTables += "Type \"RASTER\"\n  ";
    for (var i = 0; i < parImageCoordinate.Count; i++)
    {
        definitionTables += "(" + parImageCoordinate[i].Longitude.ToString("N", nfi) + "," + parImageCoordinate[i].Latitude.ToString("N", nfi) + ") ";
        definitionTables += "(" + parImageCoordinate[i].PixelColumn.ToString() + "," + parImageCoordinate[i].PixelRow.ToString() + ") ";
        definitionTables += "Label \"Точка " + (i + 1).ToString() + " \",\n  ";
     }
     definitionTables = definitionTables.Substring(0, definitionTables.Length - 4);
     definitionTables += "\n  ";
     definitionTables += "CoordSys Earth Projection 1, 104\n  ";
     definitionTables += "Units \"degree\"\n";
     sw.WriteLine(definitionTables);
            
     string metaData = "begin_metadata\n";
     metaData += "\"\\IsReadOnly\" = \"FALSE\"\n";
     metaData += "\"\\MapInfo\" = \"\"\n";

     string hash;
     using (MD5 md5Hash = MD5.Create())
     {
         hash = GetMd5Hash(md5Hash, m_fileName);
     }

      metaData += "\"\\MapInfo\\TableID\" = \"" + hash + "\"\n";
      metaData += "end_metadata\n";
      sw.WriteLine(metaData);
      sw.Close();
      return fileTabName;
}

Метод GetMd5Hash взят с MSDN. Итак, теперь наш mapControl1 готов к работе с растром.

Пока все. Вышло и объемно, наверное. Почему решил написать? Потому что не нашел ни на хабре, ни в ру/еу сегменте интернета хорошего контента по данному вопросу.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331590/


Метки:  

Дизайн города, основанный на данных. Лекция в Яндексе

Воскресенье, 25 Июня 2017 г. 13:17 + в цитатник
Под катом вы найдёте расшифровку лекции Андрея karmatsky. Он долгое время руководил службой дизайна геоинформационных сервисов Яндекса, а затем основал компанию Urbica, которая занимается анализом и обработкой городских данных. Андрей рассказывает о примерах того, как подход, ориентированный на данные, помогает улучшать городские сервисы.




Большая часть слайдов — тоже под катом.



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



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



Я хочу поделиться историей, которая меня очень вдохновляет. Она то есть из прошлого, из конца XIX века. Был такой замечательный промышленник Чарльз Бут. Если кто-то не слышал про карту бедности Лондона, то я вкратце перескажу его историю. Бут, будучи достаточно успешным предпринимателем, решил попробовать решить проблему в стране, в Англии, в конце XIX века. Что он сделал?



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

В конечном счете Бут опубликовал две карты с интервалом в 10 лет. Все данные, которые они собирали в своих блокнотах и записях, они картировали. Эта карта показывает параметр, который сейчас называется «уровень дохода населения». Ранее никто этим не занимался. Они собрали карту, в которой очень хорошо видно, что черные домики — это бедные горожане, а красные — зажиточный, богатый слой населения. И он очень наглядно, доступно показал, насколько общество расслоено, насколько они соседствуют. Вы же понимаете, что конец XIX века — это не так классно. Во-первых, это индустриализация, это эпоха паровых машин, копоть, грязь, а во-вторых — антисанитария. Соседство разных слоев населения — это и обострённая криминогенная обстановка, и вообще некоторое здоровье общества. Так что если кто-то знает и хочет детально посмотреть, «карта бедности Лондона» — одна из основополагающих, по крайней мере вдохновляющих меня историй, потому что после нее, например, в парламенте произошли ряд существенных изменений законодательства. В том числе введение пенсии по старости непосредственно приписывают череде событий, которые проистекали из этого исследования.



Занимаясь данными, я пришел к одной мысли… Это не аксиома, потому что я написал два слова и фоном запустил картинку с заказом такси, чтобы не было так скучно. Потому что это просто визуализация. Но самое важное, что когда мы занимаемся какими-то проектами с данными, у нас всегда есть два суперважных ингредиента. Чтобы что-то получилось, нужно задать себе вопрос: какую проблему мы хотим решить, зачем мы это делаем, что должно получиться в итоге? Судя по куче вопросов к предыдущему докладчику, всегда возникают вопросы про данные: «А где данные? Что нужно исследовать?». И мы всегда наблюдаем две или три ситуации. Идеальная ситуация — когда у нас есть и хорошая задача, и хорошие данные, которые структурированы. Например, данные мобильных операторов. Они сложены в CSV, все суперздорово. Мы все это можем скашеварить и получить какой-то ответ.

Другая ситуация — когда, например, у нас есть только проблема и нет данных. Третья ситуация связана с фоновым слайдом. Это когда «У нас есть классные данные. Давайте что-нибудь покрутим, и будет прикольно». И получаются классные, но бесполезные визуализации.



Когда данных много и когда они хорошо структурированы… Вы прекрасно знаете, поскольку многие из вас работают с данными, что можно легко перейти к стадии, когда вы можете наблюдать паттерны. Например, взяв хорошо структурированный набор данных о велопоездках в Нью-Йорке — есть открытые данные о велопрокатах и загруженности станций, — мы можем видеть паттерны и узнать, например, куда люди ездят или как балансируют велосипеды. Мой коллега после перерыва детально расскажет про московский «Велобайк», потому что там всё не так очевидно и нет такого существенного набора данных, позволяющего увидеть паттерны, и задача становится в разы интереснее.



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



Это не что иное, как трудовая маятниковая миграция.



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

Что же делать, когда, например, данных у нас нет? Гораздо интереснее об этом поговорить, правда? Или, например, самое простое — взять данные и пойти их купить. Правда? У вас есть много денег, вы идёте, покупаете данные. Если вы не знаете, где купить данные, то находите того, кто знает. Он берет свой процент, находит данные или собирает, структурирует и т. д.

В нашем скромном опыте было участие в таком проекте… Если кто-то знает, в прошлом году был открытый конкурс на реконструкцию советских кинотеатров. Один из застройщиков купил у города 39 московских старых кинотеатров, построенных в 1960-х годах, и взял на себя некоторые обязательства на тему того, чтобы не превращать их в торговые центры, а сделать какую-то социально значимую функцию. То есть нужно было сделать эти точки непосредственно в районах, поскольку они все разбросаны по городу, более полезными — кроме торговых центров. Понятно, что здесь задача в коммерческом успехе, но еще нужно было реализовать некоторую социальную функцию. И вот этот застройщик объявил конкурс на тему «Давайте что-нибудь придумаем». Мы помогали одной из конкурсных команд «Aventica» и Nowadays Office обработать данные и предложить какое-то функциональное наполнение. Что в данном случае стоит рассматривать? Если совсем упростить, то условия очень близки к геомаркетинговой задаче. Если кто-то слышал про геомаркетинг, про развитие сети, то что нам нужно сделать? Нам нужно понять спрос в какой-то конкретной локации. То есть речь идет либо о данных БТИ, либо, например, о количестве жителей по данным мобильных операторов. В этом случае мы посчитали просто по реформе ЖКХ — вы знаете, есть открытый сет данных, можно просто, зная среднее количество человек на одно домохозяйство, каким-то образом представить, сколько людей живет на территории. И здесь важная деталь, совершенно неправильная, потому что радиус полтора километра он вроде как описывает. В данном случае это Рязанский проспект, кинотеатр «Восход». Рн там находится чуть-чуть за Рязанским проспектом. Если кто-то хорошо знает восток, то там есть железная дорога, парк «Кусково», и Разянский проспект разбивает территорию. Там идут какие-то промзоны, потому что реальная пешая доступность совсем отличается.



То есть это не 52 тыс. человек в 15-минутной доступности, а на 20% меньше, потому что на самом деле город — не поле, в котором мы можем пройти в любом направлении. Очень важно говорить о зонах пешей доступности, которые учитывают топологию. Чуть подробнее расскажу на следующем примере.



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

Одно дело, когда мы строим хозяйственный магазин или «Ашан». Мы прекрасно знаем, что нас интересует достаточно узкий спектр сервисов, и можем посмотреть на конкурентную среду, посмотреть, насколько люди эту потребность уже реализуют на поверхности. Мы уже можем делать какие-то выводы.



Что делать, когда этого нет? Понятно, что можно каким-то образом перебирать. В данном случае у нас не было машинного обучения, а было человеческое обучение в лице аналитиков, которые занимались исследованием.

Мы сделали суперпростую штуку — прикрутили небольшой интерфейс к API Яндекс.Карт, у которых есть справочник организаций, и дали возможность просто вбивать разные запросы. И вот хороший пример, который у нас внезапным образом получился. Мы таким образом выяснили… Там была одна из локаций, находящаяся около метро Войковская. Если знаете, там есть огромный торговый центр «Метрополис». Казалось бы, какую функцию можно предложить, когда в двух минутах у нас расположена некая махина, где есть все: еда, одежда, что еще может быть. Но при этом нам нужно сделать социально полезную функцию, и чтобы она еще была интересна застройщику и девелоперу в коммерческом плане.

Поэтому таким перебором мы выяснили, что там не реализована спортивная функция, что вокруг Войковской в 10-15-минутных зонах доступности нет хороших фитнес-центров и т. д. Одно из наших предложений… Сразу скажу, чтобы все не выглядело суперкрасиво: мы не выиграли этот конкурс, мы заняли третье место. Нас похвалили за то, что у нас была неплохая аналитика. По сути там была еще и архитектурная концепция, и ряд особых требований. Тем не менее, мы предложили сделать из кинотеатра некое спортивное учреждение, где можно заниматься йогой, либо какое-то пространство, чтобы проводить занятия. Вот один из примеров.

Другой пример про наши эксперименты с данными и их отсутствием является подтверждением тезиса, что голь на выдумку хитра. Это анализ городской среды для пешеходов. Это вообще была одна из идей на старте студии в какой-то момент времени. Очень хотелось ее развить. Мы сейчас ей занимаемся в режиме домашнего проекта, потому что нам это интересно — собрать данные о качестве городской среды для пешеходов. Понятно, что, привет, программа «Моя улица», и тротуары становятся шире, среда становится комфортнее. Но меня очень зацепил один рассказ на TEDx. Он называется «Happy Maps», его рассказывал Даниэле Куэрца, и мне это стало слишком близко. Он говорил, что вот вы идите на работу из точки А в точку Б одним и тем же маршрутом и, на самом деле, можете не подозревать, что маршрут по соседней улице гораздо счастливее, но вы потратите плюс одну-две минуты. Соответственно, стало интересно попробовать сделать город удобнее для человека, добавить ему эту минуту, но добавить и чуть больше счастья. Понятно, что счастье невозможно измерить, оно уже совсем не укладывается в графики и т. д. Но на уровне ощущений хотелось попробовать эту историю. Поэтому мы начали активно заниматься этим проектом. И понятно, что при наличии некоторой экспертизы в картах…

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

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

Так что самая простая идея заключалась в следующем. Берем любое приложение. У вас телефон, у него есть микрофон. На самом деле их два. Если вы знаете, они даже используют специальное шумоподавление, и уже даже есть приложения, которые меряют уровень шума. Было классное приложение, называлось InstaDB. Оно мало того, что меряет шум. Оно еще постит фотку улицы в Instagram. Я сделал специальный аккаунт, и нафоткал под 200 точек. Обработав их, я выяснил, что есть некоторые классы улиц, где можно хорошо и спокойно разговаривать, а есть некоторые классы улиц типа проспекта Вернадского, Большой Якиманки — на ней 80-85 децибел, и совершенно невозможно. Соответственно, мы уже можем наблюдать с этим некоторым экспериментальным набором данных некоторые паттерны. Мы можем их растиражировать на модель города, и, например, взять данные OpenStreetMap.

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



Задаваясь вопросом, как уточнить модель, мы опять же решили попробовать очень простую вещь: из Arduino и разных подручных средств собрать маленький датчик, который просто-напросто каждую секунду шлет вам в телефон по Bluetooth или Wi-Fi уровень шума. И мы сейчас продолжаем эксперименты, потому что железная часть оказалась самой сложной. Мы где-то на грани между точностью измерений и бюджетом. То есть понятно, что нужно уложиться. Например — сделать 10-20 устройств для заинтересованных людей, чтобы хотя бы промерить центр. Нужно уложиться в некоторые разумные рамки при наличии профессиональных устройств, которые стоят дорого.



Отвечая на вопрос, зачем эти данные собираются: можно, например, помочь жителю города строить маршруты с учетом этих параметров, чтобы как-то помочь персонально. То есть понятно, что этими данными город может интересоваться в конечном улучшении, но на самом деле каждый житель может использовать приложение и просто-напросто смотреть, где тихо, а где шумно. Мы сейчас допиливаем некоторую бету приложения. Я думаю, она дойдет до App Store. Она просто показывает, как пройти по тихим улицам, по зеленым. Данными об уровне озеленения с нами поделились ребята из «Гринпис», а о качестве воздуха — компания Aerostate. И соответственно, можно как-то вести пользователя по более классным маршрутам. Понятно, что речь идёт об одном из возможных применений этого набора данных.



Автобусы синие видели? Честно сказать, я еще год назад ничего не знал про транспорт в профессиональном ключе. Так получилось, что в Москве задумали оптимизировать маршрутную сеть. В центре города она уже была сделана, называется «Магистраль». Я хочу рассказать о нашем участии в этом процессе.

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



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



Чем мы занимались? Работали примерно как эти сотрудницы лондонской подземки. Транспортный комплекс Москвы исторически складывался достаточно долго. И понятно, что нет идеальных систем, и не сказать, что мы занимались именно этим. Мы не пересчитывали корешки билетов, чтобы понять, кто куда ездит. Тем не менее, нам удалось… Наверное, мы видели всё. Или не всё — но того, что мы видели, нам хватило, чтобы уже никого не бояться. И в принципе, мы еще можем ходить в цирк и ни разу не улыбнуться.

Мы взяли разные данные, которые были. Эти ребята собрались на воркшопе, транспортные планировщики. Они занимались тем, что оптимизировали маршрутную сеть, то есть они проводили некоторый воркшоп. Для них мы сделали небольшой инструмент, где все эти данные представили в каком-то едином виде, чтобы можно было посмотреть, как, например, спрос пересекается с предложением, и как едет — быстро или медленно. Сигналы «ГЛОНАСС» превратились в очень похожую и знакомую вам картину Яндекс.Пробок, только непосредственно про общественный транспорт — чтобы можно было увидеть, например, где автобус в среднем простаивает на светофорах.



Вкратце про задачу и про то, что делали эти ребята.

Они выписали некоторые принципы, которым следовали, когда оптимизировали маршрутную сеть. И совершенно понятно, что высокий платежный поток и плотность означает высокий спрос. Зачем нужен высокий спрос? Низкая стоимость эксплуатации на одного пассажира. Совершенно очевидно, что если у нас автобусы возят воздух — это неэффективно, неоптимально и, соответственно, стоит достаточно дорого для города. Пешеходная доступность — очевидный параметр. Если у вас в 10-15-минутной пешей доступности нет общественного транспорта — его для вас в принципе нет. Мы берете и покупаете машину.



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

Я не буду сильно останавливаться. У меня есть такая гифка — как меняется плотность рабочих мест в центре Москвы в течение дня. То есть все более-менее понятны.



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

Что такое линейность и асимметрия? Взять, опять же, пассажиропоток. Прикладываете карточку «Тройка» к автобусу. Соответственно, есть статистика о том, сколько вы прошли, по каждой остановке. Здесь очень простой пример неоптимизированного маршрута, первого троллейбуса, который был в одну сторону. По сути, перед нами очень простой сценарий. Например, работая на Шаболовке, я мог приехать в Большой театр на этом троллейбусе, но чтобы мне на данном троллейбусе уехать, нужно было пройтись, например, до Кропоткинской и вернуться на нем обратно. Это совершенно неоптимальный маршрут, и цифры подтверждали, что у метро высокая пересадочность, и в тех местах, где у нас линия расходится, образуется не очень оптимальный трафик.



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

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

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



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

В среднем там получился буст 15–20%. После запуска первой стадии в октябре маршрутная сеть увеличилась по билетам на 20%. Но самое важное, что это очень простой и наглядный способ отвалидировать модель. В том числе у ребят, которые работают на уровне операционного принятия решений, часто возникает задача продать, презентовать эту идею, модель или предложение лицу, которое принимает решения. Если мы говорим про департаменты, то, соответственно, в них довольно понятным образом устроена модель принятия решений. Есть как бы детальное решение и проработка вопросов на уровне руководителей служб, людей, которые непосредственно занимаются транспортом. А решения так или иначе должны принимать руководители департаментов. И эта картинка на самом деле нужна руководителям департаментов, чтобы было видно, что рабочих мест там +20%. Работников в 30-минутной доступности был 905 тыс., а стало 1,015 млн. Соответственно, прибавилось 115 тыс. человек. Это очень простые цифры, которыми легко оперировать. Их можно посмотреть и легко увидеть.



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

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

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

Соответственно, буду очень рад какой-то обратной связи. Если знаете какой-то классный проект — присылайте, с удовольствием запощу.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331586/


История оптимизации одного IoC контейнера

Воскресенье, 25 Июня 2017 г. 12:59 + в цитатник

В этой заметке мне хотелось бы поделиться информацией о небольшом, но, на мой взгляд, весьма и весьма полезном проекте, в котором Stef'an J"okull Sigurdarson добавляет все известные ему IoC контейнеры, которые мигрировали на .NET Core, и с использованием BenchmarkDotNet проводит замеры instance resolving performance. Не упустил возможности поучавствовать в этом соревновании и я со своим маленьким проектом FsContainer.


image


1.2.0


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


Причина оказалась в этом участке кода:


public object Resolve(Type type)
{
    var instance = _bindingResolver.Resolve(this, GetBindings(), type);

    if (!_disposeManager.Contains(instance))
    {
        _disposeManager.Add(instance);
    }

    return instance;
}

Если задуматься, основной принцип работы benchmark'ов- измерение количества выполняемых операций за еденицу времени (опционально потребляемую память), а значит, метод Resolve запускается максимально возможное количество раз. Вы можете заметить, что после resolve полученный instance добавляется в _disposeManager для дальнейшего его уничтожения в случае container.Dispose(). Т.к. внутри реализации находится List, экземпляры в который добавляются посредством проверки на Contains, то можно догадаться, что налицо сразу 2 side-effect'a:


  1. Каждый новый созданный экземпляр, используя проверку Contains, будет вычислять GetHashCode и искать среди ранее добавленных дубликат;
  2. Т.к. каждый новый созданный экземпляр всегда будет являться уникальным (тестировался resolve с TransientLifetimeManager), то и размер List будет постоянно увеличиваться посредством выделения нового, в 2 раза большего участка памяти и копирования в него ранее добавленных элементов (для добавления миллиона экземпляров операции выделения памяти и копирования будут вызваны минимум 20 раз);

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


if (instance is IDisposable && !_disposeManager.Contains(instance))
{
    _disposeManager.Add(instance);
}

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


Method Mean Error StdDev Scaled ScaledSD Gen 0 Gen 1 Allocated
Direct 13.77 ns 0.3559 ns 0.3655 ns 1.00 0.00 0.0178 - 56 B
LightInject 36.95 ns 0.1081 ns 0.0902 ns 2.69 0.07 0.0178 - 56 B
SimpleInjector 46.17 ns 0.2746 ns 0.2434 ns 3.35 0.09 0.0178 - 56 B
AspNetCore 71.09 ns 0.4592 ns 0.4296 ns 5.17 0.14 0.0178 - 56 B
Autofac 1,600.67 ns 14.4742 ns 12.8310 ns 116.32 3.10 0.5741 - 1803 B
StructureMap 1,815.87 ns 18.2271 ns 16.1578 ns 131.95 3.55 0.6294 - 1978 B
FsContainer 2,819.01 ns 6.0161 ns 5.3331 ns 204.85 5.24 0.4845 - 1524 B
Ninject 12,812.70 ns 255.5191 ns 447.5211 ns 931.06 39.95 1.7853 0.4425 5767 B

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


1.2.1


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


private readonly IDictionary> _ctorCache = 
    new ConcurrentDictionary>();

Судя по проведённым замерам, такая нехитрая операция увеличила производительность более чем на 30%:


Method Mean Error StdDev Scaled ScaledSD Gen 0 Gen 1 Gen 2 Allocated
Direct 13.50 ns 0.2240 ns 0.1986 ns 1.00 0.00 0.0178 - - 56 B
LightInject 36.94 ns 0.0999 ns 0.0886 ns 2.74 0.04 0.0178 - - 56 B
SimpleInjector 46.40 ns 0.3409 ns 0.3189 ns 3.44 0.05 0.0178 - - 56 B
AspNetCore 70.26 ns 0.4897 ns 0.4581 ns 5.21 0.08 0.0178 - - 56 B
Autofac 1,634.89 ns 15.3160 ns 14.3266 ns 121.14 2.01 0.5741 - - 1803 B
FsContainer 1,779.12 ns 18.9507 ns 17.7265 ns 131.83 2.27 0.2441 - - 774 B
StructureMap 1,830.01 ns 5.4174 ns 4.8024 ns 135.60 1.97 0.6294 - - 1978 B
Ninject 12,558.59 ns 268.1920 ns 490.4042 ns 930.58 38.29 1.7858 0.4423 0.0005 5662 B

1.2.2


Проводя замеры, BenchmarkDotNet уведомляет пользователя о том, что та или иная сборка может быть не оптимизирована (собрана в конфигурации Debug). Я долго не мог понять, почему это сообщение высвечивалось в проекте, где контейнер подключался посредством nuget package и, какого же было моё удивление, когда я увидел возможный список параметров для nuget pack:


nuget pack MyProject.csproj -properties Configuration=Release

Оказывается, всё это время я собирал package в конфигурации Debug, что судя по обновленным результатам замеров замедляло производительность ещё аж на 25%.


Method Mean Error StdDev Scaled ScaledSD Gen 0 Gen 1 Gen 2 Allocated
Direct 13.38 ns 0.2216 ns 0.2073 ns 1.00 0.00 0.0178 - - 56 B
LightInject 36.85 ns 0.0577 ns 0.0511 ns 2.75 0.04 0.0178 - - 56 B
SimpleInjector 46.56 ns 0.5329 ns 0.4724 ns 3.48 0.06 0.0178 - - 56 B
AspNetCore 70.17 ns 0.1403 ns 0.1312 ns 5.25 0.08 0.0178 - - 56 B
FsContainer 1,271.81 ns 4.0828 ns 3.8190 ns 95.09 1.44 0.2460 - - 774 B
Autofac 1,648.52 ns 2.3197 ns 2.0563 ns 123.26 1.84 0.5741 - - 1803 B
StructureMap 1,829.05 ns 17.8238 ns 16.6724 ns 136.75 2.37 0.6294 - - 1978 B
Ninject 12,520.08 ns 248.2530 ns 534.3907 ns 936.10 41.98 1.7860 0.4423 0.0008 5662 B

1.2.3


Ещё одной оптимизацией стало кеширование функции активатора, которая компилируется с использованием Expression:


private readonly IDictionary> _activatorCache =
    new ConcurrentDictionary>();

Универсальная функция принимает в качестве аргументов ConstructorInfo и массив аргументов ParameterInfo[], а в качестве результата возвращает строго типизированную lambda:


private Func GetActivator(ConstructorInfo ctor, ParameterInfo[] parameters) {
    var p = Expression.Parameter(typeof(object[]), "args");
    var args = new Expression[parameters.Length];

    for (var i = 0; i < parameters.Length; i++)
    {
        var a = Expression.ArrayAccess(p, Expression.Constant(i));
        args[i] = Expression.Convert(a, parameters[i].ParameterType);
    }

    var b = Expression.New(ctor, args);
    var l = Expression.Lambda>(b, p);

    return l.Compile();
}

Соглашусь, что логичным продолжением этого решения должно стать компилирование всей функции Resolve, а не только Activator, но даже в текущей реализации это привнесло 10% ускорение, тем самым позволив занять уверенное 5-е место:


Method Mean Error StdDev Scaled ScaledSD Gen 0 Gen 1 Gen 2 Allocated
Direct 13.24 ns 0.0836 ns 0.0698 ns 1.00 0.00 0.0178 - - 56 B
LightInject 37.39 ns 0.0570 ns 0.0533 ns 2.82 0.01 0.0178 - - 56 B
SimpleInjector 46.22 ns 0.2327 ns 0.2063 ns 3.49 0.02 0.0178 - - 56 B
AspNetCore 70.53 ns 0.2885 ns 0.2698 ns 5.33 0.03 0.0178 - - 56 B
FsContainer 1,038.13 ns 17.1037 ns 15.9988 ns 78.41 1.23 0.2327 - - 734 B
Autofac 1,551.33 ns 3.6293 ns 3.2173 ns 117.17 0.64 0.5741 - - 1803 B
StructureMap 1,944.35 ns 1.8665 ns 1.7459 ns 146.85 0.76 0.6294 - - 1978 B
Ninject 13,139.70 ns 260.8754 ns 508.8174 ns 992.43 38.35 1.7857 0.4425 0.0004 5682 B

P.S.


В качестве эксперимента я решил произвести замеры времени создания объектов используя разные конструкции. Сам проект доступен на Github, а результаты вы можете видеть ниже. Для полноты картины не хватает только способа активации посредством генерации IL инструкций максимально приближенных к методу Direct- именно этот способ используют контейнеры из топ 4, что и позволяет им добиваться таких впечатляющих результатов.


Method Mean Error StdDev Gen 0 Allocated
Direct 4.031 ns 0.1588 ns 0.1890 ns 0.0076 24 B
CompiledInvoke 85.541 ns 0.5319 ns 0.4715 ns 0.0178 56 B
ConstructorInfoInvoke 316.088 ns 1.8337 ns 1.6256 ns 0.0277 88 B
ActivatorCreateInstance 727.547 ns 2.9228 ns 2.5910 ns 0.1316 416 B
DynamicInvoke 974.699 ns 5.5867 ns 5.2258 ns 0.0515 168 B
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331584/


Метки:  

[recovery mode] Техподдержка 3CX отвечает: условия переключения на резервный маршрут (транк) в исходящих правилах

Воскресенье, 25 Июня 2017 г. 09:50 + в цитатник
К нам часто обращаются с похожим вопросом: по какому принципу происходит переключение транка в исходящем правиле, если основной транк недоступен?

Мы подготовили исчерпывающий ответ на этот вопрос. Его можно разделить на 3 части:

  • Выбор и переключение маршрута / транка в исходящем правиле
  • Особенности обработки сообщений Early Media и Ringing
  • Особенности транков с авторизацией по IP (пиринговые транки без регистрации)

Введение


Как известно, 3CX может иметь до 5 разных маршрутов в исходящем правиле. Вы можете определить приоритет этих маршрутов. Если первый маршрут (по сути дела, транк или VoIP шлюз) не может проключить вызов, он передается по второму маршруту и т.д.

image

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

Выбор и переключение маршрута


  • В исходящем правиле 3CX опрашивает все доступные транки  и мосты (межстанционные транки) по порядку от 1 до 5
  • Если транк не зарегистрирован, он исключается автоматически
  • SIP сообщение Ringing (180 или 183) от оператора связи (или VoIP шлюза) не означает успешное проключение. Может быть выбран другой транк, либо перебор транков может завершиться в зависимости от полученных SIP сообщений в дальнейшем (см. ниже)
  • SIP сообщение Busy завершает перебор транков и передает сигнал “занято” вызывающему абоненту. Типы SIP сообщений Busy:

    • 486 Busy Here — абонент занят
    • 600 Busy Everywhere — вызываемый пользователь занят и не желает принимать вызов в данный момент
    • 1408 No Response — внутренний код ошибки 3CX

  • SIP сообщение об успешном проключении завершает перебор транков. Типы сообщений об успешном проключении:

    • 200 OK — успешное завершение проключения
    • Cancel — вызываемый абонент отклонил вызов, не подняв трубку

  • SIP сообщение об ошибке проключения начинает перебор транков (выбор следующего транка по приоритету). Список SIP сообщений об ошибках приведен в конце этой инструкции.

Параметры Early Media и Ringing


При использовании в исходящем правиле более одного транка, обработка сообщений Early Media и Ringing имеет свои особенности. Поскольку 3CX не может предсказать заранее, произойдет ли переключение на резервный маршрут, она меняет правила обработки сообщений Early media (как правило, 183 Ringing).

После получения SIP сообщения 183 Ringing, которые обычно используются при передаче специфических сообщений от оператора (например, служебных голосовых сообщений сети типа “ожидайте ответа абонента”), это сообщение конвертируется в SIP сообщение 180, и аудиопоток от оператора “заглушается”. Абонент на добавочном номере 3CX слышит не служебные сообщения оператора, а гудки дозвона SIP телефона (КПВ). Для справки:

  • 180 Ringing — местоположение вызываемого пользователя определено, выдан сигнал о входящем вызове
  • 183 Session Progress — используется для того, чтобы заранее получить описание сеанса информационного обмена от шлюзов на пути к вызываемому пользователю

Транки с IP авторизацией


Если оператор предоставил транк для сервера 3CX на публичный IP адрес этого сервера (транк с IP авторизацией), 3CX не регистрирует транк на SIP сервере оператора. Таким образом, 3CX не может определить, “работает” этот транк или нет. С точки зрения 3CX он всегда “работает” (в 3CX “светится” зеленым цветом). При обработке исходящего правила 3CX всегда будет пытаться использовать этот транк. Если транк “нерабочий”, это приведет к задержке 32 сек. перед переключением на следующий транк. К сожалению, это поведение нельзя изменить.

Коды SIP ошибок переключения транка


Коды SIP сообщений (ошибок), при котором происходит переключение транка:

  • 400 Bad Request — запрос не понят из-за синтаксических ошибок в нем; ошибка в сигнализации, скорее всего что-то с настройками оборудования
  • 401 Unauthorized — нормальный ответ сервера о том, что пользователь еще не авторизировался; обычно после этого абонентское оборудование отправляет на сервер новый запрос, содержащий логин и пароль
  • 402 Payment Required — требуется оплата
  • 403 Forbidden — абонент не зарегистрирован
  • 404 Not Found — вызываемый абонент не найден; нет такого SIP-номера
  • 405 Method Not Allowed — метод не поддерживается; может возникать, если пользователь пытается отправлять голосовую почту и т.п.
  • 406 Not Acceptable — пользователь недоступен
  • 407 Proxy Authentication Required — необходима аутентификация на прокси-сервере
  • 408 Request Timeout — время обработки запроса истекло; абонента не удалось найти за отведенное время
  • 409 Conflict — конфликт
  • 410 Gone — нет доступа к ресурсу; ресурс по указанному адресу больше не существует
  • 411 Length Required — для указанного ресурса клиент должен указать длину содержимого в заголовке запроса
  • 413 Request Entity too large — размер запроса слишком велик для обработки на сервере
  • 414 Request URI too long — размер SIP URI слишком велик для обработки на сервере
  • 415 Unsupported Media Type — звонок совершается неподдерживаемым кодеком
  • 416 Unsupported URI Scheme — сервер не может обработать запрос из-за того, что схема адреса получателя ему непонятна
  • 420 Bad Extension — неизвестное расширение; сервер не понял расширение протокола SIP
  • 421 Extension Required — в заголовке запроса не указано, какое расширение сервер должен применить для его обработки
  • 423 Interval too brief — сервер отклоняет запрос, так как время действия ресурса слишком короткое
  • 433 Anonymity Disallowed — анонимный вызов запрещен
  • 480 Temp Unavailable — временно недоступное направление, попробуйте позвонить позже
  • 481 Call Transaction does not exist — действие не выполнено; нормальный ответ при поступлении дублирующего пакета
  • 482 Loop Detected — обнаружен замкнутый маршрут передачи запроса
  • 483 Too many hops — запрос на своем пути прошел через большее число прокси-серверов, чем разрешено
  • 484 Address Incomplete — принят запрос с неполным адресом
  • 485 Ambiguos — адрес вызываемого пользователя неоднозначен
  • 487 Request Terminated — запрос отменен; обычно приходит при отмене вызова
  • 500 Server Internal Error — внутренняя ошибка сервера
  • 501 Not Implemented — в сервере не реализованы какие-либо функции, необходимые для обслуживания запроса; метод запроса SIP не поддерживается
  • 502 Bad Gateway — сервер, функционирующий в качестве шлюза или прокси-сервера, принимает некорректный ответ от сервера, к которому он направил запрос
  • 503 Service Unavailable — сервер не может в данный момент обслужить вызов из-за перегрузки или проведения технического обслуживания
  • 504 Server Timeout — сервер не получил ответа в течение установленного промежутка времени от сервера, к которому он обратился для завершения вызова
  • 505 Version Not Supported — версия не поддерживается; сервер не поддерживает эту версию протокола SIP
  • 513 Message Too Large — сервер не в состоянии обработать запрос из-за большой длины сообщения
  • 603 Decline — вызываемый пользователь не желает принимать входящие вызовы, не указывая причину отказа
  • 604 Does not exist anywhere — вызываемого пользователя не существует
  • 606 Not Acceptable — соединение с сервером было установлено, но отдельные параметры, такие как тип запрашиваемой информации, полоса пропускания или вид адресации недоступны
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331568/


[Перевод] Оптимизация расходов на AWS в SaaS-бизнесе

Воскресенье, 25 Июня 2017 г. 09:42 + в цитатник

Расходы Cronitor на AWS за последние 12 месяцев

В первые 30 дней после перевода Cronitor на AWS в январе 2015 года мы собрали платежей на $535 и заплатили $64,47 за хостинг, передачу данных и доменное имя. С тех пор мы наращивали потребление услуг, апгрейдили инстансы, добавляли сервисы. Несмотря на репутацию AWS как дорогого пистолета, чтобы выстрелить себе в ногу, наши счета сохранялись на уровне 12,5% от дохода. Смотрите сами.

Синяки и шишки от дешёвого AWS


Вскоре стало ясно, что наша идея имеет некоторую перспективу. Мы поняли, что нужно поднять планку с побочного проекта до полноценного малого бизнеса. Целью была не высокая доступность, а только повышение доступности по сравнению с предыдущей конфигурацией на единственном 2 ГБ Linode. Мы реально хотели только перезапустить базу данных без потери входящей телеметрии. Ничего особенного. Первоначальная установка была довольно простой:

  • ELB
  • SQS
  • Пара инстансов t2.small с веб-приложением и сбором данных, оба на us-west-2
  • Один инстанс m3.medium, где работали MySQL и наш демон, который определял сбои и рассылал оповещения

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

Радость оказалась недолгой.

Проблема 1: сбой ELB


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

Немедленно после миграции пользователи начали жаловаться на периодические таймауты, так что мы решили перепроверить серверную конфигурацию и историю логов ELB. Со скачками трафика пока что менее 100 запросов в секунду мы отказались от мысли, что проблема в ELB, и стали искать ошибки в нашей собственной конфигурации. В конце концов, мы запустили тест для непрерывного пингования сервиса. Он начинался за несколько моментов до 00:00 UTC и заканчивался через мгновений после полуночи — и увидели неудачные запросы, которые вообще не попадают в логи ELB. Отдельные инстансы были доступны, а очередь запросов не создавалась. Стало ясно, что соединения обрывались на уровне балансировщика нагрузки, вероятно, потому что наши скачки трафика оказались слишком большими и были слишком кратковременными, чтобы он разогрелся на б'oльшую нагрузку. Из-за дороговизны плана с техподдержкой на AWS мы не могли попросить их вручную увеличить размер нашего ELB, так что вместо этого решили запустить циклические запросы, используя DNS, чтобы вообще устранить необходимость балансировщика нагрузки.

Извлечённый урок:

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

Проблема 2: знакомство с балансом кредитов CPU


Линейка инстансов T2 с поддержкой всплесков нагрузки экономически выгодна, если нагрузка изредка возрастает, как говорит официальный сайт. Но вот я бы хотел, чтобы там было написано: если вы запускаете свой инстанс на постоянной нагрузке 25% CPU, то у вас начинает истощаться баланс кредитов CPU, а когда он заканчивается, у вас по сути остаётся вычислительная мощность Rasperry Pi. Никаких предупреждений об этом вы не получите, а ваш CPU% не будет отражать уменьшенную мощность. В первый раз, исчерпав баланс кредитов, мы решили, что соединения обрываются по какой-то другой причине.

Извлечённые уроки:

  • Если нечто предлагается очень дёшево, на это есть хорошая причина, нужно понимать это
  • Инстансы T2 следует использовать только в группе автомасштабирования
  • Просто на всякий случай создайте уведомление CloudWatch, которое предупредит в случае падения баланса кредитов ниже 100

Проблема 3: что написано мелким шрифтом


В прошлом году на конференции re:Invent компания Amazon обновила своё предложение Reserved Instance, вероятно, в ответ на более выгодные условия от Google Cloud. В пресс-релизе говорилось, что зарезервированные инстансы станут дешевле и их можно будет переносить между зонами. Это же отлично!

Когда в октябре пришло время закрывать наши последние инстансы T2, мы выкатили новые M3 с этими подешевевшими и более гибкими 12-месячными резервациями. После появления нескольких крупных клиентов в апреле мы решили снова поднять уровень инстансов, теперь до M4.large. Оставалось шесть месяцев с октябрьской брони, так что я решил продать их, как всегда делал раньше. И тогда я узнал горькую правду, что ценой за эти подешевевшие и более гибкие резервации было то, что… вы не можете перепродать их.

Извлечённые уроки:

  • Если нечто предлагается очень дёшево, на это есть хорошая причина, нужно понимать это
  • Всегда дважды читайте условия предложения. Биллинг AWS невероятно сложный.

Взгляд на реальные цены AWS


Сегодня наша инфраструктура остаётся довольно простой:

  • Кластер M4.large занимается сбором входящей телеметрии
  • Кластер M3.medium обслуживает веб-приложение и API
  • Воркер M4.large с нашим демоном мониторинга и системой оповещений
  • M4.xlarge для MySQL и Redis

Мы продолжаем использовать ряд управляемых сервисов, в том числе SQS, S3, Route53, Lambda и SNS.

Elastic Compute


Мы используем частичное авансовое бронирование для всех наших сервисов.

Можете заметить, что из наших ежемесячных счетов 2/3 тратится на обеспеченные IOPS (операций ввода/вывода в секунду), как мы делаем для инстансов. В отличие от гарантированных IOPS в большинстве облачных метрик, которые являются просто записью в условиях договора, это реальная услуга, которая имеет себестоимость. Они также составят существенную часть вашего бюджета EC2 для любого хоста, где важна дисковая производительность. Если не будете платить за IOPS, ваши задачи будут стоять в очереди и ожидать освобождения ресурсов.


Пожалуйста, не спрашивайте, что означает “alarm-month”

SQS


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

Во время миграции мы беспокоились, что SQS представляет собой единственную точку отказа для нашего конвейера сбора данных. Чтобы избежать риска, мы запустили маленьких демонов на каждом хосте для буферизации и повторных попыток записи SQS в случае сбоя. Буферизация сообщений потребовалась только однажды за 2,5 года, так что 1) его на 100% стоило запускать; 2) SQS доказал невероятную надёжность в регионе us-west-2.



Lambda


Наш сервис Healthchecks построен частично на воркерах Lambda в регионах, указанных ниже. Нужно заметить, что у Lambda есть щедрый бесплатный уровень (Free Tier), который применяется к каждому региону отдельно. В данное время бесплатный уровень рекламируется как «неопределённый».



S3


Мы делаем резервные копии снимков БД и логов на S3 с репликацией в us-east-1 для восстановления в случае катастрофы.

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



В завершение


Поработав с большими корпоративными и получившими инвестиции проектами на AWS, я могу лично поручиться, что вы можете запускать здесь большое дело. Они создали сказочный набор инструментов и блестящих объектов, которые все схвачены вместе — и с вас берут деньги за всё, к чему вы прикоснётесь. Это опасно, нужно быть сдержанным и ограничивать свои аппетиты, но вы будете вознаграждены возможностью вырастить маленький бизнес в нечто большее, когда любые ресурсы доступны вам по заказу, как только понадобятся. Иногда хорошо остановиться на секунду и задуматься о том, насколько это здорово — а затем снова возвращаться к работе.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331566/


Метки:  

Symfony 4: Тестируем плагин Symfony Flex

Суббота, 24 Июня 2017 г. 22:34 + в цитатник
Несколько месяцев назад вышла альфа версия Composer плагина Symfony Flex. С выпуском Symfony 3.3 стало возможным протестировать работу данного плагина и «попробовать на вкус» подход к построению приложений на Symfony 4. Что мы сейчас и попробуем сделать.

В предыдущем посте я писал про структуру приложения Symfony 4 и некоторых особенностях, поэтому сейчас заострять внимание на этом не буду. Для того, чтобы процесс установки прошел гладко, рекомендую пользователям Windows использовать GitBash или Cygwin терминал. Вообще-то вы можете использовать любой терминал, главное чтобы в окружении была доступна утилита make и установленные linux-tools.

Symfony Flex


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

На данный момент существует два репозитория рецептов, главный (или официальный) и публичный. Главный репозиторий отличается тем, что может определять алиасы для пакетов и размещение рецепта в этот репозиторий должно быть одобрено core-team Symfony. В публичном репозитории более мягкие правила для размещения.

Установка плагина


Первым делом установим Symfony Flex:

composer require symfony/flex


С этого момента плагин контролирует процесс установки пакетов в ваше приложение. Чтобы получить простое «Hello World» приложение установим symfony/framework-bundle. Мы уже можем воспользоваться «магией» Symfony Flex плагина и использовать удобный алиас для этого:

composer req frameworkbundle


Теперь давайте посмотрим на структуру каталогов:

/etc
/src
/vendor
/web
.env
.env.dist
.gitignore
composer.json
composer.lock
Makefile


В каталоге /etc, теперь находится вся конфигурация и файл bundles.php, который содержит массив всех бандлов установленных в вашем проекте. Этот файл модифицируется плагином Symfony Flex, каждый раз когда вы устанавливаете или удаляете бандл. Если вы хотите использовать локальный бандл, вам придется самостоятельно прописать его в этот файл.

Для конфигурации контейнера предусмотрен файл container.yaml, для роутинга routing.yaml. Так же в /etc вы увидите каталог packages, в котором хранятся минимальные конфигурации по умолчанию для всех компонентов. По сути эти конфигурации копируются из рецептов.

В каталоге /src, располагается класс Kernel, переработанный под новую структуру каталогов. Так же здесь есть пустой каталог Controller.

В папке /web теперь только index.php для всех видов окружения. Если посмотреть содержимое этого файла, можно заметить, что для имитации присутствия переменных окружения используется компонент symfony/dotenv. Этот компонент поставляется по умолчанию с symfony 3.3. В режиме разработки он позволяет определять переменные окружения в файле .env

Давайте установим этот компонент:

composer req symfony/dotenv


Теоретически мы установили минимальный набор компонентов необходимых для запуска приложения. Но есть один нюанс. По умолчанию рецепты поставляются с конфигурацией в YAML. И поэтому даже если собственные конфигурации вы хотите писать в другом формате, вам все равно придется установить компонент symfony/yaml, чтобы конфигурации всех компонентов могли быть загружены. В противном случае вам придется переписывать все конфиги в каталоге /etc/packages в нужный вам формат каждый раз когда вы добавляете новый компонент.

Давайте установим symfony/yaml:

composer req symfony/yaml


Почти все готово. Осталось немного «поколдовать» над файлом composer.json. Первое, что нужно сделать это прописать секцию psr-4, для автозагрузки наших классов. Так как весь код у нас лежит теперь прямо в /src, добавим:

{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}


Обратите внимание на следующую секцию в composer.json:

{
    "scripts": {
        "auto-scripts": {
            "make cache-warmup": "script",
            "assets:install --symlink --relative %WEB_DIR%": "symfony-cmd"
        }
    }
}


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

        "post-install-cmd": [
            "@auto-scripts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ]


Этот шаблон позволяет плагину Symfony Flex включиться в процесс при наступлении событий «post-install» и «post-update» композера.

Финальный вид composer.json
{
    "require": {
        "symfony/flex": "^1.0",
        "symfony/framework-bundle": "^3.3",
        "symfony/dotenv": "^3.3",
        "symfony/yaml": "^3.3"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "scripts": {
        "auto-scripts": {
            "make cache-warmup": "script",
            "assets:install --symlink --relative %WEB_DIR%": "symfony-cmd"
        },
        "post-install-cmd": [
            "@auto-scripts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ]
    }
}



Теперь попросим Composer пересобрать класс автозагрузки:

composer dump-autoload


Самое время добавить контроллер. Создадим в папке /src/Controller класс TestController:

namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;

class TestController
{
    public function test()
    {
        return new Response('It's work!');
    }
}


Теперь нужно прописать маршрут в файле /etc/routing.yaml:

index:
  path: /
  defaults: {_controller: 'App\Controller\TestController::test'}


Запускаем веб-сервер с помощью make:

make serve


Переходим в браузере по 127.0.0.1:8000 и видим наше сообщение «It's work!».
Минимальное приложение собрано и запущено, но хочется использовать аннотации для определения маршрутов. Без проблем! Для этого нам потребуется sensio/framework-extra-bundle и symfony/annotations-pack, для последнего предусмотрен удобный алиас annot.

composer req sensio/framework-extra-bundle

composer req annot


Теперь закомментируем маршрут в файле routing.yaml, который определили до этого и вместо него укажем где лежат наши классы контроллеров для парсера аннотаций:

controllers:
    resource: ../src/Controller/
    type: annotation


Изменим наш контроллер, добавив аннотацию для маршрута (не забудьте так же добавить use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route):

namespace App\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Response;

class TestController
{
    /**
     * @return Response
     * @Route("/test", name="test")
     */
    public function test()
    {
        return new Response('It's work!');
    }
}


Переходим по 127.0.0.1:8000/test и видим наше сообщение.
Если нам больше не нужно использовать аннотации, то мы можем удалить компоненты:

composer rem annot
composer rem sensio/framework-extra-bundle


Останется только вернуть настройки роутинга в routing.yaml, и все будет работать. Плагин Symfony Flex позаботится о том, чтобы удалить за собой весь мусор, а так же убрать из конфигурации бандл sensio/framework-extra-bundle.

Вот таким образом можно уже сейчас экспериментировать с приложениями в стиле Symfony 4.
По правде говоря, вы можете воспользоваться уже готовым composer.json файлом для установки такого приложения «из коробки»:

composer create-project "symfony/skeleton:^3.3" demo


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

Спасибо за внимание.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331526/


Метки:  

Подбор закона распределения случайной величины по данным статистической выборки средствами Python

Суббота, 24 Июня 2017 г. 20:36 + в цитатник

О чём могут «рассказать» законы распределения случайных величин, если научиться их «слушать»


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

К основным характеристикам законов распределения относятся: наиболее вероятное значение измеряемой величины под названием математическое ожидание (mean); мера рассеивания случайной величины вокруг математического ожидания под названием среднеквадратическое отклонение (std).

Дополнительными характеристиками являются – мера скученности дифференциальной формы закона распределения относительно оси симметрии под названием асимметрия (skew) и мера крутости, огибающей дифференциальной формы под названием эксцесс (kurt). Читатель уже догадался, что приведенные сокращения взяты из библиотек scipy. stats, numpy, которые мы и будем использовать.
Рассказ о законах распределения погрешности измерений был бы неполным, если не упомянуть об связи между энтропийным и среднеквадратичным значением погрешности. Не утомляя читателей длинными выкладками из информационной теории измерений [1], сразу сформулирую результат.

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

$delta0=np.std(x)*np.sqrt(np.pi*np.e*0.5)$



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

Введём ещё один показатель – энтропийный коэффициент k, который для нормального распределения равен:

$k= delta0/ np.std(x) = 2.07$



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

Лучше один раз увидеть, чем семь раз прочитать. Для дальнейшего сравнительного анализа интегральных распределений: равномерного, нормального и логистического модернизируем примеры, приведённые в документации [2].

Программа для нормального распределения:
from scipy.stats import norm
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(1, 1)
# Calculate a few first moments:
mean, var, skew, kurt = norm.stats(moments='mvsk')
# Display the probability density function (``pdf``):
x = np.linspace(norm.ppf(0.01),  norm.ppf(0.99), 100)
ax.plot(x, norm.pdf(x),
       'r-', lw=5, alpha=0.6, label='norm pdf')
ax.plot(x, norm.cdf(x),
       'b-', lw=5, alpha=0.6, label='norm cdf')
# Check accuracy of ``cdf`` and ``ppf``:
vals = norm.ppf([0.001, 0.5, 0.999])
np.allclose([0.001, 0.5, 0.999], norm.cdf(vals))
# True
# Generate random numbers:
r = norm.rvs(size=1000)
# And compare the histogram:
ax.hist(r, normed=True, histtype='stepfilled', alpha=0.2)
ax.legend(loc='best', frameon=False)
plt.show()




Программа для равномерного распределения:
from scipy.stats import uniform
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(1, 1)
# Calculate a few first moments:
#mean, var, skew, kurt = uniform.stats(moments='mvsk')
# Display the probability density function (``pdf``):
x = np.linspace(uniform.ppf(0.01), uniform.ppf(0.99), 100)
ax.plot(x, uniform.pdf(x),'r-', lw=5, alpha=0.6, label='uniform pdf')
ax.plot(x, uniform.cdf(x),'b-', lw=5, alpha=0.6, label='uniform cdf')
# Check accuracy of ``cdf`` and ``ppf``:
vals = uniform.ppf([0.001, 0.5, 0.999])
np.allclose([0.001, 0.5, 0.999], uniform.cdf(vals))
# True
# Generate random numbers:
r = uniform.rvs(size=1000)
# And compare the histogram:
ax.hist(r, normed=True, histtype='stepfilled', alpha=0.2)
ax.legend(loc='best', frameon=False)
plt.show()




Программа для логистического распределения.
from scipy.stats import logistic
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(1, 1)
# Calculate a few first moments:
mean, var, skew, kurt = logistic.stats(moments='mvsk')
# Display the probability density function (``pdf``):
x = np.linspace(logistic.ppf(0.01),
                logistic.ppf(0.99), 100)
ax.plot(x, logistic.pdf(x),
       'g-', lw=5, alpha=0.6, label='logistic pdf')
ax.plot(x, logistic.cdf(x),
       'r-', lw=5, alpha=0.6, label='logistic cdf')
vals = logistic.ppf([0.001, 0.5, 0.999])
np.allclose([0.001, 0.5, 0.999], logistic.cdf(vals))
# True
# Generate random numbers:
r = logistic.rvs(size=1000)
# And compare the histogram:
ax.hist(r, normed=True, histtype='stepfilled', alpha=0.2)
ax.legend(loc='best', frameon=False)
plt.show()




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

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


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

Первая часть программы предназначена для подготовки к сравнению трёх законов распределения в интегральной форме.
from scipy.stats import logistic,uniform,norm,pearsonr
from numpy import sqrt,pi,e
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
n=1000# объём выборки
x=uniform.rvs(loc=0, scale=150, size=n)#равномерное распределение 
x.sort()#сортировка 
print("Математическое ожидание по выборке(общее для сравниваемых распределений) -%s"%str(round(np.mean(x),3)))
print("СКО по выборке(общее для сравниваемых распределений) -%s"%str(round(np.std(x),3)))
print("Энтропийное значение погрешности-%s"%str(round(np.std(x)*sqrt(np.pi*np.e*0.5),3)))      
pu=uniform.cdf(x/(np.max(x)))#равномерное интегральное  распределение 
ax.plot(x,pu, lw=5, alpha=0.6, label='uniform cdf')
pn=norm.cdf(x, np.mean(x), np.std(x))#нормальное интегральное  распределение 
ax.plot(x,pn, lw=5, alpha=0.6, label='norm cdf')
pl=logistic.cdf(x, np.mean(x), np.std(x))# логистическое  интегральное  распределение 
ax.plot(x,pl, lw=5, alpha=0.6, label='logistic cdf')


Здесь и далее результаты для сравнения введены в функцию print для контроля за ходом вычислений.

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

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

Вторая часть программы
p=np.arange(0,n,1)/n
ax.plot(x,p, lw=5, alpha=0.6, label='test')
ax.legend(loc='best', frameon=False)
plt.show()
print("Корреляция между нормальным  распределением и тестовым - %s"%str(round(pearsonr(pn,p)[0],3)))
print("Корреляция между логистическим  распределением и тестовым - %s"%str(round(pearsonr(pl,p)[0],3)))
print("Корреляция между равномерным  распределением и тестовым - %s"%str(round(pearsonr(pu,p)[0],3)))
print('Взвешенная сумма  квадратов отклонения нормального распределения от теста -%i'%round(n*sum(((pn-p)/pn)**2)))
print('Взвешенная сумма квадратов отклонения логистического распределения от теста -%i'%round(n*sum(((pl-p)/pl)**2)))
print('Взвешенная сумма квадратов отклонения равномерного распределения от теста -%i'%round(n*sum(((pu-p)/pu)**2))) 


Тестовая функция интегральной формы закона распределения построена в виде ступенчатого накопления – 1/n +2/n+……+1/

График и результат роботы программы.
Математическое ожидание по выборке (общее для сравниваемых распределений) -77.3
СКО по выборке (общее для сравниваемых распределений) -43.318
Энтропийное значение погрешности-89.511
Корреляция между нормальным распределением и тестовым — 0.994
Корреляция между логистическим распределением и тестовым — 0.998
Корреляция между равномерным распределением и тестовым — 1.0
Взвешенная сумма квадратов отклонения нормального распределения от теста -37082
Взвешенная сумма квадратов отклонения логистического распределения от теста -75458
Взвешенная сумма квадратов отклонения равномерного распределения от теста -6622

Тестовое распределение вероятностей в интегральной форме является равномерным. При минимальном отличии по коэффициенту корреляции для равномерного распределения взвешенное отклонение от тестового в 5,6 раза меньше, чем у нормального и в 11 раз меньше, чем у логистического.

Вывод


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

1. Элементы информационной теории измерений.

2. Statistical functions.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331560/


Метки:  

Ограничивать ли пользователей по ресурсам?

Суббота, 24 Июня 2017 г. 19:01 + в цитатник
Сколько я занимаюсь ИТ — столько я слышу от админов «больно жирно будет пользователям, обрежем им трафик / объем почтового ящика / файловую шару / заблокируем сайт / подставить по вкусу». И ровно столько же у меня возникает вопрос: какое ваше дело?
Давайте забудем, что мы ИТ-шники и управляем клёвыми СХД, фермами серверов, почтовыми серверами и посмотрим на всю это катавасию отстраненно. Рассмотрим коммерческую структуру.

1. Чем занимается ваша компания?

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

image



2. Кто производит деньги?

А их производят те самые «тупые юзвери», которые просят нажать any key великого и могучего техномага. Даром что техномаг настолько велик, что не может читать инструкции на английском (рабочем языке ИТ), а для русского он и так слишком велик.

image

3. При помощи чего они производят деньги?

Деньги производятся в том числе при помощи ИТ сервисов, включая почтовые серверы для коммуникации с клиентами, интернета, систем учета, бухгалтерии и т.д. В процессе производства денег ИТ сервисы потребляют ИТ ресурсы — сырые мощности (процессоры / ОЗУ / СХД), лицензии, каналы.

4. Кому принадлежит ИТ инфраструктура и ресурсы?

Вот здесь раз от раза я натыкаюсь на то, что администраторы начинают считать серверы, СХД и коммутаторы «своими». Даже немного, и начинают относиться к ним соответственным образом.

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

5. И теперь — кто должен решать, сколько ресурсов может потребить сотрудник бизнес подразделения?

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

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

Вы конечно можете возразить сразу по нескольким пунктам. Давайте их рассмотрим.

image

1. Если не ограничивать соц. сети, то вместо работы все будут только во вконтактике сидеть.

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

2. Они безмозглые и ничего не понимают в ИТ. И совсем не думают, что их смешные картинки и видюшки в почтовых вложениях полностью съедят СХД под почту.

Ну на то и есть вы, мозглые и понимающие. Только вот есть снова интересный момент, ничего не воспитывает человека так быстро и эффективно, как воздействие на его кошелек. Один раз остаться всем отделу маркетинга без премии потому что они 90% потребленного пространства в почте потратили на картинки — и их компьютерная грамотность / здравый смысл резко повысятся. А самое главное, стимулировать их повышение будет родными и понятными методами начальник отдела, оставшийся без премии. Вся их премия уйдет на расширение СХД.

image

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

Позвольте, но это классический пример «административная проблема техническими средствами не решается». Проблема с планированием бюджета / экономическим обоснованием закупок в классической модели — это административная проблема на уровне директората, а не админа. Более того, при помощи биллинга вопрос обоснования бюджетов решается сильно проще.
Есть и другая сторона ограничения ресурсов. Вы перестаете контролировать что происходит в компании. Пользователи начинают удалять важную на самом деле почту, потому что новую не могут получить. Важные документы оказываются в облаках, переписка перемещается с корпоративной почты в Яндекс и Мейл.ру. Иными словами, именно слепой репрессивный метод является создателем «теневого ИТ».

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

https://habrahabr.ru/post/331558/


Метки:  

[Из песочницы] Автоматное программирование – новая веха или миф?

Суббота, 24 Июня 2017 г. 18:23 + в цитатник
Тема автоматного программирования ( AP, АП) уже много лет занимает заметное место в научно-популярных СМИ. Однако, несмотря на это, АП не стало магистральным трендом. Главная причина здесь — недостаточный опыт использования, и как следствие, отсутствие популяризаторов. Нельзя сказать, что недостаточно статей посвященных АП, но круг обсуждаемых в статьях вопросов по большому счёту сводится к описанию UML Statechart, т.е. инструменту описания автоматов, либо к вопросу «Как реализуются программные автоматы?». Это печально но факт, отсутствует обсуждение того, какие перспективы для программистов-профессионалов открываются при использовании данной технологии.

Эта статья – попытка взглянуть на программаты глазами прагматика, на примере задачи, взятой из реальной практики программирования микроконтроллеров. Однако она может заинтересовать не только embedderов, поскольку автоматный подход может эффективно использоваться для создания и драйверов и интерактивных приложений в системах основанных на обработке событий, как например Windows.


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

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



Рисунок 1. Общая структура цифровых автоматов

Где:

  • комбинационная схема (КС) – цифровая логическая схема, не содержащая внутри себя элементов памяти. Т.е. её состояние определяется только тем, что у неё на входе.
  • Входы КС условно делятся на две группы I(n) – внешние сигналы (входы автомата), S(n) – внутреннее состояние автомата на текущем шаге. Внутреннее состояние это, образно говоря, некий «режим», определяющий то, как автомат будет преобразовывать внешние сигналы I(n) в сигналы O(n). У классической комбинационной схемы (нет группы входов S(n)) такой «режим» только один.
  • Выходы КС тоже делятся на две группы O(n) – сигналы, выходящие вовне, которые собственно и выполняют «полезную работу», S(n+1) внутреннее состояние для следующего шага. То есть, на каждом шаге автомат в зависимости от входного сигнала I(n) не только вычисляет нужный выходной сигнал O(n), но и включает себе режим обработки сигналов на следующий шаг (т.е. сигналов I(n+1) ), причём при необходимости этот режим может быть тем же самым или другим. Иными словами, можно задать требуемые режимы на любые возможные случаи последовательностей входных сигналов, что и делает автоматы столь «всемогущими».
  • С целью синхронизации вводится запоминающее устройство (ЗУ, параллельный регистр) которое отделяет в цепи обратной связи слово относящееся к предыдущему шагу, от слова относящегося к следующему. Символы n и n + 1 означают текущий шаг, и следующий шаг, т.е. n соответствует не временной оси, а последовательности шагов. Шаги задаются тактовым сигналом, и через тактовый сигнал осуществляется привязка к временной оси. Шаги могут быть связаны не с периодическим тактовым сигналом, а с событием «приход сигнала I».
  • Аналогичное запоминающее устройство, для той же цели вводится в канал входных сигналов I

Если вы не были знакомы с автоматами, при всей ясности объяснения польза от таких устройств не очевидна, но существует математическая абстракция, хорошо иллюстрирующая суть. Работу автомата можно наглядно описать при помощи диаграммы состояний и переходов. Ниже изображена диаграмма, описывающая работу устройства, управляющего лифтом. Это очень упрощённая диаграмма, не учитывающая процессы открытия/закрытия дверей, разгона/остановки, но она даёт наглядное представление о том, как при помощи автоматов моделируется работа объектов реального мира. Над стрелками пишется условие, при котором произойдёт переход, в овале пишется
название_состояния/что_будет_на_выходе_пока_автомат_в_этом_состоянии.



Рисунок 2. Пример диаграммы состояний и переходов

К сказанному выше добавлю, что на диаграмме показан автомат Мура. Состояние выходов такого автомата зависит от текущего состояния. Альтернатива – автомат Мили. Его выходной сигнал зависит от последнего совершённого перехода, поэтому что_будет_на_выходе записывается над соответствующей стрелкой. Несмотря на это различие автоматы Мили и Мура математически преобразуются друг в друга. Для наших познавательных целей больше подходят автоматы Мура, но в практике программирования полезны обе абстракции, поэтому в следующей части мы не обойдём вниманием и автоматы Мили.

Все более-менее сложные цифровые схемы проектируются именно как цифровые автоматы. Почему? Незаменимости автоматного подхода при проектировании цифровых схем способствуют три основных преимущества автоматного подхода:

  • Декомпозиция.
  • Взгляд на процессы не как на последовательность шагов, а как на совокупность всех возможных шагов.
  • Математика.

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

Часть 1. Практическая декомпозиция.


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

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

Автоматы обычно разбиваются на операционные и на управляющие. Смысл очевиден из названия — операционный автомат это «руки», управляющий – «голова». Причём разбиение может быть многоуровневым: операционный автомат в свою очередь может быть разбит на исполнительную и руководящую части. Т.е. рука-манипулятор, может иметь свой «минимозг», транслирующий общие команды («взять предмет») в набор детальных команд, управляющих каждым «пальцем». Ещё более показательным примером является процессор, имеющий конвейеры, регистры, ALU и FPU – операционные автоматы и микропрограмму – управляющий автомат.


Рисунок 3. Декомпозиция автомата на операционный и управляющий

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

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


a)


б)


в)

Рисунок 4. Требования к модулю вывода на дисплей

Все символы в шрифте одной высоты, но шрифт может быть поменян «на лету», в процессе вывода одной и той же строки. Аналогично могут быть поменяны атрибуты – жирный, курсив, подчёркивание. Для управления параметрами используются esc-последовательности, к которым относится управляющий символ '\n', перевод строки, т.е. текст одной строки может быть выведен на несколько строк на дисплее. Например текст:

"Text 1 \033[7m Text 2  \033[27m  \033[1m Text 3 \033[21m \n Text 42"

будет отображаться так, как это показано на иллюстрации (рис.4, б)

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

Нам нужно создать функцию, которая всё это реализует, с прототипом

void Out_text(int x0, int y0, int x1, int y1, int x_shift, int y_shift, char * Text);

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

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

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

Как следует из условия задачи, исходная последовательность символов в общем случае выглядит как: Текст1 упр1 Текст2 упр2 Текст3 упр3 Текст4 упр4 Текст5 \0
где упрN управляющие esc-последовательности, символы перевода и окончания строки которые отделяют друг от друга текстовые блоки. Разбивка текста на блоки удобна тем, что позволяет в рамках одного блока использовать максимальное количество одинаковых настроек (например высота текста и координаты начала строки).

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


Рисунок 5. Первоначальное разбиение

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

  • сначала послать команду Записать_байт (координаты байта на дисплее)
  • после чего, возможно, получить подтверждение,
  • и только после этого можно передавать байт.

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

Каждый текстовый блок характеризуется координатами текстового блока x,y(относительно окна вывода), смещением x_shift, y_shift относительно координат x,y текстового блока, шрифтом и атрибутами, такими как инверсный или нет, мигающий, жирный, курсив, подчёркивание и так далее.

Автомат разбивки текста на блоки

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



Рисунок 6. Пояснение к ОА автомата разбивки на блоки

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

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

После того как разработан ОА несложно составить требуемый ему управляющий автомат



Рисунок 7. Диаграмма состояний автомата разбивки на текстовые блоки

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

Автомат вывода текстового блока

Как было показано выше, управляющие последовательности могут в том числе задавать координаты вывода очередного текстового блока. Координаты текущего текстового блока задаются переменными x, y.



Рисунок 8. Используемые при выводе текстового блока координаты

Пояснение к рисунку.

x_max, y_max – размеры дисплея
x0, y0, x1, y1 – координаты окна вывода, параметры функции Out_text
x_shift, y_shift – смещение, может быть положительным и отрицательным, влияет на расположение всех текстовых блоков.
x,y — координаты вывода текущего текстового блока, могут изменяться esc командами. Координаты отсчитываются относительно окна вывода.

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

В примере используется шрифт размером 5х7, но описываемый модуль поддерживает работу с символами любого размера. В результате может потребоваться крупный буфер сборки строки, что для embedderов часто является немаловажным фактором. Для минимизации буфера вместо набора параллельных регистров может использоваться один, осуществляющий «вертикальную развёртку», речь идёт о вертикальной развёртке всего текстового блока, т.е. выводится строка высотой один пиксел и шириной во весь текстовый блок.


Рисунок 9. Вертикальная развёртка при выводе текстового блока

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

$$display$${Height_{символа }* Width_{экрана}/8} = 768 байт$$display$$


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

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

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



Рисунок 10. Пояснение работы операционного автомата прорисовки текстового блока.

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



Рисунок 11. Пояснение связи данных в строчном буфере и видеопамяти

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

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

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

Все символы строки разделены на категории:


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

Каждая категория подразумевает свой режим обработки:

  • Символы не попавшие в окно вывода (1) просто игнорируются.
  • Первый символ частично или целиком попавший в окно вывода (2) может требовать дополнительного по сравнению с типом 3 сдвига влево, чтобы отсечь выступающие за область вывода пикселы. Выпавшие байты и биты просто теряются.



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

  • Символы полностью попавшие в область вывода (3) как и символы тип (2) требуют сдвига вправо, величина которого зависит от координаты символа.



Рисунок 14. Пояснение механизма скользящего сдвига

Для первого символа выполняется и сдвиг влево и сдвиг вправо, поэтому появляется возможность сэкономить – осуществлять фактический сдвиг на разность соответствующих величин с дополнительным маскированием «выпавших» пикселов. Маска получается табличным способом, поэтому практически не требует дополнительных вычислений, но такой подход позволяет экономить до 7 сдвигов на каждый байт первого символа, что при размере символа 16*24 даёт экономию до 336 сдвигов.


Рисунок 15. Исключение двойного сдвига

Из сдвигового регистра данные скидываются в строчный буфер, который обнуляется перед началом вывода текстового блока. Данные накладываются по или.


Рисунок 16. Заполнение строчного буфера.

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


Рисунок 17. Обработка символов 2’ и 3’.

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

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

Полная версия исходника находится по ссылке, в файле Display.h/Display.cpp. Там же находится откомпилированный пример(Project1.exe). Сам проект под Builder 6



  class tShift_register Symbol_buffer;
  vector< tShift_register > Line_buffer;

  // Значения всех этих переменных задаются на вышележащем уровне
  int Start_line, End_line;
  int Left_shift, Current_shift, Current_byte;
  // Указатель на массив шириной Width байтов - битовое поле выводимого символа
  u1x * Symbol_array;
  int Width;
  int bytes_Width;
  //Может на байт превосходить bytes_Width, в зависимости от величины сдвига
  int bytes_Width_after_shift;

  inline void Out_symbol ()
  {

    for(int Current_line = Start_line; Current_line <= End_line; Current_line++)
    {

      Symbol_buffer.Clear();

      ////////////////////////
      // Для типов 2 и 3 используется один метод Out_symbol, это фактически выбор типа символа
      if(Left_shift)// тип 2
      {
        
        // Если сдвиг больше чем 8 бит имеет смысл заменить его сдвигом на целый байт
        int Start_symbol_byte = Left_shift >> 3;

        // void tShift_register::Load(int Start_index_in_destination, u1x * Source, int Width);
        Symbol_buffer.Load(0,Symbol_array + bytes_Width * Current_line + Start_symbol_byte,\
                                                                  bytes_Width - Start_symbol_byte);

        // рис.15
        // void tShift_register::Shift(int Start, int End, int Amount);
        Symbol_buffer.Shift (0, bytes_Width_after_shift,   Current_shift - (Left_shift & 7)  );
        Symbol_buffer[0] &= Masks_array__left_for_line_buffer[ Current_shift ];

        // рис.16
        Line_buffer[Current_line].Or(Current_byte, &Symbol_buffer[0], bytes_Width_after_shift );

      }
      else // тип 3
      {
       
        Symbol_buffer.Load(0,Symbol_array + bytes_Width * Current_line, bytes_Width);

        // рис.14
        Symbol_buffer.Shift(0, bytes_Width_after_shift, Current_shift);

        // рис.16
        Line_buffer[Current_line].Or(Current_byte, &Symbol_buffer[0], bytes_Width_after_shift );

      }

    }// for(int Current_line = Start_line, Current_line <= End_line, Current_line++)

  }// inline void Out_symbol ()

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


Рисунок 18. Диаграмма состояний – хорошая альтернатива классическим блок схемам.

По алгоритму записанному этой диаграммой состояний несложно составить программный код, состоящий из классических структурных конструкций — циклов и ветвлений. Это оказывается несложно в частности потому что этот направленный граф, в общем, не циклический ( то есть не содержит переходы в предыдущие состояния). Исключение составляет пара петель (состояния Тип 1 и Тип 3), которые легко разворачиваются в циклы. Однако даже для тех автоматов, которые описываются более сложным графом с циклическими путями обхода узлов, тоже можно написать структурную программу, хотя на первый взгляд эта задача может показаться трудноразрешимой и громоздкой. Прошу акцентировать внимание: поскольку диаграмма состояний имеет дело с абсолютно идентичными с точки зрения программного процесса блоками-состояниями, связи между которыми ясно и однозначно определены, это позволяет использовать для перехода между состояниями оператор goto, не нарушая структурности языка. То есть, если оператор goto будет использован для совершения переходов между состояниями автомата, это само по себе новая структура, такая же как циклы и ветвления. Эта структура называется переход между состояниями, и она обогащает инструментарий программирования. В то же время, следует понимать, что использование оператора goto вне рамок этой программной структуры по-прежнему остаётся неструктурным, т.е. ломающим стандартные структуры из которых строится программный код. Это важный момент. Возможно, потребуется время, чтобы люди привыкли и перестали бояться, но хочется верить, что новая структурная конструкция займёт достойное место в инструментарии программистов.

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



  class tShift_register Symbol_buffer;
  vector< tShift_register > Line_buffer;
  tVideomemory Videomemory;
  
  // Значения этих переменных задаются на вышележащем уровне
  int Start_line, End_line;
  // НАЧАЛЬНЫЕ значения этих переменных задаются на вышележащем уровне
  int Left_shift, Current_shift, Current_byte;

  // Значения этих переменных задаются на вышележащем уровне
  u1x * Text;
  u1x * Text_end;
  tFont * Current_font;

  // Эти переменные полностью контролируются функцией Out_text_block 
  // Указатель на массив шириной Width байтов - битовое поле выводимого символа
  u1x * Symbol_array;
  int Width;
  int bytes_Width;
  //Может на байт превосходить bytes_Width, в зависимости от величины сдвига
  int bytes_Width_after_shift;

  // Значения этих переменных задаются на вышележащем уровне
  // Полная длина текста в пикселах
  int Line_width;


  ////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////
  inline void Out_text_block ()
  {

    Clear_line_buffer();

  ////////////////////////////////////////
  // Далее всё как показано в диаграмме состояний
  Type_1:

    // Пока не конец строки
    while(Text < Text_end)
    {

      Width = Current_font->Width_for(*Text);
      
      // Прокручиваем символы пока не попадём в область отображения
      if(Left_shift >= Width)
      {
        Left_shift  -= Width;
        Text++;
      }
      else
      
        goto Type_2;

    }// while(Text < Text_end)

    // Конец строки
    return;

  ////////////////////////////////////////
  Type_2:

    // Инициализируем
    Current_byte  = Current_shift >> 3;
    Current_shift = Current_shift & 7;

    Symbol_array = Current_font->Image_for(*Text);
    bytes_Width  = (Width + 7) >> 3;
    bytes_Width_after_shift  = (Width + Current_shift + 7) >> 3;

    Line_width     -= (Width - Left_shift);

    // Достигли границы?
    if(Line_width <= 0)
    {

      Width -= Left_shift;
    
      goto Type_4;

    }

    Out_symbol();
    
    // Компенсируем Left_shift
    Width -= Left_shift;
    Left_shift   =  0;

    // Любой следующий символ
    Text++;

  ////////////////////////////////////////
  Type_3:

    // Конец строки?
    while(Text < Text_end)
    {

      //Сдвигаем Current_byte, Current_shift на величину уже выведенного символа.
      Current_shift += Width;
      Current_byte  += (Current_shift >> 3);
      Current_shift =  Current_shift & 0x7;

      // Прокручиваем на следующий символ
      Width        = Current_font->Width_for(*Text);
      Symbol_array = Current_font->Image_for(*Text);
      bytes_Width  = (Width + 7) >> 3;
      bytes_Width_after_shift  = (Width + Current_shift + 7) >> 3;

      Line_width     -= Width;

      // Достигли границы?
      if(Line_width <= 0)

        goto Type_4;

      Out_symbol();

      Text++;

    }// while(*Text < Text_end)

    Current_shift += Width;
    Current_byte  += (Current_shift >> 3);
    Current_shift =  Current_shift & 0x7;

    // Конец строки
    goto Finalize;

  ////////////////////////////////////////
  // фактически типа 4 нет это символы тип 2' и 3'   
Type_4:

    Out_symbol();

    Current_shift += (Width + Line_width);
    Current_byte  += (Current_shift >> 3);
    Current_shift =  Current_shift & 0x7;

    for(int Current_line = Start_line; Current_line <= End_line; Current_line++)
    {

      Line_buffer[Current_line][Current_byte] &= Masks_array__right_for_line_buffer[Current_shift];

    }

Finalize:

    Out_line_buffer_in_videomemory();

    return;

  }// inline void Out_text_block ()


Этот алгоритм легко и органично, буквально 1 в 1, реализуется и на ассемблере.

Такие величины как Start_line, End_line, начальные значения Left_shift, Current_shift, Current_byte задаются на этапе инициализации процесса вывода блока. Это происходит ещё в автомате разбиения на блоки. Рассмотрим как это происходит. Напомню, что одна строка может выводиться не единственным блоком а несколькими, поэтому при выводе каждого блока мы имеем дело с параметрами показанными на рис.8.

Координаты каждого текстового блока (x,y) могут задаваться индивидуально (esc последовательности, однако есть и различие – координаты курсора задаются не в знакоместах а в пикселах). Они отсчитываются относительно координат окна вывода (x0,y0,x1,y1). Смещение x_shift, y_shift влияет на координаты каждого текстового блока. Все текстовые блоки целиком попадающие в окно вывода не обрезаются даже если задано отрицательное смещение.Обрезается только то, что не попадает в окно вывода, т.е. отрицательное смещение само по себе не критерий обрезки текстовых блоков. Для реализации описанного поведения вывод каждого текстового блока сопровождается следующими преобразованиями.

Вычисление параметров по горизонтали иллюстрирует рис. 19


Рис 19. Пояснение к вычислению параметров по горизонтали

Значение x_shift не показано, оно компенсируется добавлением к x. Параметры x_byte и Start_shift используются во время вывода в видеопамять, который осуществляется по схеме показанной на рис. 20


Рисунок 20. Вывод в видеопамять.

Процесс вывода очевиден, но не будут излишними пояснения:

  • Если левый байт пересекающийся, то читается соответствующий байт видеопамяти, маскируется и накладывается на крайний левый байт строчного буфера (который уже и так замаскирован в процессе вывода в строчный буфер).
  • Если правый крайний байт пересекающийся то читается соответствующий байт видеопамяти, маскируется, после чего накладывается на крайний правый байт строчного буфера (который при этом маскируется дополняющей маской).
  • После этого весь строчный буфер потоком копируется в видеопамять, причём строка Start_line строчного буфера идёт в строку y буфера, и так далее вплоть до End_line и y + End_line — Start_line, соответственно, как это показано на рис 20.

Параметры Start_line и End_line определяются исходя из соображений показанных на
рис. 21.


Рис. 21. Определение параметров по вертикали.

Приведённым на чертежах рис 20, рис 21 преобразованиям соответствует алгоритм



    // Стартовая инициализация 

    // Проверяем корректность параметров окна
    ////////////////////////////////////////////////////////////////////////////////////
    if(x1 < x0)
    {
       int temp = x0;
       x0 = x1;
       x1 = temp;
    }

    if(y1 < y0)
    {
       int temp = y0;
       y0 = y1;
       y1 = temp;
    }

    if(x0 < 0)
    {
      x_shift += x0;
      x0 = 0;
    }

    if(y0 < 0)
    {
      y_shift += y0;
      y0 = 0;
    }

    if(x1 > x_max)
    {
      x1 = x_max;
    }

    if(y1 > y_max)
    {
      y1 = y_max;
    }
 
// для каждого блока
inline bool Init_text_block()
{
    // Компенсируем сдвиг
    ////////////////////////////////////////////////////////////////////////////////////
    x += ( x0 + x_shift);
    y += ( y0 + y_shift);

    // по горизонтали
    ////////////////////////////////////////////////////////////////////////////////////
    if (x < x0)
    {
      Left_shift = x - x0;
      x = x0;
    }
    else
    {
      Left_shift = 0;
    }

    if(x >= x1)

      return false;

    x_byte =  x >> 3;
    Start_shift = Current_shift = x & 7; 
    Current_byte = 0;

    Line_width = x1-x;

    // по вертикали
    ////////////////////////////////////////////////////////////////////////////////////
    if (y < y0)
    {
      Start_line = y0 - y;
      y = y0;
    }
    else

      Start_line = 0;

    if(Start_line >= Current_font->Height())

      return false;

    if( (Current_font->Height() - Start_line) < ( y1 - y) )

      End_line = Current_font->Height() - 1;

    else

      End_line = Start_line + (y1 - y) - 1;

    return true;

}

Следует добавить, что фактически выводится только содержимое текстового блока, а не всего окна вывода, ограниченного координатами x0,y0,x1,y1. При необходимости можно предварительно очищать всё окно отдельно.

Автомат разбивки исходного текста на блоки.

Диаграмма состояний и переходов уже была приведена на рис.7 и по ней несложно составить следующий алгоритм.


  ///////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////
  // Описана выше
  void Out_text_block ();

  inline void Control_processing ();
  

  void Out_text (int arg_x0, int arg_y0,
                 int arg_x1, int arg_y1,
                 int arg_x_shift, int arg_y_shift,
                 unsigned char * argText)
  {


    // Инициализация
    // ...

    while(*Text_end)
    {


//////////////////////////////////////
state__Inside_text_block:

      while(1)
      {

        switch(*Text_end)
        {

		  // Пока только два кода в следующей части рассмотрим этот вопрос подробнее
          case '\0':
          case '\n':

            goto state__Out_text_block;

        } 

        Text_end++;

      }


//////////////////////////////////////
state__Out_text_block:

      if( (Text_begin != Text_end) && Init_text_block())

        Out_text_block();

      Text_begin = Text_end;

//////////////////////////////////////
state__Control_processing:

      if(*Text_end == 0)

        return;

      // Стоит выделить в отдельную функцию
      Control_processing();

    }//while(*Text_end)

  }//void Out_text (int arg_x0, int arg_y0,

Так в общих чертах выглядит решение задачи. По ссылке вы можете увидеть исходники и рабочую версию программы(файл Project1.exe). Единственный вопрос, который остался не раскрыт – структура функции Control_processing, которая разбирает esc-последовательности и выполняет команды. В её основе лежит ещё один тип автоматов, который заметно отличается от рассмотренных выше, но которые в то же время классика программных автоматов – символьные автоматы. Мы рассмотрим реализацию функции и автомата в следующей части и там же проведём обзор преимуществ автоматного подхода.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331556/


Метки:  

Автоэнкодеры в Keras, Часть 3: Вариационные автоэнкодеры (VAE)

Суббота, 24 Июня 2017 г. 16:04 + в цитатник

Содержание



В прошлой части мы уже обсуждали, что такое скрытые переменные, взглянули на их распределение, а также поняли, что из распределения скрытых переменных в обычных автоэнкодерах сложно генерировать новые объекты. Для того чтобы можно было генерировать новые объекты, пространство скрытых переменных (latent variables) должно быть предсказуемым.

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



Иллюстрация из [2]

Имея какое-то одно распределение Z, можно получить произвольное другое X = g(Z), например, пусть Z — обычное нормальное распределение, g(Z) = \frac{Z}{|Z|}+ \frac{Z}{10} — тоже случайное распределение, но выглядит совсем по-другому

Код
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns


Z = np.random.randn(150, 2)
X = Z/(np.sqrt(np.sum(Z*Z, axis=1))[:, None]) + Z/10

fig, axs = plt.subplots(1, 2, sharex=False, figsize=(16,8))

ax = axs[0]
ax.scatter(Z[:,0], Z[:,1])
ax.grid(True)
ax.set_xlim(-5, 5)
ax.set_ylim(-5, 5)

ax = axs[1]
ax.scatter(X[:,0], X[:,1])
ax.grid(True)
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)




Пример выше из [1]

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

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



Ниже непростая, но необходимая теория лежащая в основе VAE. Постарался выжать из [1, Tutorial on Variational Autoencoders, Carl Doersch, 2016] все самое принципиальное, остановившись подробнее на тех местах, которые показались сложными мне самому.

Пусть Z — скрытые переменные, а X — данные. На примере нарисованных цифр рассмотрим естественный генеративный процесс, который сгенерировал нашу выборку:

P(X) = \int_{z} P(X|Z)P(Z)dZ

  • P(X) вероятностное распределение изображений цифр на картинках, т.е. вероятность конкретного изображения цифры в принципе быть нарисованным (если картинка не похожа на цифру, то эта вероятность крайне мала, и наоборот),
  • P(Z) — вероятностное распределение скрытых факторов, например, распределение толщины штриха,
  • P(X|Z) — распределение вероятности картинок при заданных скрытых факторах, одни и те же факторы могут привести к разным картинкам (один и тот же человек в одних и тех же условиях не рисует абсолютно одинаковые цифры).

Представим P(X|Z) как сумму некоторой генерирующей функции f(Z) и некоторого сложного шума \epsilon

P(X|Z) = f(Z) + \epsilon

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

P(X;\theta) = \int_{z} P(X|Z;\theta)P(Z)dZ \ \ \ (1)

и снова

P(X|Z;\theta) = f(Z;\theta) + \epsilon

f(Z;\theta) — некоторое семейство функций, которое представляет наша модель, а \theta — ее параметры. Выбирая метрику, мы выбираем то, какого вида нам представляется шум \epsilon. Если метрика L_2, то мы считаем шум нормальным и тогда:

P(X|Z;\theta) = N(X|f(Z;\theta), \sigma^2 I),

По принципу максимального правдоподобия нам остается оптимизировать параметры \theta, для того чтобы максимизировать P(X), т.е. вероятность появления объектов из выборки.

Проблема в том, что оптимизировать интеграл (1) напрямую мы не можем: пространство может быть высокоразмерное, объектов много, да и метрика плохая. С другой стороны, если задуматься, то к каждому конкретному X может привести лишь очень небольшое подмножество Z, для остальных же P(X|Z) будет очень близок к нулю.
И при оптимизации достаточно сэмплить только из хороших Z.

Для того чтобы знать, из каких Z нам надо сэмплить, введем новое распределение Q(Z|X), которое в зависимости от X будет показывать распределение Z \sim Q, которое могло привести к этому X.

Запишем сперва расстояние Кульбака-Лейблера (несимметричная мера «похожести» двух распределений, подробнее [3] ) между
Q(Z|X) и реальным P(Z|X):

KL[Q(Z|X)||P(Z|X)] = \mathbb{E}_{Z \sim Q}[\log Q(Z|X) - \log P(Z|X)]

Применяем формулу Байеса:

KL[Q(Z|X)||P(Z|X)] = \mathbb{E}_{Z \sim Q}[\log Q(Z|X) - \log P(X|Z) - \log P(Z)] + \log P(X)

Выделяем еще одно расстояние Кульбака-Лейблера:

KL[Q(Z|X)||P(Z|X)] = KL[Q(Z|X)||\log P(Z)] - \mathbb{E}_{Z \sim Q}[\log P(X|Z)] + \log P(X)

В итоге получаем тождество:



\log P(X) - KL[Q(Z|X)||P(Z|X)] = \mathbb{E}_{Z \sim Q}[\log P(X|Z)] - KL[Q(Z|X)||P(Z)]



Это тождество — краеугольный камень вариационных автоэнкодеров, оно верно для любых Q(Z|X) и P(X,Z).

Пусть Q(Z|X) и P(X|Z) зависят от параметров: Q(Z|X;\theta_1) и P(X|Z;\theta_2), а P(Z) — нормальное N(0,I), тогда получаем:

\log P(X;\theta_2) - KL[Q(Z|X;\theta_1)||P(Z|X;\theta_2)] = \mathbb{E}_{Z \sim Q}[\log P(X|Z;\theta_2)] - KL[Q(Z|X;\theta_1)||N(0,I)]

Взглянем повнимательнее на то, что у нас получилось:

  • во-первых, Q(Z|X;\theta_1), P(X|Z;\theta_2) подозрительно похожи на энкодер и декодер (точнее декодер это f в выражении P(X|Z;\theta_2) = f(Z;\theta_2) + \epsilon),
  • слева в тождестве — значение, которое мы хотим максимизировать для элементов нашей тренировочной выборки X + некоторая ошибка KL \ (KL(x,y) \ge 0 \ \ \forall x,y), которая, будем надеяться, при достаточной емкости Q уйдет в 0,
  • справа значение, которое мы можем оптимизировать градиентным спуском, где первый член имеет смысл качества предсказания X декодером по значениям Z, а второй член, это расстояние К-Л между распределением Z \sim Q, которое предсказывает энкодер для конкретного X, и распределением Z для всех X сразу.

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

1. Точнее определим что такое Q(Z|X;\theta_1)


Обычно Q выбирается нормальным распределением:

Q(Z|X;\theta_1) = N(\mu(X;\theta_1), \Sigma(X;\theta_1))

То есть энкодер для каждого X предсказывает 2 значения: среднее \mu и вариацию \Sigma нормального распределения, из которого уже сэмплируются значения. Работает это все примерно вот так:



Иллюстрация из [2]

При том, что для каждой отдельной точки данных X энкодер предсказывает некоторое нормальное распределение

P(Z|X) = N(\mu(X), \Sigma(X))

для маргинального распределения X: P(Z) = N(0, I), что получается из формулы, и это потрясающе.



Иллюстрация из [2]

При этом KL[Q(Z|X;\theta_1)||N(0,I)] принимает вид:

KL[Q(Z|X;\theta_1)||N(0,I)] = \frac{1}{2}\left(tr(\Sigma(X)) + \mu(X)^T\mu(X) - k - \log \det \Sigma(X) \right)

2. Разберемся с тем, как распространять ошибки через \mathbb{E}_{Z \sim Q}[\log P(X|Z;\theta_2)]


Дело в том, что здесь мы берем случайные значения Z \sim Q(Z|X;\theta_1) и передаем их в декодер.
Ясно, что распространять ошибки через случайные значения напрямую нельзя, поэтому используется так называемый трюк с репараметризацией (reparametrization trick).

Схема получается вот такая:


Иллюстрация из [1]

Здесь на левой картинке схема без трюка, а на правой с трюком.
Красным цветом показано семплирование, а синим вычисление ошибки.
То есть по сути просто берем предсказанное энкодером стандартное отклонение \Sigma умножаем на случайное число из N(0,I) и добавляем предсказанное среднее \mu.
Прямое распространение на обеих схемах абсолютно одинаковое, однако на правой схеме работает обратное распространение ошибки.

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


Иллюстрация из [2]

Иллюстрация из [1]

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

VAE в Keras


Теперь, когда мы разобрались в том, что такое вариационные автоэнкодеры, напишем такой на Keras.

Импортируем необходимые библиотеки и датасет:

import sys
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns

from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

x_train = x_train.astype('float32') / 255.
x_test  = x_test .astype('float32') / 255.
x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
x_test  = np.reshape(x_test,  (len(x_test),  28, 28, 1))

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

batch_size = 500
latent_dim = 2
dropout_rate = 0.3
start_lr = 0.0001


Напишем модели вариационного автоэнкодера.

Для того чтобы обучение происходило быстрее и более качественно, добавим слои dropout и batch normalization.

А в декодере используем в качестве активации leaky ReLU, которую добавляем отдельным слоем после dense слоев без активации.
Функция sampling реализует сэмплирование значений Z из Q(Z|X) с использованием трюка репараметризации.

vae_loss это правая часть из уравнения:
\log P(X;\theta_2) - KL[Q(Z|X;\theta_1)||P(Z|X;\theta_2)] = \mathbb{E}_{Z \sim Q}[\log P(X|Z;\theta_2)] - \left(\frac{1}{2}\left(tr(\Sigma(X)) + \mu(X)^T\mu(X) - k - \log \det \Sigma(X) \right)\right)
далее будет использоваться в качестве лосса.

from keras.layers import Input, Dense 
from keras.layers import BatchNormalization, Dropout, Flatten, Reshape, Lambda
from keras.models import Model

from keras.objectives import binary_crossentropy
from keras.layers.advanced_activations import LeakyReLU
from keras import backend as K

def create_vae():
    models = {}

    # Добавим Dropout и BatchNormalization
    def apply_bn_and_dropout(x):
        return Dropout(dropout_rate)(BatchNormalization()(x))

    # Энкодер
    input_img = Input(batch_shape=(batch_size, 28, 28, 1))
    x = Flatten()(input_img)
    x = Dense(256, activation='relu')(x)
    x = apply_bn_and_dropout(x)
    x = Dense(128, activation='relu')(x)
    x = apply_bn_and_dropout(x)

    # Предсказываем параметры распределений
    # Вместо того, чтобы предсказывать стандартное отклонение, предсказываем логарифм вариации
    z_mean = Dense(latent_dim)(x)
    z_log_var = Dense(latent_dim)(x)

    # Сэмплирование из Q с трюком репараметризации
    def sampling(args):
        z_mean, z_log_var = args
        epsilon = K.random_normal(shape=(batch_size, latent_dim), mean=0., stddev=1.0)
        return z_mean + K.exp(z_log_var / 2) * epsilon
    l = Lambda(sampling, output_shape=(latent_dim,))([z_mean, z_log_var])

    models["encoder"]  = Model(input_img, l, 'Encoder') 
    models["z_meaner"] = Model(input_img, z_mean, 'Enc_z_mean')
    models["z_lvarer"] = Model(input_img, z_log_var, 'Enc_z_log_var')

    # Декодер
    z = Input(shape=(latent_dim, ))
    x = Dense(128)(z)
    x = LeakyReLU()(x)
    x = apply_bn_and_dropout(x)
    x = Dense(256)(x)
    x = LeakyReLU()(x)
    x = apply_bn_and_dropout(x)
    x = Dense(28*28, activation='sigmoid')(x)
    decoded = Reshape((28, 28, 1))(x)

    models["decoder"] = Model(z, decoded, name='Decoder')
    models["vae"]     = Model(input_img, models["decoder"](models["encoder"](input_img)), name="VAE")

    def vae_loss(x, decoded):
        x = K.reshape(x, shape=(batch_size, 28*28))
        decoded = K.reshape(decoded, shape=(batch_size, 28*28))
        xent_loss = 28*28*binary_crossentropy(x, decoded)
        kl_loss = -0.5 * K.sum(1 + z_log_var - K.square(z_mean) - K.exp(z_log_var), axis=-1)
        return (xent_loss + kl_loss)/2/28/28

    return models, vae_loss

models, vae_loss = create_vae()
vae = models["vae"]

Замечание: мы использовали Lambda-слой с функцией, сэмплирующей из N(0, I) из нижележащего фреймворка, которая явно требует размер батча. Во всех моделях, в которых присутствует этот слой, мы теперь вынуждены передавать именно такой размер батча (то есть в encoder и vae).

Функцией оптимизации возьмем Adam или RMSprop, обе показывают хорошие результаты.

from keras.optimizers import Adam, RMSprop

vae.compile(optimizer=Adam(start_lr), loss=vae_loss)


Код рисования рядов цифр и цифр из многообразия

Код
digit_size = 28

def plot_digits(*args, invert_colors=False):
    args = [x.squeeze() for x in args]
    n = min([x.shape[0] for x in args])
    figure = np.zeros((digit_size * len(args), digit_size * n))

    for i in range(n):
        for j in range(len(args)):
            figure[j * digit_size: (j + 1) * digit_size,
                   i * digit_size: (i + 1) * digit_size] = args[j][i].squeeze()

    if invert_colors:
        figure = 1-figure

    plt.figure(figsize=(2*n, 2*len(args)))
    plt.imshow(figure, cmap='Greys_r')
    plt.grid(False)
    ax = plt.gca()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
    plt.show()

n = 15 # Картинка с 15x15 цифр
digit_size = 28

from scipy.stats import norm
# Так как сэмплируем из N(0, I), то сетку узлов, в которых генерируем цифры берем из обратной функции распределения
grid_x = norm.ppf(np.linspace(0.05, 0.95, n))
grid_y = norm.ppf(np.linspace(0.05, 0.95, n))

def draw_manifold(generator, show=True):
    # Рисование цифр из многообразия
    figure = np.zeros((digit_size * n, digit_size * n))
    for i, yi in enumerate(grid_x):
        for j, xi in enumerate(grid_y):
            z_sample = np.zeros((1, latent_dim))
            z_sample[:, :2] = np.array([[xi, yi]])

            x_decoded = generator.predict(z_sample)
            digit = x_decoded[0].squeeze()
            figure[i * digit_size: (i + 1) * digit_size,
                   j * digit_size: (j + 1) * digit_size] = digit
    if show:
        # Визуализация
        plt.figure(figsize=(15, 15))
        plt.imshow(figure, cmap='Greys_r')
        plt.grid(None)
        ax = plt.gca()
        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)        
        plt.show()
    return figure


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

Для этого в keras есть коллбэки, которые передаются в метод fit перед началом обучения. Например, чтобы влиять на learning rate в процессе обучения, есть такие коллбэки, как LearningRateScheduler, ReduceLROnPlateau, чтобы сохранять модель — ModelCheckpoint.

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

Для случая, когда требуется выполнение произвольных функций в процессе обучения, существует LambdaCallback. Он запускает выполнение произвольных функций в заданные моменты обучения, например, между эпохами или батчами.
Будем следить за процессом обучения, изучая, как генерируются цифры из N(0, I).

from IPython.display import clear_output
from keras.callbacks import LambdaCallback, ReduceLROnPlateau, TensorBoard

# Массивы, в которые будем сохранять результаты, для последующей визуализации
figs = []
latent_distrs = []
epochs = []

# Эпохи, в которые будем сохранять
save_epochs = set(list((np.arange(0, 59)**1.701).astype(np.int)) + list(range(10)))

# Отслеживать будем на вот этих цифрах
imgs = x_test[:batch_size]
n_compare = 10

# Модели
generator      = models["decoder"]
encoder_mean   = models["z_meaner"]

# Функция, которую будем запускать после каждой эпохи
def on_epoch_end(epoch, logs):
    if epoch in save_epochs:
        clear_output() # Не захламляем output

        # Сравнение реальных и декодированных цифр
        decoded = vae.predict(imgs, batch_size=batch_size)
        plot_digits(imgs[:n_compare], decoded[:n_compare])

        # Рисование многообразия
        figure = draw_manifold(generator, show=True)

        # Сохранение многообразия и распределения z для создания анимации после
        epochs.append(epoch)
        figs.append(figure)
        latent_distrs.append(encoder_mean.predict(x_test, batch_size))
        
# Коллбэки
pltfig = LambdaCallback(on_epoch_end=on_epoch_end)
# lr_red = ReduceLROnPlateau(factor=0.1, patience=25)
tb     = TensorBoard(log_dir='./logs')

# Запуск обучения 
vae.fit(x_train, x_train, shuffle=True, epochs=1000,
        batch_size=batch_size,
        validation_data=(x_test, x_test),
        callbacks=[pltfig, tb],
        verbose=1)

Теперь, если установлен TensorBoard, можно следить за процессом обучения.

Вот как этот энкодер восстанавливает изображения:



А вот результат сэмплирования из N(0|I)



Вот так выглядит процесс обучения генерации цифр:

Гифка


Распределение кодов в скрытом пространстве:

Гифка


Не идеально нормальное, но довольно близко (особенно, учитывая, что размерность скрытого пространства всего 2).

Кривая обучения в TensorBoard


Код создания гифок
from matplotlib.animation import FuncAnimation
from matplotlib import cm
import matplotlib

def make_2d_figs_gif(figs, epochs, fname, fig): 
    norm = matplotlib.colors.Normalize(vmin=0, vmax=1, clip=False)
    im = plt.imshow(np.zeros((28,28)), cmap='Greys_r', norm=norm)
    plt.grid(None)
    plt.title("Epoch: " + str(epochs[0]))

    def update(i):
        im.set_array(figs[i])
        im.axes.set_title("Epoch: " + str(epochs[i]))
        im.axes.get_xaxis().set_visible(False)
        im.axes.get_yaxis().set_visible(False)
        return im
    
    anim = FuncAnimation(fig, update, frames=range(len(figs)), interval=100)
    anim.save(fname, dpi=80, writer='imagemagick')

def make_2d_scatter_gif(zs, epochs, c, fname, fig):
    im = plt.scatter(zs[0][:, 0], zs[0][:, 1], c=c, cmap=cm.coolwarm)
    plt.colorbar()
    plt.title("Epoch: " + str(epochs[0]))

    def update(i):
        fig.clear()
        im = plt.scatter(zs[i][:, 0], zs[i][:, 1], c=c, cmap=cm.coolwarm)
        im.axes.set_title("Epoch: " + str(epochs[i]))
        im.axes.set_xlim(-5, 5)
        im.axes.set_ylim(-5, 5)
        return im

    anim = FuncAnimation(fig, update, frames=range(len(zs)), interval=150)
    anim.save(fname, dpi=80, writer='imagemagick')
    
make_2d_figs_gif(figs, epochs, "./figs3/manifold.gif", plt.figure(figsize=(10,10)))
make_2d_scatter_gif(latent_distrs, epochs, y_test, "./figs3/z_distr.gif", plt.figure(figsize=(10,10)))


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

Полезные ссылки и литература


Теоретическая часть основана на статье:
[1] Tutorial on Variational Autoencoders, Carl Doersch, 2016, https://arxiv.org/abs/1606.05908
и фактически является ее кратким изложением

Многие картинки взяты из блога Isaac Dykeman:
[2] Isaac Dykeman, http://ijdykeman.github.io/ml/2016/12/21/cvae.html

Подробнее прочитать про расстояние Кульбака-Лейблера на русском можно здесь:
[3] http://www.machinelearning.ru/wiki/images/d/d0/BMMO11_6.pdf

Код частично основан на статье Francois Chollet:
[4] https://blog.keras.io/building-autoencoders-in-keras.html

Другие интересные ссылки:
http://blog.fastforwardlabs.com/2016/08/12/introducing-variational-autoencoders-in-prose-and.html
http://kvfrans.com/variational-autoencoders-explained/
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331552/


Метки:  

[Перевод] Connect: советы по современному фронтенду

Суббота, 24 Июня 2017 г. 15:36 + в цитатник
Мы недавно выпустили новую и улучшенную версию Connect, нашего набора инструментов для платформ и магазинов. Группа дизайна Stripe много работала для создания уникальных посадочных страничек, которые рассказывают историю для наших основных продуктов. К релизу мы подготовили посадочную страничку Connect, чтобы отразить эти замысловатые, передовые возможности, но в то же время не утратив ясности и простоты изложения.

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

CSS Grid Layout


Ранее в этом году три основных браузера (Firefox, Chrome и Safari) почти одновременно выкатили свои реализации нового модуля CSS Grid Layout. Эти спецификации дают разработчикам двухмерную макетную систему, которая проста в использовании и невероятно мощная. Посадочная страница Connect полагается на сетки CSS практически везде, что делает некоторые на первый взгляд хитрые дизайнерские решения банально простыми в реализации. В качестве примера, давайте спрячем содержимое заголовка и сфокусируемся на фоне:



Исторически, мы создавали такие фоновые полосы (страйпы) с помощью абсолютного позиционирования, чтобы точно разместить каждый страйп на странице. Этот способ работает, но хрупкое позиционирование часто ведёт к небольшим проблемкам: например, из-за ошибок округления между полосами может образоваться зазор 1 px. Таблицы стилей к тому же быстро разбухают, и их труднее поддерживать, поскольку медийные запросы усложняются для учёта различий фона на разных размерах экрана.

CSS Grid устраняет практически все эти проблемы. Мы просто определяем гибкую сетку и размещаем страйпы в соответствующие ячейки. В Firefox есть удобный инспектор сеток, который визуализирует структуру вашего макета. Посмотрим, как он выглядит:



Мы выделили три страйпа и убрали эффект наклона для наглядности. Вот как будет выглядеть соответствующий код CSS:

header .stripes {
  display: grid;
  grid: repeat(5, 200px) / repeat(10, 1fr);
}

header .stripes :nth-child(1) {
  grid-column: span 3;
}

header .stripes :nth-child(2) {
  grid-area: 3 / span 3 / auto / -1;
}

header .stripes :nth-child(3) {
  grid-row: 4;
  grid-column: span 5;
}

Затем мы можем просто трансформировать весь контейнер .stripes для получения эффекта наклона:



header .stripes {
  transform: skewY(-12deg);
  transform-origin: 0;
}

И вуаля! CSS Grid может испугать с первого взгляда, поскольку сопровождается необычным синтаксисом и многими новыми свойствами и значениями, но ментальная модель на самом деле очень простая. И если вы знакомы с Flexbox, то уже знаете модуль Box Alignment, а значит, можете здесь тоже использовать знакомые свойства, которые так любите, такие как justify-content и align-items.

CSS 3D


Заголовок целевой страницы показывает несколько кубов как визуальную метаформу строительных блоков, которые составляют Connect. Эти летающие кубы вращаются в 3D на случайных скоростях (в определённом диапазоне) и освещаются одним источником света, который динамически подсвечивает соответствующие поверхности, видео: cubes.mp4



Эти кубы — простые элементы DOM, которые генерируются и анимируются средствами JavaScript. Каждый из них подтверждается одним HTML template:




// JavaScript
const createCube = () => {
  const template = document.getElementById("cube-template");
  const fragment = document.importNode(template.content, true);
  return fragment;
};

Ничего сложного. Теперь мы можем довольно легко превратить эти чистые и пустые элементы в трёхмерную форму. Благодаря 3D-трансформациям добавление перспективы и перемещение сторон вдоль z-осей осуществляется вполне естественным образом:

.cube, .cube * {
  position: absolute;
  width: 100px;
  height: 100px
}

.sides {
  transform-style: preserve-3d;
  perspective: 600px
}

.front  { transform: rotateY(0deg)    translateZ(50px) }
.back   { transform: rotateY(-180deg) translateZ(50px) }
.left   { transform: rotateY(-90deg)  translateZ(50px) }
.right  { transform: rotateY(90deg)   translateZ(50px) }
.top    { transform: rotateX(90deg)   translateZ(50px) }
.bottom { transform: rotateX(-90deg)  translateZ(50px) }

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

  • Видимость. В каждый момент времени видимы не более трёх сторон, так что можно избежать лишних вычислений и ресурсоёмких перекрашиваний на скрытых сторонах.
  • Трансформация. Каждая сторона куба должна быть преобразована в зависимости от своего первоначального вращения, текущего состояния анимации и скорости по каждой оси.
  • Затенение. Хотя CSS позволяет вам позиционировать элементы в трёхмерном пространстве, здесь нет традиционных понятий из 3D-среды (например, источников света). Чтобы имитировать 3D-среду, мы можем рендерить источник света, прогрессивно затемняя стороны куба по мере того, как он удаляется от определённой точки.

Есть и другие соображения, которые нужно принять в расчёт (например, улучшение производительности с помощью requestIdleCallback на JavaScript и backface-visibility на CSS), но это главные основы для логики анимации.

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

const getDistance = (state, rotate) =>
  ["x", "y"].reduce((object, axis) => {
    object[axis] = Math.abs(state[axis] + rotate[axis]);
    return object;
  }, {});

const getRotation = (state, size, rotate) => {
  const axis = rotate.x ? "Z" : "Y";
  const direction = rotate.x > 0 ? -1 : 1;

  return `
    rotateX(${state.x + rotate.x}deg)
    rotate${axis}(${direction * (state.y + rotate.y)}deg)
    translateZ(${size / 2}px)
  `;
};

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

// Linear interpolation between a and b
// Example: (100, 200, .5) = 150
const interpolate = (a, b, i) => a * (1 - i) + b * i;

const getShading = (tint, rotate, distance) => {
  const darken = ["x", "y"].reduce((object, axis) => {
    const delta = distance[axis];
    const ratio = delta / 180;
    object[axis] = delta > 180 ? Math.abs(2 - ratio) : ratio;
    return object;
  }, {});

  if (rotate.x)
    darken.y = 0;
  else {
    const {x} = distance;
    if (x > 90 && x < 270)
      directions.forEach(axis => darken[axis] = 1 - darken[axis]);
  }

  const alpha = (darken.x + darken.y) / 2;
  const blend = (value, index) =>
    Math.round(interpolate(value, tint.shading[index], alpha));

  const [r, g, b] = tint.color.map(blend);
  return `rgb(${r}, ${g}, ${b})`;
};

Фух! К счастью, остальной код гораздо проще и состоит, в основном, из шаблонного кода, DOM-хелперов и других элементарных абстракций. Последняя достойная упоминания деталь — это техника, которая делает анимацию менее навязчивой, в зависимости от настроек пользователя: видео.


Нажмите для просмотра видео

На macOS, когда в настройках включен режим Reduce Motion, сработает триггер на новый медиазапрос prefers-reduced-motion (пока только в Safari), и все декоративные анимации на странице отключатся. Кубы одновременно используют анимации CSS для затенения и анимации JavaScript для вращения. Мы можем отключить эти анимации сочетанием блокировок @media и MediaQueryList Interface:

/* CSS */
@media (prefers-reduced-motion) {
  #header-hero * {
    animation: none
  }
}

// JavaScript
const reduceMotion = matchMedia("(prefers-reduced-motion)").matches;
const tick = () => {
  cubes.forEach(updateSides);
  if (reduceMotion) return;
  requestAnimationFrame(tick);
};

Ещё больше CSS 3D!


По всему сайту мы используем кастомные трёхмерные компьютерные устройства как витрину для клиентов Stripe и имеющихся приложений. В нашем бесконечном квесте по уменьшению размера файлов и времени загрузки мы рассмотрели несколько вариантов, как добиться трёхмерного вида при малом размере файла и независимости от разрешения. Отрисовка устройств напрямую в CSS удовлетворила нашим требованиям. Вот CSS-ноутбук:



Определение объекта в CSS определённо менее удобно, чем экспорт битмапа, но это стоит того. Ноутбук вверху занимает меньше одного килобайта и его легко настраивать. Мы можем добавить аппаратное ускорение, анимировать любую часть, сделать его интерактивным без потери качества изображения и точно позиционировать DOM-элементы (например, другие изображения) на дисплее ноутбука. Такая гибкость не означает, что нужно отказаться чистого кода — разметка остаётся чистой, лаконичной и наглядной:


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

.laptop .lid {
  position: absolute;
  width: 100%;
  height: 100%;
  border-radius: 20px;
  background: linear-gradient(45deg, #E5EBF2, #F3F8FB);
  box-shadow: inset 1px -4px 6px rgba(145, 161, 181, .3)
}

Выбор правильного инструмента для работы не всегда очевиден — выбор между CSS, SVG, Canvas, WebGL и изображениями не такой ясный, каким должен быть. Легко отказаться от CSS как эксклюзивного формата представления документов, но настолько же легко выйти за рамки и излишне использовать его визуальные возможности. Неважно, какую технологию вы выбрали, оптимизируйте её для пользователя! Значит, уделяйте пристальное внимание производительности на стороне клиента, доступности и опциям отката для старых браузеров.

Web Animations API


В разделе Onboarding & Verification представлено демо Express, новой системы адаптации начинающих пользователей Connect. Вся анимация целиком построена на программном коде и в основном полагается на новые Web Animations API.

Web Animations API обеспечивают производительность и простоту @keyframes в JavaScript, позволяя легко создавать плавную последовательность кадров анимации. В отличие от низкоуровневых интерфейсов requestAnimationFrame, здесь вы получаете все прелести анимаций CSS, такие как нативная поддержка смягчающих функций cubic-bezier. В качестве примера, взглянем на наш код для скольжения клавиатуры:

const toggleKeyboard = (element, callback, action) => {
  const keyframes = {
    transform: [100, 0].map(n => `translateY(${n}%)`)
  };

  const options = {
    duration: 800,
    fill: "forwards",
    easing: "cubic-bezier(.2, 1, .2, 1)",
    direction: action == "hide" ? "reverse" : "normal"
  };

  const animation = element.animate(keyframes, options);
  animation.addEventListener("finish", callback, {once: true});
};

Приятно и просто! Web Animations API покрывают абсолютное большинство типичных анимаций пользовательского интерфейса, какие только могут понадобиться, не имея сторонних зависимостей (в результате, вся анимация Express занимает около 5 КБ, включая всё: скрипты, изображения и т. д.). Нужно сказать, что это не полная замена requestAnimationFrame, там всё-таки предоставляется более тонкий контроль над анимацией и допускаются эффекты, которые иначе не получишь, такие как Spring Curve и независимые функции трансформации. Если вы не уверены, какую технологию выбрать для своих анимаций, то вероятно, варианты можно расставить в следующем приоритетном порядке:

  1. Переходы CSS. Это самый быстрый, простой и наиболее эффективный способ анимаций. Подходит для простых вещей вроде эффектов hover.
  2. Анимации CSS. У них такие же характеристики производительности, как у переходов: это декларативные анимации, которые могут быть сильно оптимизированы браузерами и выполняться в отдельных потоках. Анимации CSS более функциональны, чем переходы, и допускают множественные шаги и множественные итерации. Они также более сложны в реализации, поскольку требуют именованных деклараций @keyframes, а часто и явного animation-fill-mode. (А именованные штуки всегда были сложнейшими частями компьютерной науки!)
  3. Web Animations API. Этот программный интерфейс предоставляет почти такую же производительность, как анимации CSS (эти анимации проводит тот же движок, но код JavaScript по-прежнему работает в основном потоке), и их почти так же просто использовать. Они должны быть вашим первым выбором для любой анимации, где нужна интерактивность, случайные эффекты, программируемые последовательности и всё, что функциональнее чисто декларативной анимации.
  4. requestAnimationFrame. Во вселенной нет границ, но вам нужно построить космический корабль. Здесь возможности бесконечны, а методы рендеринга неограничены (HTML, SVG, canvas — что угодно), но эту технологию гораздо сложнее использовать и она может работать не так хорошо, как предыдущие варианты.

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

  • Кастомные кривые. Вряд ли вам захочется использовать встроенные timing-function вроде ease-in, ease-out и linear. Вы сэкономите много времени, если глобально определите количество кастомных переменных cubic-bezier.
  • Производительность. Всеми силами избегайте подтормаживаний в своих анимациях. В CSS для этого следует эксклюзивно анимировать дешёвые свойства (transform и opacity) и сбрасывать анимации на GPU по возможности (применяя will-change).
  • Скорость. Анимации никогда не должны мешать. Основная задача анимаций — сделать интерфейс отзывчивым, гармоничным, приятным и завершённым. Нет жёсткого ограничения на продолжительность анимации, это зависит от эффектов и кривой, но в большинстве случаев вам не стоит превышать 500 секунд.

Intersection Observer


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

Целевая страница Connect использует новый Intersection Observer API, который обеспечивает намного, намного более надёжный и производительный способ определять видимость элемента. Вот как мы начинаем воспроизводить анимацию Express:

const observeScroll = (element, callback) => {
  const observer = new IntersectionObserver(([entry]) => {
    if (entry.intersectionRatio < 1) return;
    callback();

    // Stop watching the element
    observer.disconnect();
  },{
    threshold: 1
  });

  // Start watching the element
  observer.observe(element);
};

const element = document.getElementById("express-animation");
observeScroll(element, startAnimation);

Хелпер observeScroll упрощает нам детектирование (например, когда элемент полностью видим, то обратный вызов генерируется лишь однажды) без выполнения какого-либо кода в основном потоке. Благодаря Intersection Observer API мы теперь на шаг ближе к абсолютно плавным веб-страницам!

Полифиллы и откаты


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

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

const insert = name => {
  const el = document.createElement("script");
  el.src = `${name}.js`;
  el.async = false; // Keep the execution order
  document.head.appendChild(el);
};

const scripts = ["main"];

if (!Element.prototype.animate)
  scripts.unshift("web-animations-polyfill");

if (!("IntersectionObserver" in window))
  scripts.unshift("intersection-observer-polyfill");

scripts.forEach(insert);

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

div { display: flex }

@supports (display: grid) {
  div { display: grid }
}

Запросы функций CSS простые, надёжные и, скорее всего, их следует использовать в первую очередь. Однако они не подходят для нашей аудитории, потому что около 90% наших посетителей уже используют Grid-совместимый браузер. В нашем случае нет смысла штрафовать абсолютное большинство посетителей сотнями правил отката ради маленькой и уменьшающейся доли браузеров. С учётом этой статистики, мы выбрали динамическое создание и вставку таблицы стилей с откатом, когда необходимо:

// Some browsers not supporting Grid don’t support CSS.supports
// either, so we need to feature-test it the old-fashioned way:

if (!("grid" in document.body.style)) {
  const fallback = "";
  document.head.insertAdjacentHTML("beforeend", fallback);
}

Финиш!


Надеемся, вы получили удовольствие (а может и пользу) от этих советов по фронтенду! Современные браузеры дают нам мощные инструменты для создания насыщенных, быстрых и привлекательных интерфейсов, позволяя проявить креативность. Если вы настолько же восхищены открывающимися возможностями, как и мы, вероятно, нам стоит поэкспериментировать вместе.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331548/


Метки:  

[Из песочницы] Установка ArchLinux ARM рядом с Android без chroot

Суббота, 24 Июня 2017 г. 14:59 + в цитатник
Я испробовал множество средств для установки Linux на свое Android устройство, но все они или не работали вовсе, или были слишком глючные. К счастью я использую на ПК ArchLinux и узнав о проекте ArchLinux ARM решил попробовать его в деле. И не просто установить в chroot, а заставить его работать и без него.

Нам потребуется


  • Прямые руки
  • Android устройство
  • Root доступ
  • Busybox
  • Эмулятор терминала
  • Свободное место
  • ADB(для удобства)

Все действия Вы выполняете на свой страх и риск.


Я использовал


  • Устройство Android 4.2 с 512Мб ОЗУ, ядро Linux 3.4.5 armv7l
  • Эмулятор терминала ConnectBot
  • Управление суперпольпользователем SuperSU
  • BusyBox v1.20.0

Часть 1: Подготовка


1. Скачаем архив с ArchLinux ARM с зеркала:

wget http://mirror.yandex.ru/archlinux-arm/os/ArchLinuxARM-armv7-latest.tar.gz
mv ArchLinuxARM-armv7-latest.tar.gz ArchLinuxARM.tar.gz
adb push ArchLinuxARM.tar.gz /sdcard/

Дальнейшие действия необходимо проделывать на Android устройстве

2. Создаем файл для будущего образа с помощью make_ext4fs.

Если у вас есть отдельный раздел на карте памяти — желательно использовать его. В моем случае 16Гб SD карта была забита важными данными и возможности сдвинуть главный FAT раздел не было.

В зависимости от настроек /sdcard может быть как внешней, так и внутренней картой памяти.

cd sdcard
make_ext4fs -l 3221225472 arch.img

3221225472 это 1024*1024*1024*3, следовательно будет создан образ в 3Гб. Размер образа определите для себя по вкусу. Помните что на FAT32 нельзя создать файл больше 4Гб

3. Примонтируем образ и распакуем файлы ArchLinux ARM

mount -o rw,remount /
mkdir /arch
busybox mount /sdcard/arch.img /arch
tar -xvf ArchLinuxARM.tar.gz -C /arch/

4. Несмотря на то, что наша цель — обойтись без chroot, для базовой настройки и проверки работоспособности chroot все же нужно сделать. В противном случае обновлять, доставлять пакеты Вам придется уже на боевой системе.

busybox mount -t proc none /arch/proc
busybox mount -o rbind /dev /arch/dev
busybox mount -t tmpfs none /arch/tmp
busybox mount -o size=10%,mode=0755 -t tmpfs none /arch/run
chroot /arch /bin/bash

Часть 2: Поиск и решение проблем


Проблема 1: ping не работает


Попробуем ping

ping 8.8.8.8
socket: Permission denied

Вспоминаем, что у Android серьезная система разграничения прав. И в нем существует пермишен на «Полный доступ к сети». Без этого пермишшена пользователи не могут получить полный доступ к сокетам. То что надо.

Вернемся к Android консоли и пропишем комманду id:

uid=2000(shell) gid=2000(shell) groups=1003(graphics),1004(input),1007(log),1009(mount),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats)

У вас вывод может быть другой

groupadd -g 3003 inet
usermod -a -G inet root

Но вот незадача, chroot не обновляет group. Поможет вот такой «хак»:

su root

Пробуем еще раз:

ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=59 time=89.6 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=59 time=88.6 ms

Проблема 2: Не работает DNS


Удаляем симлинк на systemd и запишем нормальные DNS:

rm /etc/resolv.conf
echo "nameserver 8.8.8.8" > /etc/resolv.conf

Если Вы запороли PATH


Если произошло такое, что простые комманды вида ls,cat,su не работают(не видятся) системой, Вы можете попробывать вызвать их напрямую: /system/bin/ls, /system/bin/cat, /system/xbin/su.
Или перезагрузить устройство.

Ставим необходимые пакеты


pacman -S gcc htop iotop sudo openssh

Проверяем gcc


cd /root
nano main.cpp

#include 
int main() {
std::cout << "Hello World!\n";
return 0;
}


g++ main.cpp
./a.out

Часть 3: Подготовка к работе без chroot


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

Если Вы не уверены в том, что файлы не пересекутся, выполните эти команды из Android консоли:

ls /etc/ > /sdcard/ls.txt
ls /arch/etc/ > /sdcard/ls2.txt
busybox grep -F -f /sdcard/ls.txt /sdcard/ls2.txt

Покажет пересечение файлов. У меня это выглядит так:

dhcpcd.conf
hosts
security

Выполним копирование файлов из /etc/ в /arch/etc/ из Android:

cp -Ra /etc/* /arch/etc/
cp -a /sbin/adbd /arch/usr/bin/

Ключ -a обязателен, так как при использовании обычного -R права скопированы не будут.

Нужно заранее позаботится о Root.


Android приложения требуют, что бы комманда su сразу же давала доступ к суперпользователю и не запрашивала пароль.

passwd
mv /usr/bin/su /usr/bin/su.r

Часть 4: Поехали!


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

mkdir /lib
mkdir /bin
mkdir /xbin
mkdir /opt
mkdir /usr
mkdir /home
mkdir /run
mkdir /srv
mkdir /tmp
mkdir /var
busybox mount --bind /arch/etc /etc
busybox mount --bind /arch/opt /opt
busybox mount --bind /arch/home /home
busybox mount -o size=10%,mode=0755 -t tmpfs none /run
busybox mount --bind /arch/srv /srv
busybox mount -t tmpfs none /tmp
busybox mount --bind /arch/sbin /sbin
busybox mount --bind /arch/usr/ /usr
busybox mount --bind /arch/var/ /var
busybox mount --bind /arch/lib/ /lib
busybox mount --bind /arch/usr/bin/ /bin
/bin/bash

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

Что дальше


В итоге мы имеем практически полноценный ArchLinux за исключением systemd с свежими версиями пакетов.

Можно установить http, php, mysql. При правильной настройке на уменьшение потребления памяти даже на моем смартфоне с ОЗУ 512Мб они работали корректно.

Можно установить иксовые библиотеки и с помощью X сервера для Android пользоваться ПО для линукса. xterm заработал корректно.

Можно собирать любые программы (и, о нет, ядра) для Linux без ПК.
Можно установить Java для ARM и использовать Java приложения.

Послесловие


Большой проблемой остается systemd и его привязка к PID 1. Для того, что бы сохранить PID 1 нужно влезть в init андроида и прописать exec после инициализации устройств. Это можно сделать заменив init андроида shell скриптом, но тогда остается вопрос что делать с оригинальным init андроида. Так как место на загрузочном диске ограничено несколькими мегабайтами, нужно будет использовать switch_root в заранее созданный образ. Мне пока не удалось завести systemd таким способом.

Мне 16, и это моя первая публикация. Конструктивная критика приветствуется.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331546/


Метки:  

Руководство: как использовать Python для алгоритмической торговли на бирже. Часть 1

Суббота, 24 Июня 2017 г. 13:30 + в цитатник


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

Среди наиболее популярных в сфере финансов языков программирования можно отметить R и Python, также часто используются C++, C# и Java. В опубликованном на сайте DataCamp руководстве речь идет о том, как начать использовать Python для создания финансовых приложений — мы представляем вам серию статей-адаптаций глав этого материала.

Структура руководства:

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

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


Прежде чем окунуться в мир торговых стратегий, имеет смысл коснуться базовых понятий. Однако это не означает, что то, о чем пойдет речь ниже, рассчитано совсем на новичков. Будет здорово, если вы предварительно ознакомитесь с курсом по использованию Python для работы с данными, а также представляете себе, как работать со списками и пакетами Python, а также хотя бы на базовом уровне знакомы с NumPy и Pandas.

Акции и торговля на бирже


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

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

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

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

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

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

Данные временных рядов


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



По оcи X расположены даты, а цена — на оси Y. «Последовательные равные промежутки времени» в данном случае означает, что по временной оси даты расположены с двухнедельным интервалом: можно сравнить 3/7/2005 и 3/31/2005, а также 4/5/2005 и 4/19/2005 (здесь даты записаны в принятом в США формате, когда сначала идет месяц, а затем день).

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

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

Основы Python для сферы финансов: Pandas


Одним из самых востребованных инструментов при использовании Python для разработки финансовых приложений является пакет Pandas. Он нужен уже в самом начале, но по мере углубления в процесс разработки понадобятся и такие пакеты как NumPy, SciPy, Matplotlib.

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

Импорт финансовых данных


Пакет pandas-datareader позволяет получать данные из таких источников, как Google, Yahoo! Finance или Всемирный банк — подробнее о доступных источниках данных написано в документации. В этом руководстве будет рассматриваться получение данных с сервиса Yahoo! Finance. Для начала работы необходимо установить последнюю версию пакета с помощью pip:

pip install pandas-datareader

Инструкции по установке версии в разработке представлены здесь.

import pandas_datareader as pdr
import datetime 
aapl = pdr.get_data_yahoo('AAPL', 
                          start=datetime.datetime(2006, 10, 1), 
                          end=datetime.datetime(2012, 1, 1))

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

Важно также понимать, что несмотря на то, что pandas-datareader — это удобный инструмент для загрузки данных, он далеко не единственный для Python. Также можно использовать библиотеки вроде Quandl, которая позволяет получать данные с сервиса Google Finance:

import quandl 
aapl = quandl.get("WIKI/AAPL", start_date="2006-10-01", end_date="2012-01-01")

Также многим известно, что в сфере финансов для анализа данных очень популярен Excel. Для удобства будущей работы можно интегрировать этот инструмент с Python (подробнее по ссылке).

Работа с данными временных рядов


Для импортирования данных мы использовали pandas_datareader. В результате возник объект aapl — это DataFrame, то есть двумерная именованная структура данных со столбцами потенциально разных типов. Первое, что следует сделать при работе с таким фреймом — запустить функции head() и tail() для того, чтобы взглянуть на первый и последний столбцы датафрейма. Для получения полезной статистической сводки по скачанным данным можно воспользоваться функцией describe().

Пример этого кода можно найти на странице исходного материала.

Данные содержат четыре столбца с ценой открытия и закрытия торгового периода, а также максимальной и минимальной ценой — мы рассматриваем дневные интервалы и акции Apple. Также мы получаем два дополнительных столбца: Volume и Adj Close. Первый из них используется для того, чтобы зафиксировать количество акций, с которыми совершались сделки в торговый день. Второй столбец — это «отрегулированная» цена закрытия (adjusted closing price), это значит, что в цену закрытия периода были добавлены все действия с акциями, которые могли быть совершены до момента открытия следующего торгового дня.

Если нужно сохранить данные в CSV-файл, это можно сделать с помощью функции to_csv(), а прочитать файл можно с помощью read_csv() — это полезно для ситуаций, когда источник данных меняется и доступ к ним временно теряется.

import pandas as pd
aapl.to_csv('data/aapl_ohlc.csv')
df = pd.read_csv('data/aapl_ohlc.csv', header=0, index_col='Date', parse_dates=True)

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

Для того, чтобы посмотреть на индекс и столбцы данных следует использовать атрибуты index и columns. Затем можно выделить подмножество из десяти последних наблюдений в столбце column. Для изолирования этих значений следует использовать квадратные скобки. Последнее значение помещается в переменную ts, а проверка ее типа осуществляется с помощью функции type().

# Inspect the index 
aapl.index
 
# Inspect the columns
aapl.columns
 
# Select only the last 10 observations of `Close`
ts = aapl['Close'][-10:]
 
# Check the type of `ts` 
type(ts)

Использование квадратных скобок удобно, но это не самый характерный способ при работе с Pandas. Поэтому также стоит рассмотреть функции loc() и iloc(): первая из них используется для label-based индексирования, а последняя для позиционального индексирования.

На практике, это значит, что можно передать ярлык ряда вроде 2007 или 2006-11-01 в функцию loc(), а целые числа вроде 22 или 43 передаются функции iloc().

# Inspect the first rows of November-December 2006
print(aapl.loc[pd.Timestamp('2006-11-01'):pd.Timestamp('2006-12-31')].head())
 
# Inspect the first rows of 2007 
print(aapl.loc['2007'].head())
 
# Inspect November 2006
print(aapl.iloc[22:43])
 
# Inspect the 'Open' and 'Close' values at 2006-11-01 and 2006-12-01
print(aapl.iloc[[22,43], [0, 3]])

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

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

# Sample 20 rows
sample = aapl.sample(20)
 
# Print `sample`
print(sample)
 
# Resample to monthly level 
monthly_aapl = aapl.resample('M').mean()
 
# Print `monthly_aapl`
print(monthly_aapl)

Прежде чем перейти к визуализации данных и проведению финансового анализа, можно начать вычислять разницу между ценами открытия и закрытия торгового периода. Эту арифметическую операцию можно с помощью Pandas — нужно вычесть значения столбца Open данных appl из столбца Close. Или, другими словами, вычесть aapl.Close из aapl.Open. Получившийся результат будет храниться в новом столбце датафрейма aapl под названием diff, который можно удалить с помощью функции del:

# Add a column `diff` to `aapl` 
aapl['diff'] = aapl.Open - aapl.Close
 
# Delete the new `diff` column
del aapl['diff']

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

Визуализация данных временных рядов
Помимо анализа данных с помощью функций head(), tail() и индексирования, также возможна их визуализация. Благодаря интеграции Pandas с инструментом для создания графиков Matplotlib это можно сделать довольно легко. Нужно лишь использовать функцию plot() и передать ей релевантные параметры. Кроме того, если добавить параметр grid, то получившийся график будет наложен на сетку.

# Import Matplotlib's `pyplot` module as `plt`
import matplotlib.pyplot as plt
 
# Plot the closing prices for `aapl`
aapl['Close'].plot(grid=True)
 
# Show the plot
plt.show()

Этот код дает вот такой график:



В следующей части руководства речь пойдет о финансовом анализе данных временных рядов с помощью Python.

Продолжение следует…..

Другие материалы по теме финансов и фондового рынка от ITinvest:


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

https://habrahabr.ru/post/331542/


Метки:  

Россия — Мексика: исторический футбольный матч роботов, управляемый болельщиками через Твиттер

Суббота, 24 Июня 2017 г. 11:08 + в цитатник
image

Матч роботов, под управлением российских и мексиканских пользователей Твиттера начнётся сегодня, в 15:00 по московскому времени, за три часа до начала официального матча между сборными в рамках Кубка конфедераций. Твиттербол организован Университетом Иннополис и компанией Трик в рамках Всероссийской Робототехнической Олимпиады.

Для управления робота нужно написать сообщение с тегом #ConfedCup и через пробелы указать сборную (Rus / Mex) и команду для робота. Регистр и знаки препинания не учитываются. Пример: “Rus go! #ConfedCup

Список доступных команд:

go — движение вперед на 30 сантиметров
back — движение назад на 30 сантиметров
left — поворот влево на 90 градусов
right — поворот вправо на 90 градусов
turn — разворот на 180 градусов

Специальный алгоритм каждые 5 секунд анализирует сообщения в Твиттере и приказывает роботам выполнить самую популярную команду за этот интервал. Болельщикам придётся действовать коллективно и обдуманно.

Наблюдать за действиями роботов можно по веб-трансляции на сайте: www.smashcast.tv/innopolisu

Поддержи своих!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331538/


Метки:  

Интегрируем Copy-Paste-Detector для Swift в Xcode

Суббота, 24 Июня 2017 г. 10:12 + в цитатник


DRY Principle


Одним из основных мета-принципов программирования является DRY (Don’t Repeat Yourself). Суть данного мета-принципа проста и должна являться негаснущим маяком для любого разработчика. Она гласит, что в разрабатываемой системе не должно быть кусков кода, имеющих одинаковый код. Выражаясь более простым языком, в программе не должно быть копипасты!
Для начала, давайте точно определим, что будем считать за копипасту. Если в проекте если одинаковые файлы — это грубейшая копипаста. Если есть одинаковые классы с разным названием и выполняющие одно и тоже — грубейшая копипаста. Даже если 10 строк одинакового кода — это тоже является копипастой. Возможно вы возразите, что 10 строк кода продублировать иногда позволительно. Из моего опыта, проект в 100000 строк вполне реально писать без подобной копипасты.
Какие же минусы несёт нам копипаста?


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

Как избежать копипасты


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


  • Code-review проводится уже после того, как код написан, а фича доделана. Поэтому если дублирование найдено, то приходится переделывать этот код.
  • Ревьюер вообще может не обнаружить копипасту, особенно если старый кусок сделан давно, а в pull request это продублировано. Часто разработчики забывают написанный код крайне быстро, чтобы освободить память под новые задачи.
  • Тратится время на написание начального кода, на ревью проекта и на исправление. А также на повторное ревью. Временные затраты вы можете посчитать сами. А как известно время=деньги! Особенно в разработке.

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


Установка CPD


Для языка Swift было найдено всего 2 детектора дублирования кода.



Второй является более кастомизируемым, поддерживаемым и в целом более стабильным.
Для начала работы необходимо установить pmd путем выполнения команды в консоли “brew install pmd”.
Важно заметить, что для других языков в пакете pmd содержатся и статические анализаторы кода. В пакете для языка Swift есть только CPD (copy paste detector). Поэтому анализаторы кода нужно ставить дополнительно. Лучшим выбором на данный момент будет: https://github.com/realm/SwiftLint


Интеграция с Xcode


Как ранее было выяснено, самым удобным способом обнаружения ошибок и копипасты является этап прекомпиляции проекта. В Xcode можно добавлять свои скрипты во вкладке Build Phases, чтобы они также отрабатывали на этом этапе и не давали скомпилироваться проекту успешно, если был найден дублирующийся код.


Для подключения в проект необходимо создать новый build script с именем “CopyPaste Detection” во вкладке Build Phases и добавить внутрь следующий скрипт:


# Running CPD
pmd cpd --files ${EXECUTABLE_NAME} --minimum-tokens 50 --language swift --encoding UTF-8 --format net.sourceforge.pmd.cpd.XMLRenderer > cpd-output.xml --failOnViolation true
# Running script
php ./cpd_script.php -cpd-xml cpd-output.xml

Разберем подробнее, что означает эта магия. Первая часть скрипта означает, что мы запускаем CPD для файлов, лежащих в корневой папке нашего проекта. minimum-tokens означает минимальное количество токенов, при нахождении которые скрипт будет помечать фрагмент кода как копипасту. Токеном является некая абстрактная единица, поэтому не нужно принимать это за букву, слово и конструкцию. Эмпирическим путём было выяснено, что цифра 50 является оптимальной для языка Swift. Если взять меньше, то можно найти то, что не является копипастой, если больше, то есть большой шанс пропустить дублирование. Следующий параметр — format означает, что вывод будет происходить в xml-файл. И последний — failOnViolation говорит о том, что приложение не соберется, если найден хоть 1 фрагмент дублирующегося кода.


В результате выполнения первой части скрипта, у нас есть xml файл с логами анализа проекта. Теперь необходимо показать нашу копипасту прямо в Xcode в привычном для разработчика формате в виде warnings. Для этого добавим еще один скрипт прямо в корень проекта:



Вторая часть нашего первого скрипта запустит второй скрипт и преобразует сгенерированный xml файл в warnings в Xcode. Подробнее о том, как можно самому генерировать warnings в Xcode можно прочитать здесь.


Теперь запустим наш проект, cmd+B/R и убедимся, что наш скрипт работает. Получаем следующую картину после запуска.


В Issue Navigator:




Внутри файла:




Сopypaste — зло


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


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

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

https://habrahabr.ru/post/331238/


Метки:  

Переносим оценки из Имхонет в Must App

Суббота, 24 Июня 2017 г. 03:05 + в цитатник
image

Имхонет закрылся. И людей оставили просто с табличкой фильмов, которые они смотрели. Мы сделали возможным перенести все ваши оценки фильмов, которые вы так долго собирали, в наше удобнейшее приложение для коллекционирования просмотренных фильмов Must App!

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



Как перенести свои оценки?


Для начала зайдите на наш сайт.

image

Нажмите «Войти», и выберите удобный для вас способ входа.

image

Выберите адрес вашей странички.

image

Ваш профиль создан. Теперь осталось только перенести ваши оценки.

image

Скачайте ваши оценки с Имхонет. Нажмите на ссылку Import Movie Library to Must. И загрузите файл с вашими оценками перетащив или нажав на кнопку списка Watched (чтобы фильмы добавились в список просмотренного).

image

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

image

Ваши фильмы добавлены!

Что дальше?


Скачайте наше прекрасное приложение для iPhone, и наслаждайтесь!
image
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331536/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1021 1020 [1019] 1018 1017 ..
.. 1 Календарь