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

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

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

Дайджест свежих материалов из мира фронтенда за последнюю неделю №267 (12 — 18 июня 2017)

Воскресенье, 18 Июня 2017 г. 22:49 + в цитатник
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.

Веб-разработка
CSS
Javascript
Браузеры
Занимательное

Веб-разработка



CSS



JavaScript



Браузеры



Занимательное



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



Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331168/


Технологии больших данных в работе с бактериями микробиоты. Лекция в Яндексе

Воскресенье, 18 Июня 2017 г. 21:50 + в цитатник
Мы часто говорим о задачах, которые лежат на стыке той или иной классической науки и анализа данных. В сегодняшнем докладе эта идеология представлена воочию — большую часть доклада читает учёный, а о конкретных методах и инструментах рассказывает программист.




Под катом — расшифровка и основная часть слайдов.



Дмитрий Алексеев, директор по R&D биомедицинского холдинга «Атлас»:
— Вообще-то я микроб. Такое необычно слышать, ведь ты привык, что тебе люди что-то рассказывают. А я микроб. Нас много живет. И в тебе тоже.

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

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

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

Изучают нас из-за того, что в нас есть ДНК. Ты удивишься, но во мне ДНК такая же, как в тебе. Я себе просто эту мысль в тишине иногда думаю, что во мне такая же ДНК, как в людях. Это поражает. Все-таки мы супер-разные, а записано все ноликами и единичками. Хотя, может, тебе это очень знакомо. Почему? Потому что ты делаешь всякие эти приложения, и полезные приложения, и бесполезные. И полезное приложение из ноликов и единичек, и бесполезное. Тут главное — правильно выстроить порядок. Хотя я уверен, что мой порядок никто не выстраивал, за меня эволюция работала. Те мои братья, которые были посильнее, предки, они просто оставались. Те, которые как-то неправильно изменялись, они исчезали. Это то, что называется отбором. И совокупность наших генов называется мета-геном. Это то, что тебе — человеку, который анализирует данные, — подается в первую очередь как сырые данные. И эти гены можно найти в любой среде. Даже в ядерном реакторе или на космической станции есть мои друзья-микробы, и у них тоже есть гены, и они действуют по тем же правилам.

Если посмотреть в кишечник, можно увидеть разнообразие, то есть много есть таких ребят, как я. Но есть еще и другие виды. Они в другой кепке, в других очках, кто-то в кроссовках, кто-то в шортах. Примерно от 300 до 1000 видов таких ребят живет в каждом кишечнике. И удивительно: если так прикинуть, то мы не очень поймем, каких клеток больше: меня, то есть микробов, или твоих собственных. Но мы там все умещаемся в эти 1,5 кг сухого веса, который есть у тебя в кишечнике. Почему? Потому что мы реально меньше. Если ты не догадался, то я сейчас — всего лишь увеличенная копия микроба.

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



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

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

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



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

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



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

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

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



Вот эта картинка на самом деле интересная. Когда-нибудь в реальности тебе понадобилось избавиться от большого количества плохих микробов. Ты сделал две вещи. Антибиотики я уже назвал. Второе — про гигиену: ты заставил всех мыть руки. Крайний вариант — Маяковский, который вообще боялся всего, и все мыл со своим тазиком. Это суперкультурное явление. Мы должны понимать, что ни в какой живой природе такого явления и мема нет, будто надо все мыть. Ты его создал, создал какие-то суперсказки, они причем в разных народностях могут быть разные. В моем детстве, в твоем детстве, в чьем-то детстве в 1980-е годы точно был этот персонаж Мойдодыр, и мальчику с грязными руками и грязными ногами просто было супермегастыдно. Сейчас, если ты наберешь «теория гигиены» в Википедии, то тут же найдешь теорию одного европейского врача о том, что количество аллергий у детей связано с тем, что они помещены в суперстерильные условия. За счет этого иммунная система не обучается, то есть она живет в стерильной коробке. И потом, столкнувшись с чем-то действительно враждебным или полезным, она на все это реагирует воспалением. А воспаление — это и есть аллергия.



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



Ты начал меня активно изучать во время этой истории про эпидемиологическое ожирение в Соединенных Штатах Америки. С 1990 по 2008 год в некоторых штатах ожирение как диагноз с 10% выросло до 30%. На самом деле, это не очень похоже на те голливудские фильмы, которые ты смотрел. По улицам действительно ходят очень полные люди. У 30% из них стоит диагноз. При этом структура питания за эти 30 лет у американцев не менялась. И Джеффри Гордон, исследователь, обнаружил, что у людей, которые более стройные, slim, и у людей полных с диагнозом «ожирение» разные микробы. Более того, когда он этих микробов от людей с ожирением пересадил в мышей, выяснилось, что мыши быстрее набирают вес. То есть этот вариант микробов, который живет у людей с ожирением, производит больше калорий из того же количества пищи. И тогда ты понял, что состав микробов оказывает влияние на то, как ты будешь себя чувствовать.



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



Другая твоя история — действительно живой проект, можно на него посмотреть. Он назывался Happy Meal. Сам догадаешься, почему. На 137 день оставленная на воздухе еда выглядит примерно так же, как она выглядела в магазине. Почему она так выглядит? Потому что ни микробы, ни плесень эту еду не едят. Представь, что такая еда попадает внутрь тебя, и там такие же микробы. Они тоже не очень хотят ее есть. Говорят, потом «Макдональдс» выпустил опровержение, что там никакой химии специальной нет, просто очень много соли. Но если подумать, нам все равно, что там в большом количестве не дает микробам нормально расти.



Еще одна картинка, уже более сложная. И она уже прикольная — про данные, которые в разных экономических группах показывают, как растет подушевой доход, и что происходит с потреблением мяса, пустых калорий — это в основном алкоголь и сладости, — и вообще с количеством калорий. Вот экономическая группа А — самые богатые страны с 1961 по 2009 год. Доход вырос, потребление мяса выросло. В группе B начиналось ниже, но там то же самое происходит. И фактически всё, что мы видим во всех экономиках, — это если растет подушевой доход, то люди начинают есть больше мяса, больше пустых и диетарных калорий. Кроме Индии, там мясо не едят. То есть прямо from data видно, что чем больше мы зарабатываем, тем больше едим. И едим мы те самые пустые калорий, которые не суперполезны нашим микробам. А образ жизни — притом, что мы больше зарабатываем — становится не суперподвижный.



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

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



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

Хороший проект, в котором мы участвуем с российской стороны — микробиота метрополитенов всяких городов. Сейчас здесь уже почти 100 городов. Началось все это с Нью-Йорка. Собрали ДНК со всех станций и посмотрели, какие там микробы. И, например, про данные — которые, кстати, тоже открыты, — мы решали недавно следующую задачу: можно ли по составу микробов определить, какая это станция метрополитена? Потому что от разных станций разные микробы едут в центр, и там уже на пересадке все смешиваются.



Вообще, мы воспринимаем эту тему про микробов как complexity science. Мне кажется, что гуру complexity science находится в Санта-Фе. Там есть такой Complexity University, Institute of Complexity Science, я когда-то там проходил стажировку. И один из способов изучения сложности… Сложностью мы называем состояние из простых объектов, между которыми есть какие-то связи. И молодежную систему мы создали уже из двух бактерий, трех веществ, которыми они обмениваются, какой-то еды, которая поступает, и кишечника. Мы даже в какой-то момент симулировали. Красные и синие бактерии — кишечник, и все это между собой общается. Это называется Agent Based Modeling. Можно эту историю запускать, а потом смотреть, получим ли мы что-то похожее на происходящее в реальной жизни. Например, мы получили систему с двумя стабильными точками, и точки отличаются в зависимости от того, как микробы распределены в кишечнике. На самом деле люди пока это померить не могут. Но мы тоже представляем, что одни живут ближе к стеночке, другие любят посерединке.

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



Другая история — например, про ДНК. Я немножко расскажу вглубь про алгоритмы, про Data Science Day. Можно прям взять последовательности ДНК микробов. Мы работаем с последовательностью примерно в 200 букв. Попилим ее на k-меры —слова длиной k. Мы брали, по-моему, девятибуквенные слова. Можно дальше по этой подписи и частоте девятибуквенных слов пытаться сравнивать между собой микробный состав всех находящихся здесь людей. Что интересно, уже при таком подходе мы нашли таких людей, у которых было какое-то новое существо, и оно оставляло новый след из этих девятибуквенных слов. Про это даже где-то напечатали, в Bioinformatics. Тогда в указанном существе не было генома. Пока мы дописывали статью, геном появился, и мы явно доказали, что перед нами то самое существо, которое можно определить всего лишь по частоте букв, то есть по не очень сложной метрике.

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

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



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

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



Первый — анализ уровня синтеза витаминов микробиоты человека, второй — text mining фактов про бактерии.

Что касается анализа уровней синтеза витаминов, задача может быть сформулирована так. На входе у нас есть множество матриц. Для начала, это так называемая матрица относительной представленности бактерий в образцах. Каждый образец — какой-то человек, микробиота конкретного человека. Таким образом, матрица говорит о том, какие бактерии у него есть в кишечнике. И затем это несколько справочных матриц из открытого проекта PICRUSt. Вот матрица о том, какие гены бактерий с какими ферментами связаны. Витамины состоят из ферментов. И на выходе мы хотим получить матрицу, в которой будет содержаться информация об уровнях синтеза витаминов в образцах. И, на самом деле, исходя из описания задачи уже видно, что она сводится к множеству операций агрегации и связывания матриц. И как data scientists — я один из них в числе этой команды — мы занимались реализацией алгоритма, проведением exploratory-анализа и всем, что связано с кодом. Но нам необходимо было руководство биолога для того, чтобы он преподнес нам концепцию алгоритма и помог понять, какие математические операции имеют и не имеют смысл с точки зрения биологии. Например, не всегда понятно, что корректно брать — медиану или среднюю.



Покажу вам heatmap. Он показывает способность микробиоты, образцов, к синтезу ферментов витамина B9. Чтобы микробиота могла его синтезировать, она должна также синтезировать все ферменты, из которых он состоит. Названия образцов немножко слились, но сейчас это не так важно. Исходя из этой heatmap мы можем увидеть некоторые группы образцов, у которых, например, все довольно хорошо с синтезом этого витамина. Почти все ферменты присутствуют. Но есть две группы образцов, у которых все хуже, и как data scientist я могу задать вопрос: что отличает группу этих образцов? Я могу сравнить эти образцы по их бактериальном составу или по каким-то метаданным. Может быть, все эти образцы принадлежат людям, которым за 40, например.



На самом деле у нас очень много матриц. В случае алгоритма анализа синтеза витаминов размер матрицы небольшой, он помещается в оперативной памяти, поэтому такой анализ легко провести у себя на ноутбуке. Но в случае, например, данных WGS (whole genome sequencing — полногеномные данные) у нас есть матрица о представленности 9 млн генов в 10 тыс. образцах, то есть это 9 млн столбцов и 10 тыс. строк. Такая матрица не помещается в оперативной памяти, поэтому нам нужны технологии для работы с Big Data, такие, например, как HBase — потому что он заточен под матрицы. Мы думаем, что с помощью Big Data-технологий мы можем собирать огромное количество биологических данных, накапливать их. Уже сейчас у «Кномикс» есть данные об относительной представленности бактерий и основанные на них результаты анализов. Например, уровни синтеза витаминов.



Дмитрий:
— Мы стали смотреть на все эти кучи матриц. У нас есть рабочее название MetaMut. Это такой проект, где мы можем представить в виде гигантской матрицы все исходные данные для биологических алгоритмов, которые мы используем в связи с микробами. Данные говорят, какие бактерии есть, какие гены, какие в этих генах мутации. Размерность матрицы мы прикидываем в районе 20 млн таких столбцов, а строк столько же, сколько образцов. И дальше каждый алгоритм, который люди официально выпускают в виде статьи, считающей что-то про микробов, мы на самом деле можем представить в виде набора матричных операций перемножений, сложений, транспонирований и т. ж. И тогда мы подходим не к биоинформатике, всей этой суперформализованной науке, а просто к правильному языку матричных операций. Если мы думаем и надеемся, что такую сложную историю про бактерии, гены, всякие обходы графов и так далее мы можем представить всего лишь в виде конечного набора матричных операторов… в этот самый момент описанный язык станет конструктором, с помощью которого любой data scientist, не погружаясь в смысл, сможет упражняться с матрицами. И когда он будет находить интересные свойства внутри матриц — с математическим образованием это не очень сложно, — у него всего останется лишь вопрос, есть ли здесь какая-то интерпретация. Он с этим вопросом будет возвращаться обратно к биологу.





Анатолий:
— Дальше я хотел рассказать про text mining фактов про бактерии. На входе этого алгоритма у нас есть корпус из 16 тыс. научных статей про микробиоту кишечника человека. И также у нас есть каталоги, они же словари названий бактерий, болезней, пробиотиков, еды. И мы хотим получить таблицу с предположительными фактами про бактерии. То есть это такие триплеты: бактерия, еда и как они между собой связаны. Среди наших инструментов для решения этой задачи были стандартные средства natural language processing —кстати, из пакета spaCy, а также ручная подготовка выборок. И именно здесь нам были необходимы биологи, чтобы они вычитывали предложения из этих статей, где мы ищем связи, и помечали их как positive или negative. Мы извлекали отношения между сущностями в этих предложениях, где сущность — это бактерия, еда. Оно у нас построено на поиске кратчайшего пути между словами в графе предложений. И у нас есть такая гипотеза, что по кратчайшему пути мы можем определить связь между двумя сущностями. Затем мы применяли transfer learning pipeline: это экстрактор фич и какой-то простой классификатор, simple classifier.



Я решил вставить небольшой пример кластеризации. Мы собрали много-много кратчайших путей из разных графов научных предложений, в которых встречалось какое-то название бактерий — например, Faecalibacterium prausnitzii — и какая-то еда или пребиотик инулин. Мы связывали эти две сущности в предложении, и получили много разных кратчайших путей в графах. И мы их кластеризовали, получив такую интересную картинку. На самом деле она нам говорит о том, что наша гипотеза, все эти кратчайшие пути в графах, возможно, действительно работают и как-то систематизируются. Это PCA.



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

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

Спасибо вам большое. Надеюсь, вы станете добрее относиться к нам, микробам, и к программистам, конечно.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331158/


[Из песочницы] Рисование толстых линий в WebGL

Воскресенье, 18 Июня 2017 г. 20:09 + в цитатник
Готовые примеры

Примеры подготовлены на базе движка OpenGlobus, который в данном случае используется как обертка над чистым Javascript WebGL.
 
Пример для 2D случая
Пример для 3D случая (используйте клавиши W,S,A,D,Q,E и курсор для перемещения)

Вступление

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

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

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

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

(рис. 2)
 
Однако угол между соседними сегментами, может быть настолько  острым, что точка пересечения, может уйти далеко от точки соединения этих линий (рис. 3).

(рис. 3)
 
В таком случае этот угол необходимо как-то обработать (рис. 4):
 

(рис. 4)

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

(рис. 5)

Числа возле вершин — это индексы вершин, по которым происходит отрисовка полигонов в графическом конвейере. Если угол тупой, в этом случае треугольник для обрезания сольется в бесконечно тонкую линию и станет невидимым (рис. 6).
(рис. 6)
 
 
Про себя представляем последовательность таким образом(рис. 7)

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

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

Т.к. WebGL позволяет использовать один буфер для разных атрибутов, то в случае, если элементом этого буфера является координата,  при вызове vertexAttribPointer каждому атрибуту назначается в байтах размер элемента буфера, и смещение относительно текущего элемента атрибута. Это хорошо видно, если изобразить последовательность на бумаге (рис. 9):
 

(рис 9)
 
