Category: история

Category was added automatically. Read all entries about "история".

glider

имплициты

Недавно прошаривался в скале и нашел несколько интересных статей про имлициты:
* Can someone explain me implicit conversions in Scala? (материал для начального ознакомления с имплицитами)
* Where does Scala looks for implicits? (исчерпывающий материал по поводу видов имплицитов в скале и правил их поиска компилятором)
* Context and view bounds again (использование имплицитов для ограничения генерик-параметров - эмуляция тайпклассов Хаскелла. два подхода, каждый со своими плюсами и минусами)
* Implicit Parameters: Dynamic Scoping with Static Types (научная работа 2000 года, в ней Эрик Мейер с коллегами добавили неявные параметры в Хаскелл. интересно сравнить научное исследование и практическую реализацию, по сути, одной и той же идеи)

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

Во-первых, оператор пайплайна из F# (оригинальный пост):

implicit def toPipeLink[X](v: X): PipeLink[X] = new PipeLink[X](v)
class PipeLink[X](value: X) {
  def |>[Y](func: X => Y): Y = func(value)
}
Во-вторых, автоматическое каррирование (stackoverflow):

implicit def curryImplicitly[A,B,C](f: (A, B) => C) =
  (a: A) => (b: B) => f(a, b)
implicit def uncurryImplicitly[A,B,C](f: A => B => C) =
  (a: A, b: B) => f(a)(b)
В-третьих, вот способ попросить компилятор искать по всему графу неявных преобразований между типами, а не останавливаться на типах, непосредственно достижимых из текущего (тоже stackoverflow).

implicit def aToB(a: A): B = ... // will convert A to B
implicit def aToB[A1 <% A](a: A1): B = ... // will convert anything, that can be converted to A, to B
glider

Телефонное собеседование с гуглом

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

В итоге поболтали в начале марта. она немножко повпаривала про то, что такое гугл и про то, что подготовить к собеседованию (по большей части это был рестейт известного блогпоста: http://steve-yegge.blogspot.com/2008/03/get-that-job-at-google.html), после чего по добазарились о телефонном интервью.

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

***

Чел позвонил минута в минуту, в 12:00. Минут 10-15 мы болтали о моем прошлом экспириенсе (это, кстати, большая проблема - связно и кратко рассказать о том, чем ты занимался. если бы я заранее не готовился и не получил неприятный опыт на одном из недавних интервью, то точно бы залажал), потом началось самое интересное - задачки на алгоритмы.

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

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

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

Четвертая задача заключалась в кодировании одной из операций из третьей задачи. Делать это надо было в режиме реального времени в google docs. Началось все хорошо - я набросал структуру данных, проверил в алгоритме краевые условия, упомянул про defensive программирование, различные стратегии работы с ошибками и все дела. И потом неправильно написал алгоритм. Нет, главную идею я выразил правильно, но ошибся в мелочи, причем смог найти ошибку только с третьего раза. Хорошо, что все-таки пофиксил баг, зато потом додумался, как упростить алгоритм, уменьшив количество строчек раза в 1.5-2. Надеюсь, хоть это произвело положительное впечатление.

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

***

Выводы:

1) Все было совсем просто, только надо было подготовиться. Так как во время двух недель подготовки я неслабо времени посвятил сортировкам и деревьям, мне было относительно просто. Впрочем, по факту я подготовил и кучу ненужных вещей, например, изучал новинки C# (додумался же сказать Дженни, что я эксперт сишарпа =)), неслабо проштудировал графы и строки (было риальне интересно!), разбирался с map/reduce, вдавался в детали работы континюэйшнов, читал про техники сборки мусора и JIT-компиляции. Наверное, для телефонного интервью это был оверкилл, но зато я поимел кучу фана. Надеюсь, меня пригласят на on-site интервью, где я смогу про все это поговорить. upd. Пригласили, не поговорили (детали ниже).

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

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

upd. Так получилось, что, несмотря на фейл с кодированием задачки, я прошел телефонное интервью, и Дженни прислала приглашение поучаствовать в onsite интервью. На эту тему есть пост в трех частях: первая, вторая и третья.
glider

Автоматическая синхронизация версии проекта с ревизией SVN

Я давно знал, что, указав для версии сборки атрибут [assembly: AssemblyVersion("1.0.*")] (или любую другую версию кроме 1.0), в 2008й студии мы получаем автогенерацию третьей и четвертой позиций версии - то есть номера билда (устанавливается в количество дней, прошедших на момент билда с 31.12.1999) и номера ревизии (устанавливается в количество секунд, прошедших на момент билда с начала суток, разделенное на 2 - если на два не делить, то получится overflow ибо у нас только 16 бит, а секунд в сутках 86400). Этот подход хорош тем, что при обычном процессе разработки практически невозможно выпустить две разных сборки с одной и той же версией (для этого придется успеть что-то поменять и после того еще и перекомпилировать проект в пределах двух секунд). Но на этом достоинства подхода заканчиваются, ибо из автогенерированной версии мы ничего полезного, кроме времени билда, узнать не можем.

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

К сожалению, непосредственно в студию такая функциональность не встроена и плагинов, ее обеспечивающих, не существует. Приходится выбирать из пачки не очень удобных вариантов - http://stackoverflow.com/questions/12176/svn-revision-version-in-net-assembly-w-out-cc-net. Написание кастомных тасков для MSBuild требует их последующей установки в систему, макросы SVN для автогенерации AssemblyInfo напрямую не подходят, руками делать тоже не хочется - везде есть какие-то недостатки.

На днях я наткнулся на наименее напряжный, с моей точки зрения, вариант и не поленился реализовать его во всех своих open-source проектах. Вот та самая статья: Using Subversion Revision as the AssemblyVersion - Revisited, а вот воркфлоу моими словами (он опускает некоторые детали, за которыми прошу обратиться к первоисточнику):
1) Используем тул SubWCRev.exe из TortoiseSVN, который умеет заменять макросы SVN на текущие значения,
2) Файлики AssemblyInfo.cs выкидываем из-под сорс контроля и для верности еще и игнорируем, чтобы не было постоянных промптов их добавить обратно,
3) Рядом с файлом AssemblyInfo.cs кладем файл AssemblyInfo.template, в котором заменяем номер билда в версии на макрос $WCREV$ (важно не забыть добавить этот файл в проект, чтобы помнить о его существовании, если надо будет что-нибудь поменять в AssemblyInfo, например, мажорную версию),
4) В пре-билд ивент проекта (конфигурация находится в свойствах проекта на закладке Build Events) записываем вызов тулзы из пункта 1, которая преобразует темплейт в AssemblyInfo.cs, после чего запускается билд,
5) Профит!

В итоге - исходная задача выполнена, и к тому же: 1) париться с настройкой надо всего лишь один раз, причем все необходимые настройки выполняются в файловом менеджере и GUI студии, 2) проект компилируется где угодно, 3) AssemblyInfo.cs заигнорен, поэтому каждый раз не будет мозолить глаза иконкой измененного контента и просьбой закоммитить.

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

P.P.S. Насколько я знаю, ревизия в Git представляет собой непрозрачный длинный токен, который не поместится в 8 байт, отведенных на версию в PE формате. Это осложняет дело. Можно, например, вешать на сборку кастомный атрибут со значением этого токена, но кастомные атрибуты не показываются в меню винды File > Properties - неудобно. Можно также хранить токен в строковых метаданных PE-модуля при помощи атрибутов AssemblyTitle или AssemblyDescription - вроде бы неплохой вариант. Впрочем, сам не пробовал - не знаю, так что это просто догадки.