MCP

вторник, 26 июня 2012 г.

Циклы. Нужны ли они?

Не стоит воспринимать данный текст слишком серьёзно. В нём присутствует некоторая доля бреда

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

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

Преобразование

Типичная задача: взять из списка объектов "Пользователь" только имя и работать с ним дальше. Есть вполне сформировавшиеся абстракции, например map в jQuery или Select в .NET. Согласитесь, что второй пример кода выглядит гораздо понятнее первого, т.к. отсутствует лишний вспомогательный код:

List<string> names = new List<string>(); 
foreach(var user in users) 
    names.Add(user.Name); 

var names = users.Select(x => x.Name);

Фильтрация

Тоже частая задача. Выбрать всех активных пользователей. Тут стандартные схемы filter и Where:
List<User> resUsers = new List<User>(); 
foreach(var user in users) 
    if(user.IsActive) 
        resUsers.Add(user); 

var resUsers = users.Where(x => x.IsActive);

Аггрегация 

Куда же без неё! Например, нам нужно посчитать количество пользователей (да, у нас сейчас нет встроенной функции):
var cnt = 0; 
foreach(var user in users) 
    cnt++; 
А в нормальном стиле (в общем виде) это можно сделать как-то так, через аккумулятор:
var cnt = users.Aggregate((e, acc) => acc + 1);

Обработка

В данном случае результат передаётся куда-то дальше, основной объект никак не трансформируется. Тут нам на помощь приходят each в jQuery и ForEach в .NET. Например, мы хотим вывести имена пользователей на консоль:
//names - List<int> из первого примера 
foreach(var name in names)  
    Console.WriteLine(name);  

names.ForEach(Console.WriteLine);

И всё вместе

И конечно же, циклы используются сразу для всех задач вместе, ещё больше запутывая код и подменяющие реализацию задачи ненужными деталями реализации (которые, действительно, никому никогда не нужны и только мешают дальнейшему чтению кода).
Итак, выбираем имена активных пользователей и находим самое длинное:
List<string> names = new List<string>();  
foreach(var user in users)  
    if(user.IsActive) 
        names.Add(user.Name);  

var maxName = ""; 
foreach(var name in names) 
    if(maxName.Length < name.length)  
        maxName = name; 

//-------------------- 
var maxName = users 
    .Where(x => x.IsActive) 
    .Select(x => x.Name) 
    .Aggregate("", (e, acc) => e.Length > acc.Length ? e : acc);

Согласитесь, второй вариант, гораздо понятнее и короче. Сразу по тексту видно что он делает (и это я ещё не воспользовался готовым методом Max). В первом же случае проще всего догадаться о том, что делает код по названию переменной, но это в данном простом тестовом примере. А в реальной жизни всё гораздо запутаннее, ведь обязательно набегут оптимизаторы и цикл превратится в более короткую и простую версию, которая понятнее (вроде бы), но в результате вся логика прибита гвоздями и её изменение становится уже более сложной задачей, связанной с практически полным переписыванием куска кода:
var maxName = ""; 
foreach(var user in users)  
    if(user.IsActive && user.Name.Length > maxName.Length) 
        maxName = name; 

Результат

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


Напоследок

Чтобы не останавливаться на элементарных примерах, я решил привести чуть более сложный. А именно, реализацию сортировки. Классический quicksort из учебника выглядит примерно так (только главная часть):
public void Sort(int start, int end) 
{ 
    if (end <= start) return; 
    if (end - start == 1) 
    { 
        if (_array[end] < _array[start]) Swap(start, end); 
        return; 
    } 
    int el = _array[(start + end)/2]; 
    int endIdx = end; 
    int startIdx = start; 
    while(startIdx <= endIdx) 
    { 
        if (_array[startIdx] < el) startIdx++; 
        else 
            if (_array[endIdx] <= el) 
            { 
                Swap(startIdx, endIdx); 
                endIdx--; 
                startIdx++; 
            } 
            else endIdx--; 
    } 
    Sort(start, endIdx); 
    Sort(endIdx + 1, end); 
}
Данный код, конечно можно подсократить, но это уже тонкости и детали реализации и оптимизации. Тем не менее, даже на такой пример я потратил достаточно времени, пока его реализовал и выловил все замеченные ошибки. И в теперь разобраться как сортирует алгоритм за всеми конструкциями весьма сложно. А теперь посмотрите, что я написал сходу на LINQ:

public IEnumerable<int> Sort(IEnumerable<int> arr) 
{ 
    if (arr.Count() <= 1) return arr; 
    int el = arr.First(); 
    return Sort(arr.Where(x => x < el))
           .Concat(arr.Where(x => x == el))
           .Concat(Sort(arr.Where(x => x > el))); 
}
Всего 5 строчек, и то разбитых для удобства! И смотрите как просто объяснить теперь алгоритм: берём первый попавшийся элемент, берём из массива все элементы меньше него, сортируем их данным же алгоритом, добавляем элементы равные данному, и большие, отсортированные тем же алгоритмом. Всё просто, банально и понятно.
Не хочем quicksort, хотим сортировку выбором? Нет ничего проще:
public IEnumerable<int> Sort(IEnumerable<int> arr) 
{ 
    if (arr.Count() <= 1) return arr; 
    int elMax = arr.Max(); 
    return Sort(arr.Where(x => x < elMax)).Concat(arr.Where(x => x == elMax)); 
}

Видите, вполне чёткая логика и понятность. Так зачем вам ещё циклы?

пятница, 11 мая 2012 г.

Про кредиты

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

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

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

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

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

пятница, 13 апреля 2012 г.

Про почту на собственном/общедоступном домене

Захотелось озвучить своё мнение про почту различных организаций. Дело в том, что раньше я считал, что как-то глупо при наличии своего сайта иметь корпоративную почту на mail.ru или подобных сайтах. Ведь деньги на хостинг есть, почта обычно в комплекте идёт, а если не идёт, то совершенно бесплатно и Google и Яндекс позволяют завести почту на своём домене и по-человечески обслуживать её.

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

Так что на сегодня моё мнение про корпоративную почту на бесплатных доменах — работает, и ладно, генеральный директор с адресом upyachka1988@mail.ru — это вполне мило. 

вторник, 27 марта 2012 г.

Проблема в Google Play и как МТС залез на смартфоны Samsung

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

Я исследовал проблему и выяснил, что проблема в Google Play (бывший Android Market), а именно в том, как он проверяет приложения на идентичность. 

Дело в том, что приложения в маркете идентифицируются не по названию приложения, а по названию пакета (package), которое имеет свои характеристики и это название есть "область имён" классов приложения. Изначально это было сделано, чтобы разные приложения не конфликтовали если в них окажется одинаковые классы. Т.е. учитывается полное имя приложения.

Например, возьмём приложение Карты Google, посмотрите какой адрес у него в маркете:
https://play.google.com/store/apps/details?id=com.google.android.apps.maps

Пакет с приложением называется com.google.android.apps.maps, т.е. по стандартному соглашению:
com — common или com от google.com (бывают разные соглашения о именовании)
google — название компании-производителя
android — это приложение для android
apps — это приложение, а не отдельная библиотека
maps — это приложение карты

Ну или как вариант, Facebook:
https://play.google.com/store/apps/details?id=com.facebook.katana
com — common или от facebook.com
facebook — компания-производитель
katana — так называется клиент. Оригинальное название

Я попробовал сделать приложение, которое называется также, как QuickOffice (только специфичный, предустановленный на телефоны от Motorola).
И тут возникла первая проблема у Google: мне удалось загрузить это приложение в Market. Мне разрешили это сделать!

Тут же я увидел шедевральную картину:
Приложение ещё никто не установил, а у него уже есть 54 ошибки, которые прислали пользователи. Т.е. ошибки подхватились от оригинального приложения и я их мог посмотреть. Это проблема в безопасности.

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

Т.е. Android просто по названию перепутал оригинальное приложение и моё. И это несмотря на то, что все приложения подписываются ключом разработчика (естественно я подписал своим, у меня нет доступа к оригинальному).

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

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

(Данный текст уже неактуален, мы можем мелко пакостить, но не сможем обновить приложение)
Как результат, мы можем делать поддельные приложения Facebook (изначально стоит на множестве устройств) или Google Search, Google Maps и любых других. Мы можем назвать их совершенно по-другому и они даже могут делать честные вещи. Например, отсылать SMS на платные номера. Что здесь честного? А мы можем назвать приложение и написать в описании, что мы делаем именно это, а то, что оно заменило другое приложение — случайное совпадение.

Выходит, что приложение для МТС и Samsung делал один разработчик (Эльдар Муртазин подтверждил это) и подписал одним ключом. Приложение называется com.seven.Z7 в обоих случаях. И из-за полного совпадения случился такой факап у пользователей (несмотря на разные названия и иконки в маркете).


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

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

среда, 15 февраля 2012 г.

Про атмосферное давление

У атмосферного давления есть замечательное свойство — оно падает с высотой, что вполне логично. Причём, как утверждает Википедия, на небольших высотах на 1мм рт.ст. каждые 12 метров. Т.е. если взять Ярославль, то в зависимости от места, давление будет на 8-10 мм. рт.ст. ниже, чем давление над уровнем моря. А если подниметесь верхний этаж многоэтажного дома, то еще на 3мм рт.ст. ниже. Весьма интересные цифры вплане того, что люди, страдающие от высокого давления могут слегка облегчить себе жизнь поселившись на верхнем этаже.

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

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