Верхняя строчка — это индексы в массиве вершин; 8 — размер элемента (координата тип vec2) т.е. 2х4 байта; Xi, Yi — значения координат в точках А, B, C; Xp = Xa — Xb, Yp = Yа — Yb, Xn = Xc — Xb, Yn = Xc — Xb т.е. вершины указывающие направление в пограничных точках. Цветными дугами изображены связки координат (предыдущая, текущая и следующая) для каждого индекса в вершинном шейдере, где current — текущая координата связки, previous — предыдущая координата связки и next — следующая координата связки. Значение 32 байт — смещение в буфере для того, чтобы идентифицировать текущее(current) относительно предыдущего (previous) значения координат, 64 байт — смещение в буфере для идентификации следующего(next) значения. Т.к. индекс очередной координаты начинается с предыдущего (previous) значения, то для него смещение в массиве равно нулю. Последняя строчка показывает порядок каждой координаты в сегменте, 1 и -1 — это начало сегмента, 2 и -2 — соответственно, конец сегмента.
 
В коде это выглядит так:

var vb = this._verticesBuffer;
gl.bindBuffer(gl.ARRAY_BUFFER, vb);
gl.vertexAttribPointer(sha.prev._pName, vb.itemSize, gl.FLOAT, false, 8, 0);
gl.vertexAttribPointer(sha.current._pName, vb.itemSize, gl.FLOAT, false, 8, 32);
gl.vertexAttribPointer(sha.next._pName, vb.itemSize, gl.FLOAT, false, 8, 64);
 
gl.bindBuffer(gl.ARRAY_BUFFER, this._ordersBuffer);
gl.vertexAttribPointer(sha.order._pName, this._ordersBuffer.itemSize, gl.FLOAT, false, 4, 0);

Так выглядит функция, которая создает массивы вершин и порядков, где pathArr —  массив массивов координат по которым заполняются массивы для инициализации буферов outVertices — массив координат, outOrders — массив порядков и outIndexes — массив индексов:

 
Polyline2d.createLineData = function (pathArr, outVertices, outOrders, outIndexes) {
    var index = 0;
 
    outIndexes.push(0, 0);
 
    for ( var j = 0; j < pathArr.length; j++ ) {
        path = pathArr[j];
        var startIndex = index;
        var last = [path[0][0] + path[0][0] - path[1][0], path[0][1] + path[0][1] - path[1][1]];
        outVertices.push(last[0], last[1], last[0], last[1], last[0], last[1], last[0], last[1]);
        outOrders.push(1, -1, 2, -2);
 
        //На каждую вершину приходится по 4 элемента
        for ( var i = 0; i < path.length; i++ ) {
            var cur = path[i];
            outVertices.push(cur[0], cur[1], cur[0], cur[1], cur[0], cur[1], cur[0], cur[1]);
            outOrders.push(1, -1, 2, -2);
            outIndexes.push(index++, index++, index++, index++);
        }
 
        var first = [path[path.length - 1][0] + path[path.length - 1][0] - path[path.length - 2][0],  path[path.length - 1][1] + path[path.length - 1][1] - path[path.length - 2][1]];
        outVertices.push(first[0], first[1], first[0], first[1], first[0], first[1], first[0], first[1]);
        outOrders.push(1, -1, 2, -2);
        outIndexes.push(index - 1, index - 1, index - 1, index - 1);
 
        if ( j < pathArr.length - 1 ) {
            index += 8;
            outIndexes.push(index, index);
        }
    }
};

Пример:

var path = [[[-100, -50], [1, 2], [200, 15]]];
var vertices = [],
     orders = [],
     indexes = [];
Polyline2d.createLineData(path, vertices, orders, indexes);

Получим:
 
vertices: [-201, -102, -201, -102, -201, -102, -201, -102, -100, -50, -100, -50, -100, -50, -100, -50, 1, 2, 1, 2, 1, 2, 1, 2, 200, 15, 200, 15, 200, 15, 200, 15, 399, 28, 399, 28, 399, 28, 399, 28]
 
orders: [1, -1, 2, -2, 1, -1, 2, -2, 1, -1, 2, -2, 1, -1, 2, -2, 1, -1, 2, -2]
 
indexes: [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11, 11]
 
Вершинный шейдер:

attribute vec2 prev; //предыдущая координата
attribute vec2 current; //текущая координата
attribute vec2 next; //следующая координата
attribute float order; //порядок
               
uniform float thickness; //толщина
uniform vec2 viewport; //размеры экрана

//Функция проецирование на экран
vec2 proj(vec2 coordinates){
    return coordinates / viewport;
}
               
void main() {
    vec2 _next = next;
    vec2 _prev = prev;

    //Блок проверок для случаев, когда координаты точек равны
    if( prev == current ) {
        if( next == current ){
            _next = current + vec2(1.0, 0.0);
            _prev = current - next;
        } else {
            _prev = current + normalize(current - next);
        }
    }
    if( next == current ) {
        _next = current + normalize(current - _prev);
    }
                   
    vec2 sNext = _next,
            sCurrent = current,
            sPrev = _prev;

    //Направляющие от текущей точки, до следующей и предыдущей координаты
    vec2 dirNext = normalize(sNext - sCurrent);
    vec2 dirPrev = normalize(sPrev - sCurrent);
    float dotNP = dot(dirNext, dirPrev);
    
    //Нормали относительно направляющих
    vec2 normalNext = normalize(vec2(-dirNext.y, dirNext.x));
    vec2 normalPrev = normalize(vec2(dirPrev.y, -dirPrev.x));
    float d = thickness * 0.5 * sign(order);
                   
    vec2 m; //m - точка сопряжения, от которой зависит будет угол обрезанным или нет
    if( dotNP >= 0.99991 ) {
        m = sCurrent - normalPrev * d;
    } else {
        vec2 dir = normalPrev + normalNext;
  
        // Таким образом ищется пересечение односторонних граней линии (рис. 2)
        m = sCurrent + dir * d / (dirNext.x * dir.y - dirNext.y * dir.x);
        
        //Проверка на пороговое значение остроты угла
        if( dotNP > 0.5 && dot(dirNext + dirPrev, m - sCurrent) < 0.0 ) {
            float occw = order * sign(dirNext.x * dirPrev.y - dirNext.y * dirPrev.x);
            //Блок решения для правильного построения цепочки LINE_STRING
            if( occw == -1.0 ) {
                m = sCurrent + normalPrev * d;
            } else if ( occw == 1.0 ) {
                m = sCurrent + normalNext * d;
            } else if ( occw == -2.0 ) {
                m = sCurrent + normalNext * d;
            } else if ( occw == 2.0 ) {
                m = sCurrent + normalPrev * d;
            }
         
        //Проверка "внутренней" точки пересечения, чтобы она не убегала за границы сопряженных сегментов
        } else if ( distance(sCurrent, m) > min(distance(sCurrent, sNext), distance(sCurrent, sPrev)) ) {
            m = sCurrent + normalNext * d;
        }
    }
    m = proj(m);
    gl_Position = vec4(m.x, m.y, 0.0, 1.0);
}

Пару слов в заключении

Данный подход реализован для рисования треков, орбит, векторных данных.

В заключении хочу добавить несколько идей, что можно сделать с алгоритмом, чтобы улучшить качество линий. Например, можно передавать для каждой вершины цвет в атрибуте colors, тогда линия станет разноцветной. Так-же каждой вершине можно передавать ширину, тогда линия будет меняться по ширине от точки к точке, а если рассчитывать ширину в точке (в вершинном шейдере) относительно удаленности от точки наблюдения (для трехмерного случая), можно добиться эффекта, когда часть линии расположенная ближе к точке наблюдения визуально больше, чем та часть линии, которая находится на отдалении. Еще можно реализовать сглаживание (antialiasing), добавив два прохода для каждого из краев толстой линии, в которых происходит рисование тонких линий (рамка по краям) с небольшой прозрачностью относительно центральной части.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331164/


Метки:  

Игры на Scheme(Lisp) в среде DrRacket

Воскресенье, 18 Июня 2017 г. 18:47 + в цитатник
В данной статье используется среда разработки DrRacket.
Для начала рассмотрим связь конечного автомата и игрового процесса.
Объект управления в игре можно представить в виде конечного автомата.
Рассмотрим программу, моделирующую светофор.
Этот пример был описан в предыдущей статье.
Переходом в другое устойчивое состояние является переключение сигнала светофора.
Диаграмму состояний можно изобразить в следующем виде.
image

Для того, чтобы создать светофор, нарисуем окружность в центре пустой сцены.
#lang racket
(require picturing-programs)
(define (DOT s)  (circle 100 "solid" s))

s — это переменная, отвечающая за цвет. Переход в другое состояние можно представить
следующей конструкцией
(define (traffic-light-next s)
  (cond
    [(string=? "red" s) "green"]
    [(string=? "green" s) "yellow"]
    [(string=? "yellow" s) "red"]))

Для того, чтобы промоделировать переключение сигнала, используем функцию big-bang.
(big-bang "red"
          [on-tick traffic-light-next 1]
          [to-draw DOT])   
    

Теперь светофор переходит в следующее устойчивое состояние 1 раз в секунду.
Управляющие воздействие в играх также могут оказывать платформы, препятствия, враги и т.д.
Например, в некоторых играх по ходу движения объект может «перескакивать»
на следующий канвас, т.е. приблизившись к границе канваса, объект исчезает
и появляется на противоположной границе. Условие перехода («перескакивания») определяется сравнением координат объекта и края канваса.
 [(> (+ x DELTA) WIDTH)  0]

Если условие не выполнено, остаёмся на том же экране.
(cond
      [(> (+ x dx) WIDTH)  0]
       [else (+ x dx)]        )

Движение определяется приращением DELTA к координате x
Напишем программу целиком
#lang racket
(require 2htdp/image)
(require 2htdp/universe)
(define WIDTH 100)
(define DELTA 1)
(define BALL (circle 5 "solid" "red"))
(define MT   (empty-scene WIDTH 10))
 (define (main x0)
  (big-bang x0
    [to-draw render]
    [on-tick bounce]))
(define (bounce x)
      (cond
      [(> (+ x DELTA) WIDTH)  0]
       [else (+ x DELTA)]  ))
 (define (render x)
  (place-image BALL   x 5 MT))
(main 50)

На этой странице представлены примеры программ, использующих big-bang. Запустить программы можно в режиме «Начинающий студент», добавив необходимые пакеты.
Далее напишем программу, в которой управление объектом осуществляется
клавишами «left» и «right».
Здесь нам понадобится функция для обработкой клавиш.
#lang racket
(require 2htdp/image)
(require 2htdp/universe)
(define BACKGROUND (empty-scene 100 100))
(define DOT (circle 10 "solid" "red"))
(define (place-dot-at x)
  (place-image DOT x 50 BACKGROUND))
(define (change-func p k) ; обработка клавиш
  (cond
    [(string=? "left" k)
     (- p 5)]
    [(string=? "right" k)
     (+ p 5)]
    [else p]))
( big-bang 50
[to-draw place-dot-at] 
[on-key change-func]   )

Если же нам также необходимо обрабатывать нажатия клавиш «up», «down»,
то координаты объекта следует хранить в структуре вида
(define-struct posn (x y))
(define INIT-WORLD (make-posn 100 100))

Напишем программу, в которой объект может перемещаться по горизонтали и вертикали.
#lang racket
(require picturing-programs) 
(define WIDTH 200)
(define HEIGHT 200)
(define BACKGROUND     (empty-scene WIDTH HEIGHT))
(define obj1 (circle 10 "solid" "red"))
(define-struct posn (x y))            ;объявляем структуру
(define INIT-WORLD (make-posn 100 100) )
(define (change-current-world-key current-world a-key-event) ;обработка "событий" клавиатуры 
  (cond
    [(key=? a-key-event "up")
     (make-posn (posn-x current-world) (- (posn-y current-world) 5))]
    [(key=? a-key-event "down")
    (make-posn (posn-x current-world) (+ (posn-y current-world) 5))]
    [(key=? a-key-event "left")
     (make-posn (-(posn-x current-world)5) (posn-y current-world) )]
    [(key=? a-key-event "right")
    (make-posn (+(posn-x current-world)5) (posn-y current-world) )]
    [else current-world]))
(define (redraw current-world)
  (place-image obj1
               (posn-x current-world)
               (posn-y current-world)
               BACKGROUND))
(big-bang INIT-WORLD
(on-key change-current-world-key)
(on-draw redraw) )

Да, но обычно в играх присутствует несколько объектов.
Напишем программу, в которой присутствует два объекта.
Используем две структуры: world и posn.
#lang racket
(require picturing-programs) 
(define WIDTH 300)
(define HEIGHT 300)
(define BACKGROUND     (empty-scene WIDTH HEIGHT))
(define obj1 (circle 10 "solid" "red"))
(define obj2 (circle 10 "solid" "green"))
(define-struct posn (x y))
(define-struct world [obj1 obj2])
; Инициализация параметров в структуре world
(define INIT-WORLD (make-world (make-posn 100 100) (make-posn 200 100)))
(define (draw-game world)
   (place-image 
      obj1
      (posn-x (world-obj1 world))
      (posn-y (world-obj1 world))
      (place-image 
         obj2
         (posn-x (world-obj2 world))
         (posn-y (world-obj2 world))
         BACKGROUND)))
(big-bang   INIT-WORLD
  [to-draw draw-game])

Теперь для того, чтобы управлять одним из объектов, добавим функцию обработки клавиш.
#lang racket
(require picturing-programs) 
(define WIDTH 300)
(define HEIGHT 300)
(define BACKGROUND (empty-scene WIDTH HEIGHT))
(define obj1 (circle 10 "solid" "red"))
(define obj2 (circle 10 "solid" "green"))
(define-struct posn (x y))
(define-struct world [obj1 obj2])
(define INIT-WORLD (make-world (make-posn 50 150) (make-posn 250 150)))
(define (change-current-world-key current-world a-key-event)
  (make-world (change-obj1 (world-obj1 current-world) a-key-event)
              (world-obj2 current-world)))
(define (change-obj1 a-posn a-key-event)
  (cond
    [(key=? a-key-event "up")
     (make-posn (posn-x a-posn) (- (posn-y a-posn) 5))]
    [(key=? a-key-event "down")
     (make-posn (posn-x a-posn) (+ (posn-y a-posn) 5))]
    [(key=? a-key-event "left")
     (make-posn (-(posn-x a-posn) 5) (posn-y a-posn) )]
    [(key=? a-key-event "right")
     (make-posn (+(posn-x a-posn)5) (posn-y a-posn) )]
    [else a-posn]))
(define (draw-game world)
   (place-image 
      obj1
      (posn-x (world-obj1 world))
      (posn-y (world-obj1 world))
     (place-image 
        obj2
        (posn-x (world-obj2 world))
        (posn-y (world-obj2 world))
         BACKGROUND)))
(big-bang   INIT-WORLD
(on-key change-current-world-key)
  [to-draw draw-game] )


Более подробно о создании игр можно прочитать в книге How to Design Worlds.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/328924/


Метки:  

[recovery mode] Пользовательские типы в PHP

Воскресенье, 18 Июня 2017 г. 18:11 + в цитатник
В отношении данных, которые программа получает извне, принято следовать правилу trustno1. Это справедливо не только в отношении данных, получаемых непосредственно от пользователя, но и в отношении данных, которые передаёт в подпрограммы клиентский код.

PHP 7 оснащён расширенной системой контроля типов аргументов, включающей не только классы, но и скаляры. Однако в том, что касается сложных структур данных, ничего не изменилось — для них существует единственный тип array, который в PHP может содержать всё, что угодно.

Я надеюсь, что новые версии PHP исправят ситуацию. А на данный момент я хочу поделиться с сообществом некоторыми своими наработками в этой области:

image

perspectea/typedef
perspectea/generics

typedef


Репозиторий на GitHub: https://github.com/perspectea/typedef
Версия PHP: 7.0

Эта библиотека предназначена непосредственно для работы с типами.

Вы можете определить собственный тип данных с помощью функции Tea\typedef:

function typedef(string $aName, IType $aType): IType;

Вы можете как создать и инстанцировать собственный класс, реализующий интерфейс Tea\Typedef\IType, так и использовать встроенные.

Для обращения к типу предназначена функция Tea\type:

function type(string $aName): IType;

Она принимает в качестве аргумента имя типа (аргумент aName функции typedef), и возвращает соответствующий объект.

Чтобы проверить значение на соответствие типу, воспользуйтесь функцией Tea\is:

function is($aValue, IType $aType): bool;

или методом validate самого объекта типа:

function IType::validate($aValue): bool;

Определены следующие встроенные типы (пространство имён Tea):

function bool(): BoolType;

Логическое значение true/false.

function number(float $aMin = null, float $aMax = null): NumericType;
function int(int $aMin = null, int $aMax = null): IntType;
function uint(int $aMax = null): UIntType;

Числовые типы.

Тип NumericType соответствует PHP-типам int и float.

Являющийся его наследником тип IntType соответствует только PHP-типу int.

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

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

function string(int $aLength = null): StringType;

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

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

function regexp(string $aRegularExpression): RegExpType;

Регулярное выражение.

function enum(...$aValues): EnumType;

Перечислимый тип.

Ограничивает множество допустимых значений заданным набором.

function object(string $aClass = null): ObjectType;

Объектный тип.

Значение может быть только объектом заданного класса (интерфейсы так же допустимы).

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

function nullable(IType $aType): NullableType;

Nullable-тип.

Дополняет множество допустимых значений дочернего типа значением null.

function any(IType ...$aTypes): MultiType;

Множественный тип.

Объединяет множества допустимых значений всех дочерних типов.

function lot(int $aLength = null): ArrayType;

Массивный тип (ключевое слово array не допустимо в качестве имени функции), может быть ограничен по максимальной длине.

Значение может быть массивом или объектом, реализующим интерфейсы ArrayAccess, Countable и Traversable (вы можете дополнительно ограничить множество допустимых значений с помощью self-return методов acceptArray и acceptObject).

Чтобы задать допустимый тип значений массива, используйте self-return метод of(IType), а для ключей используйте self-return метод by(IType). Если вы зададите тип ключей, отличный от PHP-типов int и string, тип будет иметь смысл только в отношении объектов, поскольку у массивов PHP не может быть ключей других типов.

Так же, как и для строкового типа, вы можете использовать self-return метод fix, чтобы сделать ограничение по длине строгим.

function struct(IField ...$aFields): StructType;

Структурный тип.

Значение, так же как и в случае массивного типа, может быть массивом или объектом с массивным доступом, и так же может быть дополнительно ограничено с помощью self-return методов acceptArray и acceptObject.

Членами структурного типа являются поля — объекты класса, реализующего интерфейс Tea\Typedef\IField. Переданное для валидации значение является валидным, если оно является массивом или объектом с массивным доступом (в соответствии с дополнительными ограничениями) и проходит валидацию всех полей.

Определены следующие встроенные виды полей:

function field(string $aName, IType $aType = null): Field;

Обычное поле. Не является самостоятельным типом.

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

function optional(IField $aField): OptionalField;

Опциональное поле. Не является самостоятельным типом.

Допускает отсутствие в переданном значении ключа, соответствующего дочернему полю.

function union(IField ...$aFields): Union;

Объединение. Является самостоятельным типом.

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

Для наглядной демонстрации работы библиотеки рассмотрим следующий пример:

typedef('input', struct(
	field('name', string()),
	field('authors', any(
		string(),
		lot()->of(string())
	)),
	optional(union(
		field('text', string()),
		field('content', struct(
			field('title', string(255)),
			optional(field('annotation', string(65535))),
			field('text', string()),
			optional(field('pages', nullable(uint(5000))))
		))
	)),
	field('read', enum(false, true, 0, 1, 'yes', 'no'))
));

if (PHP_SAPI === 'cli') {
	$input = [];
	parse_str(implode('&', array_slice($argv, 1)), $input);
} else {
	$input = $_GET;
}
echo "Validation: " . (is($input, type('input')) ? 'success' : 'failed') . "\n";

Этот код проверяет корректность переданного описания элемента книжной серии:
  • Обязательный параметр name должен быть строкой произвольной длины.
  • Обязательный параметр authors должен быть строкой произвольной длины или массивом таких строк.
  • Может быть передан параметр text, являющийся строкой произвольной длины, либо составной параметр content.
  • Обязательный параметр read должен иметь одно из указанных значений.


Такой набор параметров будет валидным:
name="The Lord of the Rings"
authors[]="J. R. R. Tolkien"
content[title]="The Return of the King"
content[text]=...
read=yes


А такой не пройдёт проверку:
name="The Lord of the Rings"
authors[]="J. R. R. Tolkien"
text=...
content[title]="The Return of the King"
content[text]=...
read=yes


generics


Репозиторий на GitHub: https://github.com/perspectea/generics
Версия PHP: 7.0

Эта библиотека вводит некоторое подобие дженериков. Основными являются два вида объектов-массивов:

Tea\Generics\IndexedArray(array $aValues = null, callable $aValueConstraintCallback = null);

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

function ($aValue): bool;

Tea\Generics\AssocArray(array $aValues = null, callable $aKeyConstraintCallback = null, callable $aValueConstraintCallback = null);

Ассоциативный массив. Для него аналогичным образом могут быть заданы ограничения значений ключей и элементов. Ключами ассоциативного массива могут быть любые значения, а не только целые числа и строки.

Так же определены следующие встроенные конструкторы (пространство имён Tea):

function values(...$aValues): IndexedArray;

Индексированный массив с произвольными значениями.

function numbers(float ...$aValues): NumericArray;
function integers(int ...$aValues): IntArray;
function cardinals(int ...$aValues): UIntArray

Индексированный массив чисел. Соответственно любых (float и int), целых (int) и беззнаковых целых (int >= 0).

function strings(string ...$aValues): StringArray

Индексированный массив строк.

function objects(string $aClass, array $aValues = null): ObjectArray;

Индексированный массив объектов заданного класса (интерфейса).

function map(array $aItems = null): AssocArray;

Ассоциативный массив с произвольными ключами и значениями.

function dict(array $aItems = null): Dictionary;

Ассоциативный массив со строковыми ключами и произвольными значениями.

function hash(array $aItems = null): StringDictionary;

Ассоциативный массив со строковыми ключами и значениями.

function collection(IType $aType, array $aValues = null): Collection;

Индексированный массив значений, соответствующих заданному типу (см. typedef).

Вместо заключения


Хотя всё это — в некоторой степени набор велосипедов, но я надеюсь, что он может кому-то пригодиться в работе. typedef может быть удобен для проверки параметров скрипта вместе с их преобразованием с помощью json_decode. А «дженерики» (хотя это не совсем дженерики в привычном понимании) могут пригодиться для ограничения типов массивов в аргументах с помощью уже готовых инструментов.

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

image

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

Благодарю за ваше внимание!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331160/


Метки:  

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

Воскресенье, 18 Июня 2017 г. 15:31 + в цитатник
До нас волнами продолжают докатываться обновления WWDC и I/O – на этой неделе были статьи про ARKit, изменения в App Store, новики Android O. А кроме того про мошеннические заработки, возможности Firebase, наступившее будущее от Яндекс.



Тестируем возможности ARKit. Создаем игру с дополненной реальностью

На WWDC 2017 Apple анонсировала ARKit — SDK для работы с дополненной реальностью. Благодаря ему порог вхождения в эту технологию стал значительно ниже. Можно ожидать появления большого количества качественных игр и приложений.

May the Code Review be with you

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

Как технологии Яндекс.Такси приближают будущее личного и общественного транспорта

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

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

iOS


Android


Windows


Разработка


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


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



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

https://habrahabr.ru/post/331152/


Метки:  

Список доменов в зоне ru/su/tatar/рф/дети утек в публичный доступ из-за некорректной настройки DNS

Воскресенье, 18 Июня 2017 г. 14:45 + в цитатник
Начиная с сегодняшнего утра, на части DNS, обслуживающих российские доменные зоны, разрешен трансфер (AXFR), который позволяет получить полный список доменных имен зон .ru, .su, .tatar, .рф и .дети.

AXFR-запрос доступен на следующих серверах:
  • a.dns.ripn.net
  • b.dns.ripn.net
  • d.dns.ripn.net

Чтобы получить все домены зоны, можно воспользоваться утилитой dig из состава BIND:
$ dig axfr su. @a.dns.ripn.net

; <<>> DiG 9.10.4-P8-RedHat-9.10.4-5.P8.fc25 <<>> axfr su. @a.dns.ripn.net
;; global options: +cmd
SU.                     345600  IN      SOA     a.dns.ripn.net. hostmaster.ripn.net. 650170097 86400 14400 2592000 3600
SU.                     345600  IN      RRSIG   SOA 8 1 345600 20170728095315 20170618085528 12810 su. AMqjxdOOp9rIVMk9gvH6TiXEB8FAWZn0xbqxxYxsQMYrqCUE49AXSUQ1 nESJ//bPE8g7mNa/KCqrt21icAOG6+gzwes+TZ2nu62dwbwH3IeEoxry GZ+uEIGlQfjPC6AxRuPELnaKRpLUy+Ir28ScJDLjIBewVs3J7zAHXw/K bYU=
SU.                     345600  IN      NS      a.dns.ripn.net.
SU.                     345600  IN      NS      b.dns.ripn.net.
SU.                     345600  IN      NS      d.dns.ripn.net.
SU.                     345600  IN      NS      e.dns.ripn.net.
SU.                     345600  IN      NS      f.dns.ripn.net.
…
0--0.SU.                345600  IN      NS      ns1.shop.reg.ru.
0--0.SU.                345600  IN      NS      ns2.shop.reg.ru.
0--GDE-KUPIT-DOMEN-DESHEVO--RU--SU--COM--NET--ORG--INFO--ME--TV.SU. 345600 IN NS ns1.hostmonster.com.
0--GDE-KUPIT-DOMEN-DESHEVO--RU--SU--COM--NET--ORG--INFO--ME--TV.SU. 345600 IN NS ns2.hostmonster.com.
0-0.SU.                 345600  IN      NS      dns1.yandex.net.
0-0.SU.                 345600  IN      NS      dns2.yandex.net.
0-0-0.SU.               345600  IN      NS      ns1.shop.reg.ru.
0-0-0.SU.               345600  IN      NS      ns2.shop.reg.ru.
0-1.SU.                 345600  IN      NS      dns1.elasticweb.org.
0-1.SU.                 345600  IN      NS      dns2.elasticweb.org.
0-3.SU.                 345600  IN      NS      ns1.timeweb.ru.
0-3.SU.                 345600  IN      NS      ns2.timeweb.ru.
0-4.SU.                 345600  IN      NS      ns.hostland.ru.
0-4.SU.                 345600  IN      NS      ns3.hostland.ru.
…

Зона Количество доменов
.ru 5325898
.su 109719
.tatar 869
.рф 796707
.дети 1066

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

Данная особенность DNS была обнаружена mandatoryprogrammer (Matthew Bryant), который давно занимается отслеживанием изменений всех доменных зон автоматизированными средствами. Зона .ru такая большая, что его ПО упало от недостатка оперативной памяти. В прошлом году он обнаружил возможность получения всех доменов северокорейской зоны .kp, используя этот же метод.

Дампы зон, сжатые xz, можно скачать через Bittorrent и I2P Bittorrent, torrent-файлом или по следующей Magnet-ссылке:
magnet:?xt=urn:btih:3457106ce62b2707639978d802e20567e278aa39&dn=Russian%20domain%20zones&tr=udp%3a%2f%2ftracker.leechers-paradise.org%3a6969&tr=udp%3a%2f%2ftracker.coppersurfer.tk%3a6969%2fannounce&tr=udp%3a%2f%2fp4p.arenabg.com%3a1337%2fannounce&tr=udp%3a%2f%2ftracker.zer0day.to%3a1337%2fannounce

Не обязательно их распаковывать, можно работать с ними в сжатом виде. Используйте xzcat вместо cat, xzdiff вместо diff, xzgrep вместо grep, xzless вместо less.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331144/


Метки:  

[Из песочницы] Система синтеза самосинхронных схем Petrify: проблемы и их решение

Воскресенье, 18 Июня 2017 г. 13:58 + в цитатник
Сказать, что Petrify решает, поставленные перед ней задачи, можно лишь с большой натяжкой. Вернее она кое-что может для небольших заданий (где количество сигналов едва превышает 20), проблема взрыва состояний так и не была решена. Но и для таких задач удовлетворительный результат не гарантирован. Декомпозиция далеко не всегда дает приемлемые результаты.

В чем причина этих неудач? Я бы назвал 3 основные:

1. Увлеченность STG. Да, это красивая, забавная модель, очень интересно играть маркерами и т.п. Но, подумайте, процесс переключения сигналов схемы это такой же процесс как выполнение какой-либо программы. Мы используем для описания программы сети Петри? Для чего тогда они нужны при описании процессов, происходящих в схеме? В результате разработчики Petrify львиную долю своих усилий потратили на изучение свойств сетей Петри. А собственно задачи синтеза схем так и не были решены.

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

3. Неспособность разобраться в причинах возникающих проблем. Но об этом ниже.

А есть ли в Petrify, что-либо хорошее? Да, есть! Это — ДЕКОМПОЗИЦИЯ. На самом деле это естественный, фундаментальный и прекраснейший метод. Важно только разобраться в причинах, почему обычно невозможна декомпозиция до минимальных двухвходовых элементов без нарушения спид-индепендед. А причина этого — кратные сигналы. Под этим я подразумеваю сигналы, которые переключаются более чем 2 раза за цикл, или же сигналы переключающиеся в двух и более альтернативных ветвях. Авторами Petrify этот факт установлен не был, чему свидетельство «жадный» алгоритм добавления дополнительных сигналов. При этом количество дополнительных сигналов уменьшается за счет увеличения числа переключений таких сигналов. Да, это уменьшает объем вычислений (которые, как я покажу ниже вообще не нужны), но создает дополнительные проблемы для декомпозиции.

Покажу на примере что я имею ввиду. Рассмотрим поведение со следующими ограничениями:

1. нет выбора;
2. нет параллелизма;
3. нет входных сигналов;
4. поведение знакочередуемое, за "+" следует "-", за "-" — "+";
5. все сигналы переключаются строго по 2 раза.

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

было:

$$display$$ ... x+ ... x- ... $$display$$


стало:

$$display$$ ... y- x+ ... x- y+ ... $$display$$


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

$$display$$x=NOR(y,AND(a,b,c,...,d)) $$display$$


Поведение сигналов этого уравнения выглядит так:

$$display$$... d- ... y- x+ ... c- ... d+ ... b- ... c+ ... a- ... b+ ... a+ x- y+ ... $$display$$


Вычислять такое уравнение нет необходимости. Нужно на графе найти цепочку подхватов a, b, c, d (против часовой стрелки) от x-, перескакивающую через x+. CSC свойство это обеспечивает.
А теперь сделаем декомпозицию этого уравнения:

$inline$x=NOR(y,f) $inline$
$inline$f=AND(g,a) $inline$
$inline$g=AND(h,b) $inline$
$inline$h=AND(c,d) $inline$

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

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

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

Снимем ограничение 4 — требование знакочередуемости. В получающейся итоговой схеме все двухвходовые элементы имеют два вида поведения:
image
Отмена ограничения 4 может создать только одну проблему: рассогласованность направления переключений между сигналами a и b. Это исправляется введением инвертора (сигнал c). Опять же без нарушения спид-индепендед.
image
В связи с этим знаки можно расставить в самом конце процесса синтеза схемы (с целью минимизации количества дополнительных инверторов).

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

1 класс. В упрощенном виде это переключение входного сигнала дважды подряд:

$$display$$... n+ n- ... $$display$$


В этом случае приходится вставлять дополнительные события перед входным. Иначе схему вообще нельзя построить. Делается это самым щадящим образом. Исправляется так:

$$display$$... n+ a- b+ n- c+ ... d+ b- a+ c- ... d- ... $$display$$


При этом:

$inline$a=NOR(n,b) $inline$
$inline$b=NOR(d,a) $inline$
$inline$c=NOR(a,n) $inline$

Сигналы a, b, c объявляются псевдовходными, т.е. вводится запрет на вставку дополнительных событий перед ними. Далее можно работать с поведением как описано выше, при этом сигнал n вообще можно вывести из рассмотрения.

2 класс — выбор в зависимости от порядка переключения двух входных сигналов:
image

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