Для начала возьмём детальные данные с сайта rp5.ru, на данном сайте есть оба варианта давления, и сравним с показателями на остальных. Итак, в настоящий момент для Ярославля: фактическое 746.2, приведённое к уровню моря 755.2. Теперь посмотрим остальные:

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

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

суббота, 31 декабря 2011 г.

Итоги 2011-ого года

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

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

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

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

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

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

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

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

воскресенье, 6 ноября 2011 г.

SVN vs DVCS

Последнее время, появилось множество фанатов, орущих про то, что SVN — сраное говно, а Git/Mercurial — активно рулят. При этом, несмотря на плюсы распределённых систем, у них ещё дофига минусов, которые решаются или костылями, или инструкциями по работе, или надеванием штанов через голову, потому что "это модно".

Покопавшись в интернете, я не нашёл особых статей со сравнением DVCS (распределённых системы управления версиями) и SVN (хотя не сильно-то и искал), так что решил написать свою, чтобы Трухин Юрий посмеялся. 
Собственно, буду сравнивать концепцию DVCS (без сильных завязок на Git или Mercurial) и SVN, как одного из самых ярких представителей централизованных систем (TFS тоже яркий, но это комбайн с кучей бантиков и практически только под Visual Studio)

Всё нижеперечисленное, моё личное мнение, которое может не совпадать с мнением фанатов какой-либо системы, да и вообще быть ошибочным.

Начнём с плюсов DVCS:
  • У каждого своя пользователя копия репозитория в которой он может делать всё что угодно, до заливки в основной репозиторий
  • Удобные бранчи и теги 
  • Более приличный мёрж (за счёт ченджсетов)
  • Отличная автономность
  • Возможность устраивать сложную систему иерархий в организации проекта
А теперь, про недостатки, про то, что не пишут или что обходится административными мерами:
  • Один репозиторий — один проект. Т.е. распределённые системы удобны для работы над одним проектом, и очень плохо разделять зону ответственности между связанными проектами. Технически, два независимых проекта, это два полностью независимых проекта, с разными репозиториями и отсутствием общей инфраструктуры
  • Подключение к проекту внешних модулей (например, общей между разными проектами, библиотеки). В svn это весьма коряво решается через svn:externals, и в git и в mercurial есть ещё более корявые решения, но в принципе решить задачу можно, хоть и очень коряво.
  • Проблема с хранением больших бинарных файлов (особенно, если они ещё и изменяются периодически). Т.к. у каждого разработчика по копии своего репозитория, то у каждого по копии всех этих файлов (и всей истории), даже если они ему и не нужны. Решение — использовать всяческие расширения или выносить эти файлы из DVCS в другие системы (например, тот же SVN).
  • Невозможность забрать всего-лишь несколько файлов из проекта. Например, менеджерам абсолютно не нужен код, им нужно только ТЗ, макеты и прочая мелочёвка. При этом, уровень грамотности у них в плане работы с VCS значительно ниже. В общем, как результат, проще документацию хранить отдельно, чем обучать менеджеров работе с DVCS.
  • Отсутствие единой сквозной нумерации. Прощай автоверсионирование в билдах и удобная привязка к багтрекеру. Проблема некритичная, но очень "радует" своим удобством.
  • Отсутствие возможности тонкого разграничения прав, например, запретить писать некоторым пользователям в важные конфигурационные файлы, или же заблокировать от изменений часть проекта.
  • Отсутствие возможности блокировки файлов. Фича редкая, но когда нужна, тогда без неё плохо. Можно обойти административными мерами, но их любят нарушать.
  • Практически всегда необходимо выделять центральный репозиторий (хотя бы для автомтатических билдов), как результат, он выполняет роль сервера SVN, т.е. в принципе, всё сводится назад, к централизованной системе с более умными клиентами у разработчиков.
  • Хуки на определённые группы файлов, и вообще слежение за изменениями. Необходимо прилагать дополнительные усилия для слежки, то, что в SVN делается из коробки.
  • Потеря исходников автоматически означает потерю всего репозитория, т.е. включает в себя всю историю, уже удалённые файлы, даты, список пользователей, ветвления. Т.е. ценность данной информации гораздо выше, стоимость потери — тоже. А с учётом пункта об проблемах с разграничением прав, всё становится совсем плохо. А потерять один ноутбук разработчика гораздо проще чем данные с одного сервера, охраняемого злобным цербером администратором. 
И как закономерный результат: отлично DVCS применимы для распределённой разработки (как и следует из их названия) и слабо применимы для локальной группы разработчиков, когда все преимущества распределённости теряются, а недостатки никуда не деваются.

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

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