$inline$e=NAND(a,f) $inline$
$inline$j=NAND(e,h) $inline$
$inline$i=NOT(j) $inline$
$inline$h=NAND(b,j) $inline$
$inline$f=NAND(b,i) $inline$
$inline$k=NOT(f) $inline$
$inline$g=NAND(a,k) $inline$

Сигналы e, j, i, h, f, k, g объявляются псевдовходными. Сигналы a, b можно убрать из рассмотрения. Более сложная коррекция в верхней ветви обусловлена порядком переключений a- и b-. Если в верхней ветви их поменять местами, то решение будет такое же как в нижней ветви.

3 класс — выбор по уровню. Выбор делается в момент переключения входного сигнала a+ в зависимости от значения входного сигнала b, установившегося к этому моменту.
image

Цель коррекции такая же, как в предыдущем случае.
image

$inline$g=NAND(a,i) $inline$
$inline$h=NOR(b,g) $inline$
$inline$f=NAND(a,b) $inline$
$inline$j=NOR(f,g) $inline$
Сигналы g, h, f, j объявляются псевдовходными. Сигналы a, b, g далее можно не рассматривать. Выбор теперь определяется событиями h+ и j+. Как видно здесь даже нет нарушения спид-индепендент.

Теперь снимем ограничение 5, введем кратные сигналы.

$$display$$... x- ... x+ ... x- ... x+ ... $$display$$


Для нейтрализации кратного сигнала x введем дополнительные сигналы.

$$display$$... a+ x- b+ ... a- x+ b- ... c+ d- ... f+ x- g+ ... f- x+ g- ... c- d+ ... $$display$$


$inline$x=NOR(a,f) $inline$
$inline$b=NOR(x,c) $inline$
$inline$g=NOR(x,d) $inline$

Сигналы x, b, g объявляются псевдовходными. Сигнал x дальше может не рассматриваться.

Отменим ограничение 2, введем параллелизм. С параллелизмом связаны следующие проблемы.

1. Нужно обеспечить синхронизацию параллельных ветвей.
image
$inline$f=NAND(a,b) $inline$
$inline$g=NOR(f,c) $inline$

Сигналы f, g объявляются псевдовходными.

2. Событие a+ не может быть причиной события b-, если события a- и b+ параллельны.
image
Кроме вышеперечисленных проблем параллелизм не препятствует декомпозиции.

Наконец, отменим ограничение 1, введем выбор.

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

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

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

Например: для дуги 4 множество достижимых дуг — {4, 5, 6, 1, 2}. Для дуги 3 — {3, 1, 2}. Алгоритм синтеза таков. Для каждой точки выбора, для обеих ее выходных дуг ищем множества достижимых дуг.

1. Если найдется точка выбора, для которой множества достижимых дуг не пересекаются. По этой точке можно разделить поведение на отдельные части. Для этого изолируем сигналы, которые переключаются в обоих частях.
image
$inline$x=NAND(a,b) $inline$
$inline$c=NAND(x,g) $inline$
$inline$d=NAND(x,f) $inline$

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

2. Если для всех точек выбора множества достижимых дуг пересекаются, но найдется точка выбора, где эти множества не совпадают. По этой точке поведение можно разделить на две части, но более сложным образом.
image
Разделение проходит по точке выбора с выходными дугами 4 и 3. В остальном все тоже: изоляция общих сигналов и разделение на два отдельных поведения с меньшим количеством точек выбора.
3. Если для всех точек выбора множества достижимых дуг совпадают. Берем любую дугу и изолируем все сигналы которые переключаются в ней нечетное число раз.
image
$inline$x= NAND(a,y) $inline$
$inline$y=NAND(b,x) $inline$
$inline$c=NOR(a,y) $inline$
$inline$d=NOR(b,x) $inline$

Сигналы x, y, c, d теперь псевдовходные. После этого изолируем кратные сигналы. В выбранной дуге остались только сигналы, которые переключаются только в этой дуге (по два раза). Такую дугу можно рассматривать как полноценное циклическое поведение. Из исходного поведения эта дуга изымается, так что количество точек выбора уменьшается на 1.

Таким образом поведение с выбором постепенно дробится до поведений без выбора.

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

Все описанные проблемы присущи и синтезу в произвольной элементной базе. Только решение этих проблем обычно носит случайный, не явный характер. И как следствие этого они избыточны. К примеру, я сравнивал свои решения с бенчмарками Petrify. Мои решения в 1.5 раза меньше. И это при том, что Petrify позволяет себе такие вольности, как использование нестандартных элементов (C-элемент), использование элементов с двухуровневой логикой, гораздо меньшее ограничение по количеству входов элемента, ограничения по задержкам при использовании входных инверторов.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331142/


Метки:  

[Из песочницы] История создания одной игры, или Все, что нас не убивает, делает нас сильнее

Воскресенье, 18 Июня 2017 г. 13:42 + в цитатник
Всем здравствуйте! Год назад, 13.6.2016, мы дали старт нашему проекту.


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

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

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

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

Как все начиналось


Наша небольшая команда разработчиков вышла из геймеров. Мы играли в World of Tanks Generals. Когда в апреле 2016 года команда WoT Generals сообщила о том, что их проект больше не будет обновляться, мы поняли, что теряем мир, в котором хочется жить. Нужно было создавать свой.

И мы его создали!

Но клонирование – не наш путь. Оттолкнувшись от идеи, команда сделала собственный полностью оригинальный проект. Конечно, не обошлось без проблем и сложностей – а кому легко?

— 15.4.2016 Узнаем о приостановке игры WoT Generals.

+ 13.6.2016 Появляется идея создать свою игру, браузерную версию ККИ в сеттинге ВОВ. Обговариваем бюджет в +- 50 000 рублей. Из своих в команде – идейный вдохновитель, дизайнер, который начал работу над интерфейсом, два механика, переводчик для локализации игры на английский и несколько человек, которые вложили свои личные сбережения. На фрилансе находим программиста и художника для изображений на карточки.

Начинаем работу! Все полны позитива, уже начинаем считать прибыль, советуемся, какую лучше купить машину, дом, яхту, куда съездить отдохнуть и куда вложить прибыль. Ну а почему бы и нет, мы же разработчики?!

Первые сложности и первые радости


  • — 13.7.2016 Первый прокол. Понимаем, что заказанные и оплаченные изображения на карты в виде плакатов нам не подходят.

  • + 13.7.2016. Идем в соцсети. Создали группу ВКонтакте и Facebook, а также страничку в Twitter.

  • + 15.7.2016. Принимаем решение попытать свои силы на площадке GreenLight в Steam. Создаем несколько макетов (скриншотов) в фотошопе и собираем видеопрезентацию.

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

  • + 18.7.2016. В связи с походом на GreenLight, решаем, что будем создавать эмулятор игры в клиенте.

Начало работы


  • + 19.7.2016. Находим программиста, который начинает писать эмулятор клиента.

  • + 19.7.2016. Сообщаем о том, что через три недели запускаем Альфа-тест.

  • + 26.7.2016. Первый независимый видеообзор, на основе картинок из фотошопа и видеопостановки, на нашу игру.

  • + 26.7.2016 Вышли в ТОП 100 на GreenLight в Steam. Спустя несколько часов нам дают зеленый свет, мы проходим GreenLight и получаем возможность продавать игру через Steam.

Крылья мешают ходить, а нимб не дает спать по ночам. Начинаем осознавать, что мы крутые разработчики. Согласовываем свод святых правил, который состоит всего из одного правила, так сказать, свод одного святого правила, которое гласит, что даже если ГИГАНТЫ игровой индустрии, которые выдают игры класса ААА, будут предлагать нам миллионы долларов (а они будут нам их предлагать, мы в это верим, вопрос времени) за то, чтобы мы продали им права на наш шедевр – мы не согласимся, даже обсуждать и торговаться не будем!

Свершения и крушения


Удивительно, сколько всего может произойти за одну неделю.

  • + 27.7.2016 У нас появляется новостной сайт.

  • — 29.7.2016 Команду покидает дизайнер.

  • + 29.7.2016 Придумываем «ноу-хау» в ККИ. «Переходные карты». (Переходные карты не продаются за деньги: их можно только выиграть у противника в бою. Если игрок разыграет особые карты и потерпит поражение, они достанутся противнику.)

  • + 1.8.2016 Находим на фрилансе нового дизайнера.

  • — 3.8.2016 Понимаем, что эмулятор клиента игры не позволит нам интегрировать игру в Steam.

  • + 3.8.2016 Принимаем решение отказаться от браузерной версии игры и писать полноценный клиент. За работу берется программист, который написал нам эмулятор клиента.

Планы меняются


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

- 8.8.2016 Из проекта уходит программист, который работал над
браузерной версией игры.

+ 9.8.2016 У нас появляется страничка в магазине Steam.

+ 11.8.2016 Меняем дизайн игры практически на 90%.

+ 16.8.2016 На фрилансе находим звукорежисера и заказываем первые треки
(меню и боевка).

- 18.8.2016 Команду покидает один из двух механиков.

- 22.8.2016 Отменяем Альфа-тест.

+ 27.8.2016 Первый розыгрыш среди подписчиков группы ВКонтакте.

+ 5.9.2016 Открываем регистрацию на альфа-тест.

+ 9.9.2016 Сообщаем о том, что Альфа-тест проходит внутри команды и вместо
него будет сразу бета-тест.

+ 7.9.2016 Решаем ввести в игре двойные показатели на картах.
«Прочность» — «Броня» и «Урон» — «Бронебойность».

+ 15.9.2016 Первое видео обновленного дизайна игры.

+ 17.9.2016 Презентуем музыкальный трек (Главная тема/меню) для игры.


Вперед, и только вперед!


Так думали мы, в чем нас убеждали происходящие события.
Статус Дата Событие
+ 19.9.2016 В преддверии ЗБТ рассылаем пресс-релизы.

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

- 27.9.2016 Первый стрим/интервью.

+ 3.10.2016 В нашей группе ВКонтакте 500 подписчиков.

+ 5.10.2016 Презентуем первую сувенирную продукцию (кружки).

+ 6.10.2016 Презентуем музыкальный трек (боевка) для игры

+ 11.10.2016 Презентуем коллекционные карточки для Steam.


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

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

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

Начинаем с нуля


За два дня мы находим нового программиста, цена его работ высокая, но мы решаемся, лишь бы как можно быстрее получить первую рабочую сборку. Расписываем график работ, утверждаем, и он приступает к работе. Что дальше? А дальше – снова работа, и снова таблица.

Статус Дата Событие
+ 25.10.2016 Открытие нового сайта/форума.

- 27.10.2016 Сообщаем о том, что в связи с подставой предыдущего программиста
ЗБТ отменяется.

- 1.11.2016 Начинаем рубрику «Дневник разработчика» с отчетами о процессе
создания игры.

+ 11.11.2016 Проводим конкурс на лучшую «обзывалку» исходя из названия
Heroes of Card War.

+ 13.11.2016 Отказываемся от полного названия игры (перестаем его публиковать)
в пользу «обзывалки» аббревиатуры HoCWar, легко запомнить и
красиво звучит:-)

+ 13.11.2016 Вводим пакеты доступа.

- 3.11.2016 Команду покидает второй механик.

+ 5.11.2016 Находим новых механиков, два человека, не любители,
а профессионалы в играх жанра ККИ. Они знакомятся с наследством
предыдущих механиков и вносят свои корректировки, в лучшую сторону.
Первым делом отменяют двойные показатели на картах и возвращают
это дело в привычный и простой для всех вариант «Урон/Здоровье»

+ 5.11.2016 Переезд сайта на международный домен, .NET

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


Ноу-хау, которые потрясли мир Хоквар


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

+ 1.12.2016 Придумываем «ноу-хау» на тему бустеров в игре.(Вместо привычных всем бустеров, где после покупки игроки получают то количество карт, которое находится в самом бустере, в наших бустерах – смесь азарта с удачей. Мы предоставляем возможность выбрать определенное количество карт. Выбираешь бустер с 5 картами – можешь открыть любые 4 карты. Из 15 доступно 11 карт, из 35 доступно 23 карты. В каждом бустере есть гарантированные премиумные карты. Угадает игрок премиумные карты или нет, зависит только от удачи и магических ритуалов, в которые верят многие.

И вот очередные плюсы – 1.12.2016 презентуем наш первый рекламный арт, а 3.12.2016 – первый стрим, на котором презентуем геймплей игры. Наши механики играют несколько партий, рассказывают о процессе создания, делятся планами.

Предновогодние подарки и не только…


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

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

  • + 5.12.2016 Решаем загрузить рабочую сборку в Steam и поставить статус «Ранний доступ», да, игра еще очень сырая, но многие уже давно просили предоставить возможность опробовать игру.

  • — 7.12.2016 Понимаем, что один дизайнер не справляется, обращаемся за помощью к знакомому, профессиональному дизайнеру.

  • + 7.12.2016 Презентуем новый дизайн «Переходных» карт.

  • + 9.12.2016 Презентуем дизайн карт типа «Премиум»

  • + 11.12.2016 Презентуем дизайн карт типа «Трофейные»

  • — 15.12.2016 Команду покидает один из 2-х новых механиков.

  • + 19.12.2016 Рабочая сборка загружена в Steam, ждем одобрения модераторов.

  • — 21.12.2016 Модераторы Steam вернули сборку и указали на ошибки при интеграции.

  • + 22.12.2016 Исправили ошибки и отправили сборку снова в Steam.

  • — 22.12.2016 Модераторы Steam вернули сборку и указали на ошибки при интеграции.

  • + 23.12.2016 Исправили ошибки и отправили сборку снова в Steam.

  • — 24.12.2016 Модераторы Steam вернули сборку и указали на ошибки при интеграции.

  • + 25.12.2016 Исправили ошибки и отправили сборку снова в Steam.

  • + 29.12.2016 Игра прошла модерацию и стала доступна в Steam.

  • + 29.12.2016 Началось ЗБТ. Игра платная, так как в ней еще нет внутриигрового магазина.

  • + 29.12.2016 Первые продажи.

  • — 30.12.2016 Первые баги.

  • — 31.12.2016 Первое разочарование игроков.


Работа кипит


После новогодних праздников мы полны энтузиазма. Боремся с багами, пока не заканчиваются деньги. Подробнее? Ну что ж, подробности – в таблице.
Статус Дата Событие
+ 11.1.2017 Вводим в игру бота.

+ 13.1.2017 Частичное обновление интерфейса.

+ 17.1.2016 БОЛЬШАЯ раздача ключей доступа, 303 ключа.

+ 19.1.2017 Презентуем дизайн «Простых карт».

+ 25.1.2017 Заказываем шевроны в виде логотипа игры.

+ 3.2.2016 В игре появляется внутриигровой магазин, это означает, что мы
можем перевести игру в бесплатный доступ и начать ОБТ.

- 11.2.2017 Понимаем, что один программист не справляется, слишком много
багов и другой работы. Начинаем поиски второго программиста.

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

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

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

+ 19.2.2017 Идем на фриланс на поиски второго программиста. Люди начинают
отзываться, но посмотрев код — отказываются.

- 21.2.2017 Нам временно блокируют игру в Steam. Необходимо заменить
в игре практически всю графику.

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

+ 23.2.2017 Находим на фрилансе дизайнеров, не хотим терять много времени,
в связи с этим берем не 1, а 5 человек. Начинается процесс создания и
замены 95% графики в игре.

+ 26.2.2017 По поискам второго программиста появляются результаты.
Из более 15 желающих — несколько человек соглашаются взяться за наш
проект. Выбираем на наш взгляд самого толкового (как показала практика,
не прогадали, до сих пор с нами).

+- 27.2.2017 Нас покидает предыдущий (основной) программист.

- 29.2.2017 Мы узнаем, почему нас покинул предыдущий программист
(в коде косяк на косяке).


  • 3.2017 Весь март мы работаем над новым дизайном игры. Дорогое удовольствие. Заканчиваются деньги на дальнейшую разработку игры. Решаем создать краудфандинг-проект.

Краудфандинг – мы решаемся. Странная история со Steam


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

  • + 3.4.2017 Принимаем решение, что все необходимые тексты лучше заказать у профессионального копирайтера, идем на наш родной фриланс.

  • + 5.4.2017 Загружаем в Steam новую сборку после замены графики.

  • + 6.4.2017 Нам снимают блокировку в Steam.

  • + 9.4.2017 Сообщаем о том, что через неделю ОБТ.

И – новые трудности:

  • — 23.4.2017 Steam не переводят игру в бесплатное распространение, ссылаясь на то, что у тех, кто купил игру за реал, до перевода в бесплатное распространение, должно быть преимущество перед теми, кто будет получать игру бесплатно. ОБТ откладывается.

Вообще, интересная история. Мы отправили заявку в Steam и попросили перевести нашу игру в бесплатное распространение, администраторы Steam задали нам вопрос «Какое преимущество будут иметь игроки, которые приобрели игру за деньги?» Скажем честно, что мы не ожидали такого исхода дел и не были к этому готовы. Мы с самых первых дней указали, что наша игра будет Free-to-play и даже в информации о раннем доступе четко прописали “В момент ОБТ и после релиза игра станет Free-to-play, но будет присутствовать внутриигровой магазин” и все игроки, которые приобретали нашу игру — 100% знали о том, что в итоге игра будет распространяться бесплатно.

И вот, наконец, 25.4.2017 мы запускаем краудфандинг-проект на бумстартере.
Дальше – наша любимая таблица.
Статус Дата Событие
- 26.4.2017 Предыдущий дизайнер снова подает жалобу в Steam и нам повторно
блокируют игру.

+ 27.4.2017 Мы грозим предыдущему дизайнеру судом за клевету, и он
отзывает свою претензию в Steam.

+ 28.4.2017 Нам снимают блокировку в Steam.

+ 29.4.2017 Начинаем активную работу над способностями карт.

+ 3.5.2017 Steam переводит игру в бесплатное распространение. Начало ОБТ.
(Перевод на бесплатку решен. Модераторы Steam сперва предложили нам
вернуть деньги уже тем кто купил игру, потом договорились на том, что всем
кто приобрел игру за деньги, мы в качестве компенсации перед игроками,
которые будут получать игру бесплатно — предоставить им в будущем одно
из платных DLС — бесплатно. Что делать, наверное, это справедливо.)

+ 6.5.2017 Презентуем еще один тип карт, самые простые — солдатские,
изображение на которых стилизовано под рисунок карандашом, в
черно-белом варианте.

- 13.5.2017 Мы понимаем, что пролетели с краудфандинг-проектом на бумстартере.


Поиск инвестора и дальнейшая работа


Раз краудфандинг не дал результатов, мы решили искать инвестиции. Май 2017 оказался довольно насыщенным:

  • + 13.5.2017 Начинаем поиск инвестора. Публикуем заявки с предложениями на ряде площадок.

  • + 16.5.2017 Первая версия нашей рекламы в виде мультика 40х годов.

  • + 16.5.2017 Производим рассылку пресс-релиза.

  • + 17.5.2017 Начинают поступать первые предложения от инвесторов.

  • + 20.5.2017 Уничтожаем самый страшный баг нашей игры.

  • + 23.5.2017 Начинаем переговоры с одним из инвесторов.

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

  • + 27.5.2017 Находим общий язык с инвестором, начинаем сотрудничество.

  • + 28.5.2017 Получаем первые средства от инвестора и понимаем, что у нас есть будущее.

  • + 29.5.2017 Начинаем активно работать, думаем о расширение штата.

  • + 31.5.2017 Принимаем решение об участие в GTP Indie Cup.

Июнь. Подводим итоги


Июнь еще не закончился, поэтому событий немного – зато все положительные:

  • + 6.6.2017 Публикуем новый дизайн сайта.

  • + 13.6.2017 Первый выпуск нашей «газеты» HoCWar News

Подводим итоги, подсчитываем плюсы и минусы:

  • 85 Положительных моментов.

  • 32 Отрицательных моментов.

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

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

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

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

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

С уважением, команда разработчиков.

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

https://habrahabr.ru/post/331140/


Метки:  

Ловись Data большая и маленькая! (Краткий обзор курсов по Data Science от Cognitive Class)

Воскресенье, 18 Июня 2017 г. 13:31 + в цитатник

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


Итак Cognitive Class он же Big Data University от IBM (иногда сокращенно BDU) – портал с бесплатными курсами по тематике близкой к BIG Data и соответственно Data Science.


Хотите узнать, чему он может или не может Вас научить, тогда милости прошу под кат.



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


  1. Не дружу с мат. анализом и статистикой;
  2. Немного умею кодить (писать «быдло код» и как-то работающие «програмки»);
  3. Худо-бедно понимаю английский. Письменно — терпимо, на слух — плохо.
  4. Ранее сталкивался с on-line обучением.
  5. На момент регистрации в Cognitive Class, о Data Science не знал вообще ничего.

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


Будем исходить из того, что люди с разным «бэкграундом» будут смотреть на этот курс по-разному, поэтому не претендуя на объективность, начну:


Часть 1. Почему Cognitive Class?


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


Надо заметить, что в русскоязычном Интернет пространстве ни под новым брендом (Cognitive Class) ни под старым (Big Data University) портал особо не «светится». Скорее всего главная причина – он не переведен на русский язык.


Тем не менее, то что все материалы бесплатные, большая часть курсов базируется на open-source ПО, а по окончании выдают какие-то сертификаты и «бейджи»(о них позже), в сочетании с любопытством сделали свое дело. Ну и как плюс можно потренировать английский.


Часть 2. Batman Data Science: Начало


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


Каждый курс можно пройти по отдельности, а можно в составе учебной программы (learning path). За прохождение каждого учебного курса выдается электронный сертификат, за выполнение требований учебной программы — бейдж


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


Поскольку о Data Science на момент регистрации я не знал вообще ничего, сам бог велел начать с программы обучения Data Science Fundamentals, в принципе это же мне подтвердил местный «Скайнет». На сайте есть бот (Student Advisor), если ему написать: «Data Science», то он как раз присоветует эту учебную программу. Для каких-либо более сложных и душевных бесед бот не подходит, потому что он понимает похоже только ключевые слова из тем курсов.


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


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


Часть 3. Data Science Fundamentals


Программа состоит из следующих курсов в рекомендуемом порядке прохождения


  • Data Science 101 (первый бейдж)
  • Data Science Methodology
  • Data Science Hands-on with Open Source Tools
  • R 101

По окончанию всех — второй бейдж


Кратко о концепции «среднестатистического курса» на платформе.


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


Основной материал разбит на модули (похоже их всегда 5)


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


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


Схема оценки обычно 50%/50%. Первая половина за итоговые тесты модулей, вторая за итоговый экзамен. Порог прохождения обычно 70%. В некоторых курсах значения могут меняться, не поленитесь заглянуть в раздел Grading Scheme.


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


Сами по себе тесты довольно простые, на любые вопросы, кроме тех, где надо ткнуть true/false дается 2 попытки без штрафа (в true/false – как у сапера нет права на ошибку). Обычно в конце модуля 3-4 вопроса, большинство из них с одним вариантом ответа из 3-5 значений, иногда попадаются вопросы с галочкой, иногда с полем для ввода, вопросы обычно по материалу и простые до ужаса. Время на сдачу теста не ограничено, «тыкать» в ответы можно в любой момент.


Экзамен в отличии от тестов надо сдать ровно за 60 минут (но можно и раньше), в остальном похож на тесты, только вопросов побольше (10-20).


По окончании каждого курса дадут сертификат и если предусмотрено бейдж (о них ближе к концу)


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


Data Science 101 – Полезный, воодушевляющий курс, чем-то напоминает курсы «Как стать программистом за 2 часа от известных школ программирования». В первом же видео, почтенный ученый из Канады, и молодые ребята расскажут Вам, кто такой Data Scientist и с чем его едят.


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


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


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


С другой стороны, это же вводный курс, не будем хотеть от него чудес.


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


Но зато вы получите бейдж который с гордостью подтвердит то что вы: «This badge earner has an understanding of the possibilities and opportunities that data science, analytics and big data bring to new applications in any industry.» Ценность его примерно, такая же как у «зайчиков», которые вместо оценок в моем детстве ставили в тетрадку первоклассникам


О других буду писать еще более сжато:


Data Science Methodology – Для меня этот курс как ни странно оказался сложным, причем в первую очередь из-за языкового барьера, если специфика английского для IT, глазу привычна, то более научная специфика языка, вызывала затруднения. Сам по себе курс реальной практики по сути не дает, но описывает основные концепции, рассказывает, как примерно должен думать Data Scientist (буду называть его так, потому что «ученый по данным» как-то не так круто звучит)


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


Data Science Hands-on with Open Source Tools – Было бы странно если бы IBM в рамках своих курсов не продвигало бы свои разработки, данный курс познакомит вас с их инструментарием datascientistworkbench.com. Штука бесплатная, висит в облаке, один минус модули не очень быстро инициализируются. В рамках курса Вас научат пользоваться по всей видимости основными open-source инструментами, которые применяются для обработки данных (а может и нет, я профан в этом вопросе, поэтому буду верить IBM). Помимо вводной части основной упор будет сделан на следующие приложения: Jupyter Notebooks, Zeppelin Notebooks, RStudio IDE, Seahorse. Еще раз повторюсь все висит в облаке ставить ничего не надо.


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


R 101 – тут мы подробней изучим основы языка R и их версию RStudio. Лабораторные уже приобретают хоть какой-то смысл, местами даже надо будет, чуть-чуть подправить их код, чтобы получить нужные для ответов в тестах цифры (если память меня не подвела). Но опять курс – для совсем новичков, чуть сложнее чем «Hello world», так что не питайте иллюзий программировать на R вы тут вряд ли так сразу научитесь.


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


Часть 4. Сертификаты, бейджи и проблемы с закрытием курса.


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


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


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


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


Теперь про бейджи. Бейджи размещаются на сайте партнере https://www.youracclaim.com. (придется создавать еще 1 профиль), там вы можете в публичный доступ выставить все свои достижения и потом делиться ссылкой на профиль сразу со всеми, например в соц. сетях или резюме.


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


Если бейджа нет, первое на что стоит обратить внимание так это на вкладку Progress. Программа выдаст Вам сертификат, как только вы перевалите за пороговую планку (обычно 70%), а вот с бейджем сложнее. Обязательно убедитесь, что вы ответили на все вопросы в тестах (нажали Final Check там, где требуют). Если будет хоть 1 незачтенный вопрос в одном из курсов, учебную программу вам до конца не закроют.


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


«You were most recently in Get your completion certificate and badge. If you're done with that, choose another section on the left»


или от:


«You were most recently in Download your completion certificate. If you're done with that, choose another section on the left»,


то есть смысл перейти в те пункты куда он советует. Обязательно рекомендую запрашивать сертификат именно внизу вкладки «прогресс» по ссылке типа «Download your completion certificate» и там во все потыкать, заметил, что когда запрашиваешь сертификат на вкладке прогресс сверху он возможно не фиксирует тот факт, что обучение совсем закончилось.


Итак, мы разобрали стартовую учебную программу по Data science на площадке Cognitive class, для тех, кто уже устал от большого текста предлагаю перейти к заключению в конце статьи, для остальных бонус — краткое описание еще нескольких курсов.


Часть 5. Датый «Data» рыцарь или еще немного о Data Science


Поскольку первая учебная программа была пройдена за день, а особых знаний не прибавилось, логично было продвинуться дальше, тем более сами разработчики учебных программ советуют перейти к Data Science for Business, ну и еще я решил посмотреть в сторону курса Statistics 101, с него и начну.


Statistics 101 – Похоже к этому курсу ребята «сдулись», потому, что после определенного момента к видео не сделали субтитры, конечно есть автоматический перевод Youtube, но по мне это не очень удобно. С моим плохим английским курс кажется сложным, по крайней мере если вам раньше в ВУЗе не давалась статистика, сложно ожидать, что вот на «басурманском» языке к вам придет озарение. И тем не менее курс несложный и что-то полезное сообщает (средне квадратичное отклонение, медиана, дисперсия и т.д.). Может быть есть смысл посмотреть его перед курсами по Data Science, а может и нет, вам решать.


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


Data Science for Business


Состоит из:


  1. Data Privacy Fundamentals
  2. Digital Analytics & Regression
  3. Predictive Modeling Fundamentals

К первым двум курсам нет субтитров для видео (вроде).


Кратко про каждый:


Data Privacy Fundamentals – курс на примере канадского законодательства говорит о том, как важно соблюдать информационную безопасность. Кроме текста и видео в курсе будет одно упражнение, где нам с помощью заготовок на R, подскажут как легко взломать ненадежные пароли. Ну а на экзамене придется «хакнуть» пароль для бедолаги Джастина (можно не «хакать», а просто «включить голову»)


Digital Analytics & Regression – курс наконец-то дает хоть немножечко адекватной практики и демонстрации анализа «небольших» данных на R. Не абы что, но все же полезно.


Predictive Modeling Fundamentals I – ужасный курс, на видео мужик часто шипит в микрофон и говорит так как будто у него во рту … леденец, причем не знаю глюк или нет, но на youtube видео не выложено, а в плеере на сайте нельзя включить субтитры так, чтобы они вылезали как нормальные субтитры (получается только сбоку) в итоге просмотр видео превращается в пытку.


Курс заточен под еще одно творенье от IBM — SPSS Modeler, так что надо опять скачивать триал (в этот раз на 30 дней)


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


В рамках курса разбирают задачку про Титаник (как я понял тема, избитая до жути)


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


Заключение:


Предполагая, что в момент «развилки» не все прочитали часть 5, поэтому поделю свои впечатления на две части.


Пройдена только Data Science Fundamentals:


Ну в целом достаточно, для того, чтобы в самых общих чертах понять, что такое Data Science. Подготовки никакой не требуется, ни мат. анализом ни статистикой, ни программированием можно не владеть, главное «шпрейхать по-аглицки».


Думаю, итак очевидно, что за один день вы ничего толком не выучите и на 1500000 млн. рублей зарплату рассчитывать не стоит (я надеюсь вы еще не успели открыть «Хантер» и создать резюме?)


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


Пройдена Data Science Fundamentals + Data Science for Business + Statistics 101:


Рушит все надежды, потому что по-настоящему толковая практика так и не попалась, а курсы Data Science for Business + Statistics 101 выполнены несколько хуже по качеству чем Data Science Fundamentals, да еще и требуют установки триал версий программ от IBM.


Примеры в задачках не абы какие и оторваны во многом от реальности.


Наверное, пройдя все это, вы возможно, сдлаете для себя вывод — Data Science это ваше или нет, было ли вам до ужаса скучно или вы в восторге от колдовства над данными.


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


В любом случае, всем кто потратил время на обучение по программе от Cognitive class, советую не останавливаться на достигнутом. В конце концов даже у них там еще много чего интересного (Big Data, Hadoop, Scala и т.п.)


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


Спасибо за внимание, всем удачной недели!

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

https://habrahabr.ru/post/331118/


Метки:  

[Перевод] Не используйте return в Scala

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

Сегодня я бы хотел представить вашему вниманию перевод небольшой статьи Роберта Норриса, возможно, знакомого вам под никнеймом tpolecat. Этот человек достаточно хорошо известен в Scala-сообществе как автор бибилиотеки doobie и участник проекта cats.


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


Итак, каждый раз, когда на Coursera запускают курс Мартина, у нас на #scala появляются люди, вопрошающие, почему за return с них снимают очки стиля. Поэтому, вот вам ценный совет:


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

Взглянем на этот небольшой пример:


// Сложим в методе два инта и затем используем его,
// чтобы просуммировать список.
def add(n: Int, m: Int): Int = n + m
def sum(ns: Int*): Int = ns.foldLeft(0)(add)

scala> sum(33, 42, 99)
res0: Int = 174

// То же самое, но при помощи return.
def addR(n:Int, m:Int): Int = return n + m
def sumR(ns: Int*): Int = ns.foldLeft(0)(addR)

scala> sumR(33, 42, 99)
res1: Int = 174

Пока что все в порядке. Между sum и sumR нет очевидной разницы, что может навести вас на мысль о том, что return является просто необязательным ключевым словом. Но давайте слегка отрефакторим оба метода, вручную заинлайнив add и addR:


// Заинлайнили add.
def sum(ns: Int*): Int = ns.foldLeft(0)((n, m) => n + m)

scala> sum(33, 42, 99)
res2: Int = 174 // Вполне норм.

// Заинлайнили addR.
def sumR(ns: Int*): Int = ns.foldLeft(0)((n, m) => return n + m)

scala> sumR(33, 42, 99)
res3: Int = 33 // Хм...

Какого...?!


Если кратко, то:


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

В нашем втором примере оператор return не возвращает значение из анонимной функции — он возвращает значение из метода, внутри которого находится. Еще пример:


def foo: Int = {
  val sumR: List[Int] => Int = _.foldLeft(0)((n, m) => return n + m)
  sumR(List(1,2,3)) + sumR(List(4,5,6))
}

scala> foo
res4: Int = 1

Нелокальный возврат


Когда функциональный объект, содержащий вызов return, выполняется нелокально, прекращение вычисления и возврат результата из него происходит путём возбуждения исключения NonLocalReturnControl[A]. Эта деталь реализации легко и без особых церемоний просачивается наружу:


def lazily(s: => String): String =
  try s catch { case t: Throwable => t.toString }

def foo: String = lazily("foo")
def bar: String = lazily(return "bar")

scala> foo
res5: String = foo

scala> bar
res6: String = scala.runtime.NonLocalReturnControl

Если кто-нибудь мне сейчас возразит, что перехватывать Throwable — дурной тон, я могу ему ответить, что дурной тон — использовать исключения для управления потоком исполнения. Глупость под названием breakable из стандартной библиотеки устроена аналогичным образом и, подобно return, не должна никогда использоваться.


Ещё пример. Что, если оператор return оказывается замкнут в лямбда-выражение, которое остаётся живым даже после того, как его родной метод отработал? Возрадуйтесь, в вашем распоряжении бомба замедленного действия, которая рванет при первой же попытке использования.


scala> def foo: () => Int = () => return () => 1
foo: () => Int

scala> val x = foo
x: () => Int = 

scala> x()
scala.runtime.NonLocalReturnControl

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


Какой тип у return?


В конструкции return a возвращаемое выражение a должно соответствовать по типу результату метода, в котором находится return, однако выражение return a и само по себе имеет тип. Исходя из его смысла «прекратить дальнейшие вычисления», вы, должно быть, догадались какой тип оно имеет. Если нет, вот вам чутка просвещения:


def x: Int = { val a: Int = return 2; 1 } // результат 2

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


def x: Int = { val a: String = return 2; 1 }

Хм, тоже не ругается. Что вообще происходит? Каким бы ни был тип у return 2, он должен быть приводимым к Int и String одновременно. А так как оба эти класса являются final, а Int — еще и AnyVal, вы знаете, к чему всё идёт.


def x: Int = { val a: Nothing = return 2; 1 }

Именно так, к Nothing. А всякий раз, сталкиваясь с Nothing, вам было бы благоразумнее развернуться и пойти другой дорогой. Так как Nothing — необитаем (не существует ни одного значения этого типа), то и результат return не имеет никакой нормального представления в программе. Любое выражение, имеющее тип Nothing, при попытке его вычислить обязано либо войти в бесконечный цикл, либо завершить виртуальную машину, либо (методом исключения) передать управление куда-либо еще, что мы и можем тут наблюдать.


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


Return нарушает ссылочную прозрачность


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


def foo(n:Int): Int = {
  if (n < 100) n else return 100
}

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


def foo(n: Int): Int = {
  val a = return 100
  if (n < 100) n else a
}

Конечно, он не будет работать: выполнение return порождает побочный эффект.


Но что, если мне это действительно нужно?


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


// Складываем числа из списка до тех пор,
// пока их сумма меньше ста.
def max100(ns: List[Int]): Int =
  ns.foldLeft(0) { (n, m) =>
    if (n + m > 100)
      return 100
    else
      n + m
  }

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


def max100(ns: List[Int]): Int = {
  def go(ns: List[Int], a: Int): Int =
    if (a >= 100) 100
    else ns match {
      case n :: ns => go(ns, n + a)
      case Nil     => a
    }
  go(ns, 0)
}

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


От переводчика:
Большое спасибо Бортниковой Евгении за вычитку. Отдельная благодарность firegurafiku за уточнения в переводе. Спасибо Владу Ледовских, за пару дельных советов которые сделали перевод немного точнее.

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

https://habrahabr.ru/post/331138/


Метки:  

[Из песочницы] В чем сущность дизайна? Нахожу ответ на вопрос рассматривая UI и UX, новые термины в области веб-дизайна

Воскресенье, 18 Июня 2017 г. 13:00 + в цитатник

Метки:  

[Из песочницы] Альтернатива Emacs Lisp'у

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

image


Вы когда-нибудь искали альтернативу Emacs Lisp'у? Давайте попробуем добавить в Emacs ещё один язык программирования.


В этой статье:


  • Потенциальные преимущества, которые будут получены при возможности расширять Emacs на Go;
  • Определим способы взаимодействия Go и Emacs Lisp;
  • Затронем некоторые детали реализации описанного транскомпилятора;

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


В самом конце статьи представлена ссылка на work in progress проект, который позволяет конвертировать Go в Emacs Lisp.


Выбираем Emacs Go


Как и любой другой язык программирования, Emacs Lisp имеет ряд "недостатков", которые мы будем политкорректно назвать "design tradeoffs". Говорить "лучше" или "хуже" о тех или иных свойствах языка программирования на объективном уровне достаточно сложно, потому что почти всегда найдутся защитники противоположных позиций. Нам же, как пользователям языков программирования, можно стараться выбрать тот язык, чьи компромиссы нам принять проще в силу наших задач или личных предпочтений. Ключевой момент — возможность выбора.


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


  1. Использовать Emacs модули для запуска Go функций. Вдохновение можно черпать из проекта go-emacs.
  2. Найти (или написать) интерпретатор Go, встроить его в Emacs путём патчинга или теми же C модулями, а затем вызывать eval из редактора.
  3. Транслировать Go в Emacs Lisp байт-код.

Способов может быть больше, но ни один из них не будет ближе к "нативному" лиспу, чем (3). Он позволяет на уровне исполнения иметь ту же виртуальную машину, что и обычный Emacs Lisp.


Это, в свою очередь, означает, что:


  • Emacs Lisp код сможет вызывать транслированный Go код;
  • FFI бесплатен. Вызов уже определённой в Emacs функции из Go максимально эффективен;
  • Легко распространять сконвертированные пакеты (родной для Emacs формат);

Если вы в первый раз слышите о байт-коде Emacs'а, ознакомьтесь со статьёй Chris Wellons.


Почему Go?


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


Есть несколько причин из-за которых сделанный выбор становится более обоснованным. Основные из них:


  • Компилятор языка внутри стандартной библиотеки;
  • Лаконичная спецификация;
  • Скромный runtime;
  • Tooling;

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


  • Go — достаточно популярный язык с С-подобным синтаксисом (т.е. это тебе не Scheme);
  • Статическая типизация;

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


Пакеты go/* значительно упрощают написание инструментов для Go.


Не нужно писать parser, typechecker и прочие прелести frontend'а компилятора. За 20 строк кода мы можем получить AST и информацию о типах для целого пакета.


Документация, по большей части, хороша. А для go/types на мой взгляд — образцовая.


Изначально для меня это было убийственным аргументом. Задача казалась на 90% решённой благодаря этому секретному оружию: "осталось только преобразовать AST в байт-код Emacs'а".


Ложка дёгтя

На практике возникали сложности с теми или иными нюансами.


В первую очередь — запутанность API и дублирование разными пакетами похожих сущностей, да ещё и под одинаковыми именами. Часто одно и то же можно сделать через go/ast и go/types; не редко вам нужно перемешивать сущности из обоих пакетов (да-да, в том числе те, что с одинаковыми именами).


Ещё на удивление неудобной оказалась работа с import'ами и декларациями (ох уж этот ast.GenDecl).


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


Лаконичная спецификация


Создать реализацию, которая в большей степени (~80%) конформна спецификации — вполне посильная задача для одного человека. Спецификацию Go легко читать, её можно осилить за вечер.


Особенности спецификации:


  1. Некоторые моменты вызывают сомнения в однозначности трактовки. Цена краткости;
  2. Кроме спецификации есть ещё Effective Go. Без него в спецификации останутся белые пятна;

Скромный runtime


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


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


Tooling


Это чертовски приятно, когда многие привычные фичи работают в нескольких твоих любимых редакторах, причём единообразно.


Для Go многие функции, которые обычно переизобретаются для каждой IDE отдельно, реализованы в виде отдельных утилит. Самый простой пример, известный каждому Go разработчику — gofmt. В немалой степени этому способствуют упомянутые выше go/types,
go/build и остальные пакеты из группы go/*.


Sublime text, Emacs, Visual Studio Code — выбираешь любой из них, ставишь плагин(ы), и наслаждаешься рефакторингом через gorename, множеством линтеров и автоматическими import'ами. А автодополнение… превосходит company-elisp во многих аспектах.


Рефакторить и поддерживать проект на Emacs Lisp уже после 1000 строк кода лично мне уже некомфортно. Программировать Emacs на Go чуть ли не удобнее, чем на Emacs Lisp.


Как выглядит Emacs Go


Пофантазируем на тему того, как мог бы выглядеть Go для Emacs'а. Насколько он был бы удобен и функционален?


Мост типов


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


С примитивными типами вроде int, float64, string и другими всё более-менее просто. И в Go, и в Emacs Lisp эти типы присутствуют.


Интерес представляют слайсы, символьные типы (symbol) и беззнаковые целочисленные типы фиксированной разрядности (uintX).


  • Слайсы реализуем в рантайме (например, на том же Emacs Lisp);
  • Символы представляем в виде opaque типа;
  • Беззнаковую арифметику с детерминированным переполнением — эмулируем;

Тип, который может выразить "объект произвольного типа", который возвращается Emacs Lisp функцией, назовём lisp.Object. Его определение дано под спойлером lisp.Object: детали реализации.


Go slices

Для аналогии: слайсы в Go по своему "интерфейсу" — это std::vector из C++, но с возможностью брать полноценный subslice без копирования элементов.


Начнём с интуитивного представления {data, len, cap}.
data будет вектором, len и cap — числами. Чтобы хранить атрибуты выбираем improper list, где у нас нет финального nil, чтобы немного экономить память:
(cons data (cons len cap))


Почему список, а не вектор?

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


Более развёрнутый ответ на этот вопрос поможет найти дизассемблер (или таблица опкодов). Доступ к спискам из 2-3 элементов — очень эффективный. Чем ближе к голове списка, тем более ощутима разница. Атрибут data используется чаще всего, поэтому он в самом начале списка.


При N=4 можно считать, что список начинает уступать по эффективности в случае считывания последнего элемента, но остальные три атрибута всё так же более эффективны в доступе => даже для объектов из четырёх атрибутов я склонен полагать, что список является более удачной структурой, чем вектор.


Оговорка: это всё справедливо для виртуальной машины Emacs'а, её набора инструкций. Вырывать из контекста не стоит.


Операции slice-get/slice-set будут очень эффективными. У нас будет тот же aset/aget, но с одной дополнительной инструкцией car для извлечения атрибута data.


Но что будет, когда нам нужен subslice?


В C можно было бы data сделать указателем и сместить его, куда нужно. Адресация была бы такой же, 0-based. В нашем случае это невозможно, что приводит к необходимости хранить ещё и offset:
(cons data (cons offset (cons len cap)))


Для каждого slice-get/slice-set теперь нужно к индексу прибавлять offset.


Сравним байт-код для операции slice-get.


;; Обычный вектор
 ;; [vector]
  ;; [vector index]
aref     ;; [elem]

;; Slice без offset (не поддерживаем subslice)
 ;; [slice]
car     ;; [data]
 ;; [data index]
aref    ;; [elem]

;; Slice с поддержкой subslice
     ;; [slice]
dup         ;; [slice slice] (1)
car         ;; [slice data]
stack-ref 1 ;; [slice data slice]
cdr         ;; [slice data slice.cdr]
car         ;; [slice data offset]
     ;; [slice data offset index]
plus        ;; [slice data real-index]
aref        ;; [slice elem]
stack-set 1 ;; [elem] (2)

;; (1) Поскольку  может быть дорогим выражением, мы
;; вычисляем его единожды.

;; (2) Нам требуется восстановить инвариант стека и удалить
;; лишний slice со стека. 

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


Opaque types

Некоторые типы мы не захотим/не сможем выразить как Go структуры. К таким типам относятся lisp.Object, lisp.Symbol и lisp.Number.


Главной целью opaque типа для нас является запрет на произвольное создание объектов через литералы. С этим отлично справляются интерфейсные типы с неэкспортируемым методом.


type Symbol interface {
    symbol()
}

type Object interface {
    object()
    // Другие методы...
}

// Для создания объектов должна использоваться специальная функция-конструктор.

// Intern returns the canonical symbol with specified name.
func Intern(name string) Symbol

Функция Intern обрабатывается компилятором по-особому. Другими словами она — intrinsic функция.


Теперь мы можем быть уверены, что у этих особых типов такое API, которое мы хотим им придать, а не то, какое возможно по законам Go.


lisp.Object

Если lisp.Object представляет "любое значение", то почему мы не используем interface{}?


Вспомним, что такое interface{} в Go — это структура, хранящая в себе динамический тип объекта, плюс сам объект — "данные".


Это не совсем то, чего хотелось бы, потому что для Emacs'а такое представление "чего угодно" не является эффективным. lisp.Object нужен для того чтобы хранить unboxed Emacs Lisp значения,
которые легко можно передавать в функции лиспа и получать в качестве результата.


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


type Object interface {
    object()

    Int() int
    Float() float64
    String() string
    // ... etc.

    // Можно также предоставить следующие методы:
    IsInt() bool                // Предикат для проверки типа
    GetInt() (val int, ok bool) // Для "comma, ok"-style извлечения
    // ... аналоги для оставшихся типов.
}

Каждый вызов генерирует проверку типа. Если внутри lisp.Object хранится значение отличного от запрошенного типа, должен быть вызван panic. Чем-то напоминает API reflect.Value, не так ли?


Emacs Lisp из Go


Если сигнатура функции неизвестна, то единственное, что нам остаётся — это принимать вариативное количество аргументов произвольного типа, а возвращать lisp.Object.


pair := lisp.Call("cons", 1, "2")
a, b := lisp.Call("car", pair), lisp.Call("cdr", pair)
lisp.Call("insert", "Hello, Emacs!")
sum := lisp.Call("+", 1, 2).Int()

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


part := "c"
lisp.Insert("Hello, Emacs!")     // Возвращает void
s := lisp.Concat("a", "b", part) // Возвращает string, принимает ...string

FFI DSL

DSL для аннотирования функций можно написать на макросах.


;; Пример описания функций, доступных через FFI.
(ffi-declare
 (concat Concat (:string &parts) :string)
 (message Message (:string format :any &args) :string)
 (insert Insert (:any &args) :void)
 (+ IntAdd (:int &xs) :int))

;; Разворачивается макрос, например, в Go сигнатуры.

Разворачиваться такой макрос должен в Go сигнатуры функций. Нужно оставлять комментарий-директиву для сохранения информации о том, какую Lisp функцию следует вызывать.


// IntAdd - ... <комментарий функции + из Emacs>
//$GO-ffi:+
func IntAdd(xs ...int) int

// ... Остальные функции

Документацию можно подтягивать из Emacs'а функцией documentation. Получаем функции с известной арностью и при этом не теряем ценные docstrings.


Go из Emacs Lisp


Результатом транскомпиляции будет Emacs Lisp пакет, в котором все символы из Go имеют преобразованный вид.


Схема преобразования идентификаторов может быть, например, такой:


package "foo" func "f"       => "$GO-foo.f"
package "foo/bar" func "f"   => "$GO-foo/bar.g"
package "foo" func (typ) "m" => "$GO-foo.typ.m"
package "foo" var "v"        => "$GO-foo.v"

Соответственно для того, чтобы вызвать функцию или воспользоваться переменной, нужно знать какому Go пакету она принадлежала (и её название, разумеется). Префикс $GO позволяет избежать конфликтов с уже определёнными в Emacs именами.


Тонкости транскомпиляции


Bytecode или lapcode?


В качестве выходного формата можно выбирать среди трёх вариантов:


  1. Emacs Lisp код (source-to-source compilation)
  2. Bytecode
  3. Lapcode (Lisp Assembly Program)

Первый вариант сильно проигрывает остальным вариантам, потому что он не позволит эффективно транслировать return statement, а ещё в нём сложнее реализовать оператор goto (который есть в Go).


Второй и третий варианты по возможностям практически эквивалентны.


  • Bytecode — это аналог машинного кода, самый низкий уровень;
  • Lapcode — это язык ассемблера виртуальной машины со стековой архитектурой;

Компилятор Emacs'а умеет оптимизировать на уровне исходного кода и lapcode представления.
Если выбираем lapcode, то можем дополнительно применять низкоуровневые оптимизации,
реализованные Emacs разработчиками.


Недостатки lapcode

Lisp assembly program — это внутренний формат компилятора Emacs'а (IR). Документации по нему ещё меньше, чем по байт-коду.


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


Я так и не нашёл точного описания формата инструкций. Здесь помогает метод проб и ошибок, а также чтение исходников компилятора Emacs Lisp'а (вам понадобятся стальные нервы).


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


Go, который "бегает" внутри Emacs VM не может быть быстрее Emacs Lisp.


Или может?


В Emacs Lisp есть динамический scoping для переменных. Если вы заглянете в "emacs/lisp/emacs-lisp/byte-opt.el", то сможете найти множество отсылок к этой особенности языка; из-за неё некоторые оптимизации либо невозможны, либо значительно затруднены.


Констант в Emacs Lisp нет. Имена, объявленные с помощью defconstant менее неизменяемые, чем те, что определены через defvar. В Go константы встраиваются в место использования, что позволяет сворачивать больше константных выражений.


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


Трудности реализации


Даже без горутин есть такие возможности Go, которые не имеют очевидной и/или оптимальной реализации внутри Emacs VM.


Наиболее интересной трудностью являются указатели.


В контексте задачи мы можем выделить две категории значений в Emacs Lisp:


  • Ссылочные типы (string, vector, list/cons)
  • Типы-значения (integer и float)

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


Ещё не следует забывать, что для указателей определён оператор =,
поэтому предлагаемым решением должно соблюдаться тождество адресов объектов.


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


Если все числа изначально создавать в boxed виде (внутри cons),
сильно повысится количество аллокаций.
Распаковка будет требовать дополнительную инструкцию car при каждом считывании значения.


У реализации указателей через cons есть значительный изъян: &x != &x,
потому что (eq (cons x nil) (cons x nil)) всегда ложно.


Корректная эмуляция семантики указателей — это открытый для обсуждения вопрос.
Буду рад услышать ваши идеи для их реализации.


Seems like Go-ism inside Emacs


Проект goism — это инструмент, который позволяет получать из Go пакетов близкий к оптимальному Emacs Lisp байт-код.


Библиотека времени выполнения изначально была написана на лиспе, но с недавних пор полностью переписана на транслируемом в lapcode Go.
emacs/rt на данный момент — один из самых крупных пакетов, написанных с помощью goism.


На данный момент goism не особо дружелюбен по отношению к конечному пользователю,
придётся работать руками, чтобы правильно его собрать и настроить
(guick start guide должен упростить задачу).


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


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

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

https://habrahabr.ru/post/331134/


Метки:  

Вариант быстрой настройки системы управления оптовой компанией

Воскресенье, 18 Июня 2017 г. 11:43 + в цитатник
После настройки создания аналитического CRM на базе Vtiger 5.3 (1, 2) стал очевиден ее недостаток — необходимость создания модулей с привлечением программистов. Но поскольку Vtiger 5.3 выполнял свои функции, было решено оставить все как есть, следуя принципу — лучшее враг хорошего.



Спустя год выбирая программное обеспечение для управления проектным офисом (но совсем для другого проекта), я столкнулся с Адвантой.
Программа представляла собой нечто большее, чем просто среду для управления проектами. Не буду вдаваться в подробности, на сайте программы есть ее подробное описание, скажу лишь, что программа мне показалась конструктором, с помощью которого можно самостоятельно и быстро настроить любую среду управления.
Мне стало любопытно, возможно ли в ней воспроизвести CRM для оптовой компании. Оказалось можно, причем все настройки заняли у меня несколько часов.
Первым делом был настроен раздел «Управление взаимоотношениями с клиентами".
В карточке клиента были настроены его реквизиты (стадия ЖЦ, общая информация и т.д.).

image

Затем был создан раздел «Ассортиментная линейка», в котором находится информация о структуре потребления клиента в разрезе ассортиментных групп.



В разделе «Бренды» находится информация о структуре потребления клиента в разрезе брендов.


Разделы «История работы» и «Контакты» являются стандартными для любой CRM, мной лишь был сформирован набор необходимых полей.




В разделе «Наши конкуренты» находится информация об угрозах для отдельных ассортиментных групп и брендов компании.


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


На вкладке «Факт продаж» агрегируется информация о фактических продажах на основании графика поставок.




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



Информация по вкладкам «Факт продаж», «Бренды», «Ассортиментная линейка», «План продаж» агрегируется на уровне групп клиентов. Таким образом возможен переход по уровням иерархии клиентов.



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



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



Пример карточки бренда.



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



Для каждого поставщика выделен раздел «Управление финансами».



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



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



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

В ней возможно реализовать все четыре функции управления.
Планирование — через постановку задач в разрезе сотрудников, клиентов, брендов и ассортиментных групп
Организация — обсуждение рабочих вопросов с помощью инструментов Дискуссии, Согласование документов и назначение исполнителей на задачи.
Мотивация и контроль — с помощью визуализации прогресса достижения целей, расчета показателей и KPI.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331114/


Метки:  

[Перевод] Типичные распределения вероятности: шпаргалка data scientist-а

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

Метки:  

Функциональное программирование в Scala — нужно ли оно вообще?

Воскресенье, 18 Июня 2017 г. 09:32 + в цитатник
В этой статье я хотел бы на примере простого чисто функционального кода показать, как он видоизменяется при добавлении требований к тестируемости и расширяемости, и что же получается в итоге. Надеюсь, это будет интересно всем, кто интересуется дизайном ПО и функциональным дизайном в частности. Также желательно немного понимать язык Scala, ну или уметь быстро разбираться.

Попробуем написать простую программу, вычисляющую выражение 4 * x^3 + 2 * x^2 + abs(x). Поскольку это пост про функциональное программирование, оформим всё в виде функций, вынеся операции возведения в степень и модуля:

object Main {

  def square(v: Int): Int = v * v
  def cube(v: Int): Int = v * v * v
  def abs(v: Int): Int = if (v < 0) -v else v

  def fun(v: Int): Int = {
    4 * cube(v) + 2 * square(v) + abs(v)
  }

  println(fun(42))
}

Выглядит симпатично, не правда ли? Теперь добавим пару требований:

— мы хотим тестировать функцию fun(), используя свои реализации функций square, cube и abs вместо «зашитых» в текущую реализацию
— функция cube работает медленно — давайте её кешировать

Таким образом, fun должна принимать свои зависимости в виде аргументов, заодно можно сделать мемоизацию функции cube.

object Main {

  def square(v: Int): Int = v * v
  def cube(v: Int): Int = v * v * v
  def abs(v: Int): Int = if (v < 0) -v else v

  // выносим все зависимости в аргументы функции
  // сразу делаем частичное каррирование (два списка аргументов), чтобы упростить частичное применение аргументов чуть ниже
  def fun( 
    square: Int => Int,
    cube: Int => Int,
    abs: Int => Int)
    (v: Int): Int = {
    4 * cube(v) + 2 * square(v) + abs(v)
  }

  // делает мемоизацию - по функции одного аргумента возвращает функцию того же типа,
  // которая умеет себя кешировать
  def memoize[A, B](f: A => B): A => B = new mutable.HashMap[A, B] {
    override def apply(key: A): B = getOrElseUpdate(key, f(key))
  }

  val cachedCube = memoize(cube)

  // cachedFun - это лямбда с одним аргументом, умеющая кешировать cube. Тип функции - как в первом примере
  val cachedFun: Int => Int = fun(
    square = square,
    cube = cachedCube,
    abs = abs)

  println(cachedFun(42))
}

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

object Test3 {

  trait Ops {
    def square(v: Int): Int = v * v

    def cube(v: Int): Int = v * v * v

    def abs(v: Int): Int = if (v < 0) -v else v
  }

  def fun( //более симпатичная сигнатура, не так ли?
    ops: Ops)
    (v: Int): Int = {
    4 * ops.cube(v) + 2 * ops.square(v) + ops.abs(v)
  }

  // мемоизация уже не нужна - мы можем просто переопределить поведение методов
  // дополнительный бонус - мы управляем мутабельным состоянием явно
  // т.е. можем выбирать время жизни кеша - к примеру, не создавать Map здесь,
  // а использовать какую-то внешнюю реализацию. Из Guava к примеру.
  val cachedOps = new Ops {
    val cache = mutable.HashMap.empty[Int, Int]
    override def cube(v: Int): Int = cache.getOrElseUpdate(v, super.cube(v))
  }

  val realFun: Int => Int = fun(cachedOps)

  println(realFun(42))
}

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

object Main {

  trait Ops {
    def square(v: Int): Int = v * v

    def cube(v: Int): Int = v * v * v

    def abs(v: Int): Int = if (v < 0) -v else v
  }

  class MyFunctions(ops: Ops) {
    def fun(v: Int): Int = {
      4 * ops.cube(v) + 2 * ops.square(v) + ops.abs(v)
    }
  }

  val cachedOps = new Ops {
    val cache = mutable.HashMap.empty[Int, Int]
    override def cube(v: Int): Int = cache.getOrElseUpdate(v, super.cube(v))
  }

  val myFunctions = new MyFunctions(cachedOps)

  println(myFunctions.fun(42))
}

Таким образом, у нас получился классический ООП дизайн. Который гибче исходного варианта, который более типизирован (Int => Int уж точно менее понятен, чем MyFunctions.fun), который эффективен по быстродействию (ФП вариант не будет работать быстрее, а вот медленнее — легко), который просто понятнее.

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

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

Жду ваших комментариев )
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331130/


Метки:  

Терминология OneGet, NuGet, Chocolatey, PowerShellGet — разложим по полочкам

Воскресенье, 18 Июня 2017 г. 04:55 + в цитатник
В этой статье я хочу помочь разобраться в структуре пакетных менеджеров под Windows. Статья нацелена больше на тех, кто, как и я, пришли из мира Linux, где принято заходить в понимание процессов ниже уровня абстракций.
Уверен, что абстракции вы уже прочитали и без меня:
chocolatey для установки приложений, nuget — для установки зависимостей разработчиком.

Но это мало того грубо, так еще и неправда.

Итак, какие типы пакетов мы знаем из мира Linux? Внимание: не пакетные менеджеры, а именно сами пакеты. Самые распространенные условно делятся на две группы: ОС-зависимые (deb, rpm) или языко-зависимые (как правило, tar-болы). В принципе можно сказать, что первая группа — это приложения (утилиты), а вторые — зависимости (библиотеки). Но иногда это не так: среди пакетов ОС есть библиотеки, а среди языковых пакетов есть пакеты, устанавливающие еще и утилиты (например stdeb в pip или elastalert в npm) — если их устанавливать глобально, то получится как пакет ОС.

Возвращаемся к Windows.

Изначально здесь придумали тоже формат пакета. Сделан он был на замену старому формату msi/msu потому, что старый формат имел достаточно высокий входной порог для понимания, как его автоматизировать [вполне вероятно, что я сейчас брежу]. В общем и целом новый формат очень похож на rpm. У него даже есть spec-файл. Имя ему — NuGet, но расширение .nupkg. Внутри этого пакета есть директории, файлы и инсталляционные скрипты — все нам, линуксойдам, привычно и знакомо.

Теперь давайте вспомним, какие пакетные менеджеры мы знаем. Для ОС это apt, yum,… Для языковых: pip, gem, npm, cpan, cpm,…
Что для Windows?
Тут мы знакомимся с новым NuGet. Есть NuGet-пакет, а есть одноименный nuget.exe — утилита, которая умеет эти пакеты скачивать и разархивировать.

Какой расклад мы получаем:
Debian: apt(deb) + pip + npm + gem +…
RHEL: yum(rpm) + pip + npm + gem +…
Windows: nuget(nupkg) + pip + npm + gem +…
обратите внимание, для msi пакетного менеджера так и не создали (это не совсем так, но пока для простоты).

И вот здесь начинается отличие от Linux.
В мире Windows появился какой-то талантливый парень, который решил, что он хочет устанавливать все одной командой. И начал писать open-source'ный проект OneGet (его проприетарное название: PackageManager, который является одним из модулей PowerShell версии >=5.0).
OneGet — это абстрактный интерфейс, который умеет разговаривать с каждым пакетным менеджером на его языке. OneGet публикует для нас набор унифицированных команд. Например, команду Install-Package, которая является wrapper'ом каждый раз для разных команд.
Что получается: мы подключаем к OneGet несколько пакетных менеджеров. Например: NuGet, PIP, NPM.
Далее, если мы хотим поставить какой-то питоний пакет, то мы пишем:
Install-Package <имя pip-пакета>

OneGet преобразует это в:
pip install <имя pip-пакета>

А теперь мы хотим поставить пакет NuGet и запускаем:
Install-Package <имя nupkg-пакета>

В этот раз команда вызвала:
nuget install <имя nupkg-пакета>


Я соврал. На самом деле Install-Package не вызывает эти команды в бэкграунде — пакетные менеджеры более не используются, выпали из пищевой цепочки. Вместо них установлены специализированные пакетные провайдеры (считайте плагины PowerShell) для управления инородными пакетами. И эти провайдеры занимаются задачей установки вместо привычных нам пакетных менеджеров. А OneGet над ними начальник. За зависимостями следят сами провайдеры.

И тут мы знакомимся с третьим NuGet — NuGet-пакетный провайдер. Я с самого начала статьи мечтал взорвать ваш мозг: NuGet — это пакетный провайдер, который пришел на смену пакетному менеджеру NuGet(nuget.exe), чтобы управлять пакетами NuGet(.nupkg).

Здесь очень важный момент. Старые пакетные менеджеры имели каждый свой список репозиториев (Source в терминологии Windows):
PS C:\> nuget source list
Registered Sources:

  1.  nuget.org [Enabled]
      https://api.nuget.org/v3/index.json
  2.  ABC [Enabled]
      http://<тут_я_спрятал_IP>/artifactory/api/nuget/<имя_репо>

Или:
PS C:\> gem sources
*** CURRENT SOURCES ***

https://rubygems.org

OneGet опирается не на них, а на заменивших их провайдеров. А список репозиториев один для всех, но с указанием, кому какой репозиторий принадлежит — по аналогии с внешними ключами БД.

Смотрим сначала список пакетных менеджеров:
PS C:\> Get-PackageProvider

Name                     Version          DynamicOptions
----                     -------          --------------
Chocolatey               2.8.5.130        SkipDependencies, ContinueOnFailure, ExcludeVers
ChocolateyGet            1.0.0.1          AdditionalArguments
msi                      3.0.0.0          AdditionalArguments
msu                      3.0.0.0
NuGet                    2.8.5.208        Destination, ExcludeVersion, Scope, SkipDependen
PowerShellGet            1.0.0.1          PackageManagementProvider, Type, Scope, AllowClo
Programs                 3.0.0.0          IncludeWindowsInstaller, IncludeSystemComponent

А теперь смотрим кому какой репозиторий принадлежит:
PS C:\> Get-PackageSource

Name                             ProviderName     IsTrusted  Location
----                             ------------     ---------  --------
nuget.org                        NuGet            False      https://api.nuget.org/v3/index.json
PSGallery                        PowerShellGet    False      https://www.powershellgallery....
chocolatey                       Chocolatey       False      http://chocolatey.org/api/v2/


Теперь я сделаю паузу и отвлекусь на один из странных пакетных провайдеров — PowerShellGet. Этот провайдер призван устанавливать модули самого PowerShell. Появился он в PowerShell 2 — еще до прихода PackageManager(aka OneGet). Но они друг другу не ровня. PowerShellGet — это один из рядовых пакетных провайдеров, умеющих делать только свой тип пакетов, а не управленец пакетными провайдерами, как OneGet.
Его тип пакетов — .nupkg, содержимое которых — модули PowerShell. Поэтому не удивляйтесь, что его репозиторий PSGallery имеет формат NuGet:
PS C:\> Get-PSRepository -Name "PSGallery" | Format-List * -Force

Name                      : PSGallery
SourceLocation            : https://www.powershellgallery.com/api/v2/
Trusted                   : False
Registered                : True
InstallationPolicy        : Untrusted
PackageManagementProvider : NuGet
PublishLocation           : https://www.powershellgallery.com/api/v2/package/
ScriptSourceLocation      : https://www.powershellgallery.com/api/v2/items/psscript/
ScriptPublishLocation     : https://www.powershellgallery.com/api/v2/package/
ProviderOptions           : {}

И сейчас вы могли заметить следующую тонкость: список репозиториев в PackageManager выполняется командой Get-PackageSource, а не Get-PSRepository.
Дело в том, что работа этого модуля, как я сказал выше, устанавливать модули PowerShell. В комплект установки входит и регистрация новых командлетов, что он и сделал. Этот командлет дает дополнительные поля при регистрации репозитория (например PublishLocation).
Этого не видно, если использовать стандартный командлет:
PS C:\> Get-PackageSource -Name "PSGallery" | Format-List * -Force

Name         : PSGallery
Location     : https://www.powershellgallery.com/api/v2/
Source       : PSGallery
ProviderName : PowerShellGet
Provider     : Microsoft.PackageManagement.Implementation.PackageProvider
IsTrusted    : False
IsRegistered : True
IsValidated  : False
Details      : {[ScriptPublishLocation, https://www.powershellgallery.com/api/v2/package/], [InstallationPolicy, Untrusted], [PackageManagementProvider, NuGet],
               [ScriptSourceLocation, https://www.powershellgallery.com/api/v2/items/psscript/]...}


Последнее: Chocolatey.
Вот все говорят, что Chocolatey — надстройка над NuGet. Оно как бы так, но не совсем. Chocolatey использует те же spec'и, но не полностью. Вместо полноценного пакета он зачастую использует скачивание .msi или чего угодно другого и последующую silent-установку. То есть в то время, как пакетный провайдер NuGet или пакетный менеджер nuget.exe устанавливает только нативные NuGet-пакеты, Chocolatey устанавливает их же + все то, что что другие устанавливать не умеют. Поэтому база пакетов Chocolatey такая внушительная по сравнению с базой NuGet. И поэтому в пакетах Chocolatey столько скриптов много.

Возможно вы заметили, что есть два провайдера Chocolatey и ChocolateyGet. Первый — самодельная времянка. Второй — официальный провайдер, который недавно только вышел. Ну вы видите по версиям.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331124/


Метки:  

Android: динамически подгружаем фрагменты из сети

Воскресенье, 18 Июня 2017 г. 03:43 + в цитатник
В этой статье мы рассмотрим, как загружать классы (в том числе, фрагменты) из сети во время выполнения программы, и использовать их в своем Android-приложении. Область применения подобной технологии на практике — это отдельная тема для разговора, мне же сама по себе реализация данной функциональности показалась довольно интересной задачей.

Приступим.


Создаем фрагмент


Для начала создадим некий фрагмент Fragment0 и реализуем у него метод onCreateView():
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    //return inflater.inflate(R.layout.fragment1, container, false);

    LinearLayout linearLayout = new LinearLayout(getActivity());
    linearLayout.setOrientation(LinearLayout.VERTICAL);
    linearLayout.setGravity(Gravity.CENTER);
    LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

    Button button = new Button(getActivity());
    button.setText("Кнопка");
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            showFragment("jatx.networkingclassloader.dx.Fragment1", null); // рассмотрим чуть позже
        }
    });
    linearLayout.addView(button, lp);

    return linearLayout;
}

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

Далее нам нужно на основе модуля, содержащего фрагмент, создать APK, распаковать его с помощью unzip, и выложить файл classes.dex на сервер.

Реализуем загрузку классов


В отдельном модуле создадим класс NetworkingActivity и реализуем в нем следующие методы:
@Override
protected void onCreate(Bundle savedInstanceState) {
    // ......
    dataDir = getApplicationInfo().dataDir;
    frameLayout = (FrameLayout) findViewById(R.id.main_frame);

    progressDialog = new ProgressDialog(this);
    progressDialog.setIndeterminate(true);
    progressDialog.setMessage("Загружаем классы из сети");
    progressDialog.show();

    // Загружаем classes.dex с сервера, подробно рассматривать не будем:
    DownloadTask downloadTask = new DownloadTask(this, dataDir); 
    downloadTask.execute(null, null, null); 

    // receiver нужен для того, чтобы мы могли из фрагмента открывать другие фрагменты:
    BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String className = intent.getStringExtra("className");
            Bundle args = intent.getBundleExtra("args");
            showFragment(className, args);
        }
    };
    IntentFilter filter = new IntentFilter("jatx.networkingclassloader.ShowFragment");
    registerReceiver(receiver, filter);
}

// Вызывается, когда наш AsyncTask успешно загрузил c сервера classes.dex:
public void downloadReady() {
    Toast.makeText(this, "Классы из сети загружены", Toast.LENGTH_SHORT).show();
    progressDialog.dismiss();
    showFragment("jatx.networkingclassloader.dx.Fragment0", null);
}

public void showFragment(String className, Bundle arguments) {
    // Наш загруженный файл:
    File dexFile = new File(dataDir, "classes.dex");
    Log.e("Networking activity", "Loading from dex: " + dexFile.getAbsolutePath());
    // Каталог кэша, нужен для DexClassLoader:
    File codeCacheDir = new File(getCacheDir() + File.separator + "codeCache");
    codeCacheDir.mkdirs();
    // Создаем ClassLoader:
    DexClassLoader dexClassLoader = new DexClassLoader(
                dexFile.getAbsolutePath(), codeCacheDir.getAbsolutePath(), null, getClassLoader());
    try {
        // Загружаем класс фрагмента по имени:
        Class clazz = dexClassLoader.loadClass(className);
        // Создаем объект класса:
        Fragment fragment = (Fragment) clazz.newInstance();
        // Передаем фрагменту аргументы и отображаем его:
        fragment.setArguments(arguments);
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.main_frame, fragment);
        fragmentTransaction.commit();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Открываем из фрагмента другие фрагменты


Для этого в классе LoadableFragment (суперкласс всех наших фрагментов) реализуем следующий метод:
public void showFragment(String className, Bundle args) {
    Intent intent = new Intent("jatx.networkingclassloader.ShowFragment");
    intent.putExtra("className", className);
    intent.putExtra("args", args);
    getActivity().sendBroadcast(intent);
}

Надеюсь, здесь все понятно.

Наш следующий фрагмент мы попробуем создать несколько иначе.

Подгружаем из сети xml-разметку


Для начала, создаем и выкладываем на сервер файл разметки.
Я нашел на github библиотеку, которая умеет парсить xml layout из строки. Для корректной работы пришлось ее немного подпилить.

И так, добавим в наш класс LoadableFragment следующие методы:
protected void loadLayoutFromURL(FrameLayout container, String url) {
    this.container = container;
    // загружаем файл разметки:
    LayoutDownloadTask layoutDownloadTask = new LayoutDownloadTask(this, url);
    layoutDownloadTask.execute(null, null, null);
}

// Вызывается, если xml-разметка успешно загружена:
public void onLayoutDownloadSuccess(String xmlAsString) {}


Теперь с помощью этого всего создадим фрагмент Fragment1:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    FrameLayout frameLayout = new FrameLayout(getActivity());
    loadLayoutFromURL(frameLayout, "http://tabatsky.ru/testing/fragment1.xml");
    return frameLayout;
}

@Override
public void onLayoutDownloadSuccess(String xmlAsString) {
    LinearLayout linearLayout = (LinearLayout) DynamicLayoutInflator.inflate(getActivity(), xmlAsString, container);
    FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    linearLayout.setLayoutParams(lp);
    final EditText editText = (EditText) DynamicLayoutInflator.findViewByIdString(linearLayout, "edit_text");
    Button button = (Button) DynamicLayoutInflator.findViewByIdString(linearLayout, "button");
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Bundle args = new Bundle();
            args.putString("userName", editText.getText().toString());
            showFragment("jatx.networkingclassloader.dx.Fragment2", args);
        }
    });
}

Послесловие


Полностью исходный код проекта можно посмотреть на github.
Готовый APK можно скачать здесь.
Ну и напоследок, хочу сказать пару слов о возможном применении подобной технологии: например, можно выдавать с сервера разные classes.dex в зависимости от типа аккаунта пользователя (платный/бесплатный), что должно несколько увеличить сложность реверс-инжиниринга приложения.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331122/


Метки:  

[recovery mode] Новый плагин для гарнитур Sennheiser и бесплатное обновление до 3CX v15.5

Воскресенье, 18 Июня 2017 г. 01:36 + в цитатник

Плагин гарнитур Sennheiser для клиента 3CX


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

Возможности плагина Sennheiser


  • Автоматическое поднятие трубки (технология EHS — remote electronic hook switching) с клиента 3CX
  • Полное управление сфтфоном с гарнитуры – ответ / завершение вызова, регулировка громкости и отключение микрофона
  • Одновременная поддержка Skype for Business / Microsoft Lync и клиента 3CX

Требования к системе


  • Windows 7 — 10 (32 и 64 bit)
  • Установленный .Net framework 4.5.1 и выше
  • Установленный клиент 3CX v15 и выше
  • Любая редакция 3CX, включая бесплатную, не ниже v15.

image

Поддерживаемые гарнитуры и спикерфоны Sennheiser:


  • DW Series
  • SD Series
  • D 10 Series
  • Culture Series
  • Culture Plus Series
  • Circle Series
  • Century Series
  • MB Pro Series
  • MB 660 UC Series
  • PRESENCE Series
  • Speakerphone Series
  • BTD 800 USB

Загрузки и документация



Программа бесплатного обновления устаревших версий 3CX Phone System


После выпуска 3CX v15.5 мы стремимся, чтобы как можно большее число пользователей получило преимущества новой версии. Мы понимаем, что во многих компаниях ИТ бюджеты ограничены. Иногда руководству трудно объяснить необходимость обновления, ведь многие преимущества новой версии находятся на стороне сервера и не так очевидны рядовым пользователям – зато принципиально упрощают жизнь системному администратору или партнеру-интегратору.

Поэтому мы разработали программу бесплатного обновления всех бессрочных лицензий 3CX предыдущих версий до текущей версии 15.5!

Суть предложения:

  • Бессрочная лицензия 3CX любой устаревшей версии бесплатно меняется на годовую лицензию 3CX v15.5 той же редакции и емкости. Например, если у вас есть лицензия 3CX 64 Pro v12, мы бесплатно обменяем ее вам на 64 Pro v15.5 Annual (на 1 год)
  • Через год вы можете оплатить годовую подписку и пользоваться новой системой далее, перейти на бессрочную лицензию, либо прекратить пользоваться системой
  • Срок действия программы – до 30 сентября 2017 г.

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

Также, пользуясь случаем, сообщаем, что с 1 сентября 2017 г. прекращается поддержка 3CX Phone System v14, веб- и видеоконференций Webmeeting v14, и поддержка совместимости новых клиентов 3CX (для всех платформ) с сервером 3CX v14.

Рекомендуем воспользоваться нашим предложением!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331116/


Размышления на тему науки программирования

Суббота, 17 Июня 2017 г. 22:11 + в цитатник
Программирование не является наукой на сегодняшний день. Но когда сфера пройдет некоторую критическую точку и станет наукой, то какой? Технической (точной) или гуманитарной (не точной)? Статья — свободное размышление на данную тему. Автор имеет 9 классов образования и с темой про науку, написанную за 2 часа перед сном, залезает «со свиным рылом в калашный ряд твою ленту Хабра». Поэтому научным светилам не стоит заходить во внутрь, содержание носит легкий, юмористический характер.

Программирование



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

Я считаю, что де-факто программирование является наукой и если хорошо напрячь извилины, то в этих ветвлениях if-else и прочих конструкциях можно найти новизну. Хотя, нет… Как раз if-else и манипуляции с битами нет — это всего-лишь алгоритмика (раздел информатики), а научная новизна располагается где-то в очень высокоуровневом программировании, где манипуляция идет объектами реальной жизни. Но о реальной жизни чуть позже, сразу после главы про математику и алгоритмику.

Математика и алгоритмика



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

Много-много слоев абстракции



За последние 30 лет все, чем занимались программисты — строили слои абстракции поверх предыдущего слоя. Простой пример на уровне языка программирования: Assambler -> C -> PHP -> Smarty. Каждый следующий язык программирования — это язык программирования, который написан на предыдущем языке программирования. Все ради чего? Ради упрощения технической сложности и более изящной абстракции. Чем проще ЯП, тем быстрее идет разработка. Чем изящнее абстракция, тем больше шансов, что какой-то бизнес-процесс в коде всю жизнь будет соответствовать реальному.

Благодаря такой слоености, сегодня любой школьник может освоить выскоуровневый ЯП C# и с помощью фреймворка Unity написать свою игру, где можно грабить фуры и кастовать ульт Зевса по КД. Никогда игры не писал, но уверен, что «умные» персонажи сегодня создаются одной строкой area.createCharacter(new Zeus('Зюся', 100, -1)).

Все хорошо, но есть и проблемы у абстракции — она все еще кривая. И порой программисты натягивают презерватив на глобус, программа едет по сценариям вымышленного мира. Программа может выписать сотруднику вместо зарплаты просмотры какой-то статьи или автоматически уволить штраф за невыход на работу. Бред, наркомания и шизофрения! Как вообще такое могло родиться, ЭТОГО ЖЕ НЕТ В РЕАЛЬНОЙ ЖИЗНИ!!! Да такое даже в голову не придет здоровому человеку!!! Примерно вот так, казалось бы, нужно к этому относиться. Но люди относятся лояльно по состоянию на 2017 год, потому что так все устроено, абстракции кривые и без багов пока что никуда. Я уверен, через 100 лет сегодняшние баги будут казаться чем-то нереальным, как сейчас представления людей о плоском мире.

Гуманитарная наука



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

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

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

Реальная жизнь



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

Бой с тенью



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

1) В программировании не существует правильного и неправильного решения *. Когда все работает, качество решения субъективно, нравится — не нравится, модно — не модно. Самым важнейшим объективным критерием является опыт. Жизненный опыт.
2) Социализация программиста — один из важнейших критериев приема на работу на должность программиста в 2017. Программист не должен быть оторван от внешнего мира и замкнут на компьютере. Матерящийся бородатый сисадмин в свитере и с крошками от чипсов на груди уходит в прошлое. Робкий очкарик с вейпом, который шарит смешные мемы и смешно разговаривает — вот образ программиста в простонародии сегодня. Извините, если кого обидел, вся статья — шутка ;)
3) ООП — мейнстрим. Каждый объект — это сложная абстракция. Невозможно написать объект, который точно, до атомов распишет какой-то жизненный процесс, хоть многие ПМ и заказчики пытаются это делать. Лучший объект напишет тот программист, который мыслит параметрами реальной жизни, а не цифрами, паттернами, алгоритмами и адресами памяти компьютера.

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

А вот мои контраргументы.

1) Гуманитарий — это заказчик, который создает ТЗ. Задача программиста — точно закодить описанные алгоритмы и выбрать правильный стек, успех работы программы будет зависеть, в основном, от составителя ТЗ.

Второй аргумент — тот же первый, но с другого бока.

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

Вот такие размышления. Надеюсь, никого сильно не загрузил.

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

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

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

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

https://habrahabr.ru/post/331110/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1011 1010 [1009] 1008 1007 ..
.. 1 Календарь