среда, 18 декабря 2013 г.

Mendeley - must have для любого научного сотрудника

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

Думала, что с моими проблемами Mendeley не справится, но потом включила разум и нашла такую статью и  вот такую, и управилась со всем минут за 10! (а если бы оформляла вручную, то полдня бы потратила). Спасибо доброму человеку из второй ссылки, который сделал стиль оформления источников по русскому ГОСТу да еще с упорядочением по фамилиям авторов (именно такой замороченный стиль требуют в журнале УБС).

среда, 11 декабря 2013 г.

Mathtype: эта версия Word не поддерживается

Эта информация мне сегодня помогла вовремя отправить правки статьи в журнал :) Взято отсюда.

Официально MathType 6.6a не поддерживает Microsoft Word 2010 и при установке не распознает данное приложение. Но существует ручной способ для подключения надстройки MathType 6.6a, для этого необходимо скопировать два файла в папку автозагрузки Microsoft Word 2010:
 КОПИРУЕМ
C:\Program Files\MathType\MathPage\MathPage.wll
C:\Program Files\MathType\Office Support\MathType Commands 6 For Word.dotm
В каталог установки офиса (по умолчанию c:\Program Files)
C:\Program Files\Microsoft Office\Office14\STARTUP\
 1. Нажимаем на "Запуск макросов отключен"
2. Нажимаем на кнопку "Включить содержимое"

воскресенье, 8 декабря 2013 г.

О научных журналах

Цитата с Хабра:

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

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


Да-да, абсолютно согласна!! Вот доразбираюсь со статьями в труднодоступных журналах, чтобы прокачать ПРНД, и попробую запилить еще один постик на Хабр в популярном стиле.

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

понедельник, 2 декабря 2013 г.

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

Пример 1.
"Все планеты круглые. Земля - планета. Значит, Земля круглая".
Это дедукция, нормальное такое логическое утверждение (от общего - к частному).

Пример 2.
"Все планеты круглые. Маша круглая. Значит, Маша-планета".
Это абдукция, в данном случае полный бред, но иногда абдукция совпадает с правдой.

А вот еще:


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


понедельник, 21 октября 2013 г.

Как писать комментарии

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

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

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

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

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

4. Предупреждения о последствиях - это весьма полезные комментарии. Например:


5. Иногда бывает полезно оставить заметки на будущее в форме комментариев TODO. Регулярно просматривайте такие комментарии и удаляйте те, которые потеряли актуальность.

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


7. Избыточные комментарии. Читаются не проще, чем сам код. Например:

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

При всей полезности комментариев Javadoc для API общего пользования не применяйте их в коде, не предназначенном для общего потребления. 

9. Позиционные маркеры. Это строки типа:

//Действия///////////////

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

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

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


12. Плохо, когда сам комментарий нуждается в объяснениях. Например, (см. ниже) - что такое байты фильтра? Они как-то связаны с +1 или *3? Один пиксел соответствует одному байту? 


пятница, 18 октября 2013 г.

Монада и моноид: что это такое

Монада

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

В частности, к монадам относятся:

  • IO (монада строго последовательных вычислений): стратегия связывания — «сначала первое вычисление, затем второе»;
  • Maybe (монада вычислений с отсутствующими значениями): стратегия связывания — «если первое вычисление дало результат, то второе; иначе — отсутствие результата»;
  • List (монада вычислений с несколькими результатами): стратегия связывания — «все возможные результаты второго вычисления, примененного к каждому из вычисленных первым значений параметра»;
  • State (монада вычислений с переменной состояния): стратегия связывания — «начать второе вычисление с состоянием, измененным в результате первого»;
Вот тут описан хороший пример создания и использования монады Maybe.

Моноид

Моноид - это множество с заданной на нем ассоциативной бинарной операцией и нейтральным элементом. Примерами моноидов могут служить:

  • String с операцией конкатенации и значением "пустая строка";
  • Boolean с операцией && и значением true;
  • Boolean с операцией || и значением false;
  • Integer с операцией (+) и значением 0;
  • -Integer с операцией (*) и значением 1;

и т.д.

Определения, которые помогают глубже постигнуть суть моноида

Свойство ассоциативности


Операция \(\circ\) ассоциативна, когда \(\forall x,y,z  (x\circ{y})\circ{z}=x\circ(y\circ{z})\).

Операция сложения целых чисел - ассоциативна.
Например, вычисление выражения 1 + (2 + 3) даст такой же результат, как и вычисление выражения (1 + 2) + 3  число 6.

Операция деления на множестве вещественных чисел - не ассоциативна.
Например, вычисление выражения 4/(2/2) даст число 4, а (4/2)/2 - число 1.

Бинарная операция


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

Нейтральный элемент

Нейтральный элемент бинарной операции - это элемент, который оставляет любой другой элемент неизменным при применении к ним этой бинарной операции. Говоря более формально, пусть \((M, \circ)\) - множество \(M\) с определенной на нем бинарной операцией \(\circ\). Элемент \(e \in M\) называется нейтральным относительно \(\circ\), если \(x\circ{e}=e\circ{x}=x\) \( \forall x \in M\).

1. Определение монады (wiki)
2. Туториал по системе переписывания термов TermWare
3. Про моноиды с примерами на F#
4. Определение ассоциативности (wiki)
5. Определение ассоциативности (ru.math.wikia)
6. Бинарная операция (wiki)
7. Определение нейтрального элемента (ru.math.wikia)


понедельник, 14 октября 2013 г.

Как сделать презентацию для конференции: клевая ссылка

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

http://wiki.4intra.net/Conference-for-speakers

http://www.powerlexis.ru/klub_powerpoint/tekstozavr/ - этот инструмент заценяет адекватность презентации, надо будет попробовать :)

А вот кстати и интутитивно понятный онлайн инструмент для построения графиков и диаграмм (полезно, когда никак навскидку не можешь вспомнить, как строить их в MS Excel):

http://www.chartgo.com/en/chartbar.jsp


суббота, 5 октября 2013 г.

Как подчистить историю коммитов (личный опыт)

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

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

Вопрос.

What I'd like to do is remove ALL the version history from the local Git repository, so the current contents of the repository appear as the only commit (and therefore older versions of files within the repository are not stored).

Ответ.

The only solution that works for me (and keeps submodules working) is
git checkout --orphan newBranch
git add -A  # Add all files and commit them
git commit
git branch -D master  # Deletes the master branch
git branch -m master  # Rename the current branch to master
Вопрос.

How to force push a reset to remote repository
(когда откатываешь master на пару коммитов назад, этот зараза отказывается пушить изменения)

Ответ.

git push origin master -f

Вопрос.

Как посмотреть, какие ветки есть и к какому коммиту прикреплена каждая ветка?

Ответ.

git show-branch --all

P.S. http://www.syntevo.com/smartgithg/documentation?page=git-flow

Информационные источники: stackoverflowеще stackoverflowдокументация Git

понедельник, 30 сентября 2013 г.

Как писать функции

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

1. Функции должны быть компактными. Желательно, чтобы длина функции не превышала 20 строк.

2. Максимальный уровень отступов в функции не должен превышать одного-двух (отступы создаются командами if, else, while и т.д.)

3. Функция должна выполнять только одну операцию. Она должна выполнять ее хорошо и ничего другого она делать не должна. Чтобы убедиться в том, что функция "выполняет только одну операцию", необходимо проверить, что все команды функции находятся на одном уровне абстракции. Например, посмотрим на вот такой кусочек кода:


В этом куске кода данное правило нарушается. Некоторые из концепций - например, getHtml() - находятся на очень высоком уровне абстракции, другие (например, String pagePathName = PathParser.render(pagePath) - на среднем уровне). Наконец, третьи - такие, как .append("\n") - относятся к чрезвычайно низкому уровню абстракции.

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

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

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

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

Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius)

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

7. Выбор хорошего имени для функции  способен в значительной мере объяснить смысл функции, а также порядок и смысл ее аргументов. В унарных функциях сама функция и ее аргумент должны образовывать естественную пару "глагол/существительное". Например, вызов вида write(name) смотрится весьма информативно. Читатель понимает, что чем бы ни было "имя", оно куда-то "записывается". Еще лучше запись writeField(name), которая сообщает, что "имя" записывается в "поле" какой-то структуры.

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

Функция использует стандартный алгоритм для проверки пары "имя пользователя/пароль". Она возвращает true или false. Побочным эффектом этой функции является вызов Session.initialize(). Имя checkPassword сообщает, что функция проверяет пароль. Оно ничего не говорит о том, что функция инициализирует сеанс. Таким образом, тот, кто поверит имени функции, рискует потерять текущие сеансовые данные, когда он решит проверить данные пользователя. 

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

9. Почти все наверняка сталкивались с необходимостью дополнительной проверки аргументов, которые на самом деле оказывались выходными, а не входными. Пример: appendFooter(s). Присоединяет ли эта функция s в качестве завершающего блока к чему-то другому? Или она присоединяет какой-то завершающий блок к s? Является ли s входным или выходным аргументом? Конечно, можно посмотреть на сигнатуру функции и получить ответ. Но это нарушает естественный ритм чтения кода. На самом деле функцию appendFooter лучше вызывать в виде: 
report.appendFooter()
В общем случае выходных аргументов стоит избегать. Если ваша функция должна изменять чье-то состояние, пусть она изменяет состояние своего объекта-владельца.

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

public boolean set(String attribute, String value);

Функция присваивает значение атрибуту с указанным именем  и возвращает true, если присваивание прошло успешно, или false, если такой атрибут не существует. Это приводит к появлению странных конструкций вида 

if (set ("username", "UncleBob"))...

Представьте происходящее с точки зрения читателя кода. Что проверяет это условие? Что атрибут "username" содержит ранее присвоенное значение "UncleBob"? Или что атрибуту "username" успешно присвоено значение "UncleBob"? Смысл невозможно вывести из самого вызова, потому что мы не знаем,  чем в данном случае является слово set - глаголом или прилагательным. 

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

if (attributeExists("username")){
    setAttribute("username", "UncleBob")
...
}

11. Изолируйте блоки try/catch. Блоки try/catch выглядят весьма уродливо. Они запутывают структуру кода и смешивают обработку ошибок с нормальной обработкой. По этой причине тела блоков try и catch рекомендуется выделять в отдельные функции:

12. Используйте исключения, а не коды ошибок!

13. Если ваши функции остаются очень компактными, редкие вкрапления множественных return, команд break и continue не принесут вреда, а иногда даже могут повысить выразительность по сравнению с классической реализацией с одной точкой входа и одной точкой выхода. 




понедельник, 16 сентября 2013 г.

Как пользоваться Sublime 2

Ниже будут представлены не все тонкости работы с Sublime, а только те, которыми буду пользоваться в ближайшее время. Все проверялось на ОС Ubuntu.

Ctrl + + -  увеличить шрифт
Ctrl + /  -  закомментировать/раскомментировать выделенный текст

Если нажать на Shift и правую кнопку мыши, то можно сделать вертикальное выделение текста:



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


По нажатию Ctrl+P вызывается панель перехода и поиска. Если мы в ней введем @func, то перейдем к функции с именем func, если введем :10 - то перейдем к 10 строке.

Ctrl+Home, Ctrl+End - переход к началу или концу файла соответственно.

Ctrl + <-, Ctrl + -> - переход по словам

Сtrl + l - добавляем к выделенному еще одну строку

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

Ctrl + Shift + M - 
выделить текст внутри скобки, квадратной или фигурной. Повторяя нажатие этих клавиш, выделим и сами скобки, потом выделится текст внутри охватывающих эти скобки других скобок и т.д.:


Ctrl + F2 -поставить/убрать закладку. 
F2 -перейти к закладке

Sublime Package Control

Позволяет с легкостью устанавливать новые плагины. Устанавливается так. Нажимаем Ctrl + ` . Затем в открывшуюся консоль пишем


Перезапускаем Sublime. Идем в Preferences->Package Settings. Там должен появиться Package Control.
Плагин Alignment

Этот плагин помогает красиво выравнивать знаки равенства:


Для установки этого плагина нажимаем Ctrl + Shift + P. В строке набираем install и ищем Alignment.
Для того, чтобы сделать автоматическое выравнивание, набираем Ctrl + Alt + A. 

Плагин Emmet

Хорошо про этот плагин написано вот тут. Вкратце, этот плагин помогает сильно экономить время при создании макетов страниц. Например, можно написать 

ul>li*4>a[href=#]{текст}

нажать Tab, или Ctrl+E и получится вот что:


Плагин CSS Comb

Применение этого плагина позволяет сделать код CSS более читаемым и красивым, т.е. улучшает стиль кода. Используется он следующим образом. Встаем курсором на блок CSS и нажимаем Shift + Ctrl + C.

До и после:



Информационные источники: ПервыйВторойТретий и еще какие-то...

воскресенье, 15 сентября 2013 г.

Как пользоваться git: тонкости

Прошла часть туториалов, находящихся вот тут. Разъяснилось несколько вещей (см. ниже).

Во-первых, ветки (бранчи) в Git не занимают много места, т.к. по сути это просто ссылки на конкретный commit и больше ничего. Во-вторых, ...

Merge vs Rebase

Команда merge создает специальный commit, у которого есть два уникальных родителя. Этот commit как бы говорит нам "я хочу включить в себя всю ту работу, которая была проделана при разработке обоих моих родителей и их родителей". Рассмотрим следующий пример.

Введем команду 
git merge bugFix
Получится вот что:



Теперь в master'е содержится вся выполненная работа. Теперь выполним такие команды:

git checkout bugFix
git merge master


Т.к. ветка bugFix находилась в дереве выше (то есть, менее актуальна), чем master, то ссылка bugFix просто передвинулась на тот же commit, на который указывает ветка master.

Команда rebase берет несколько commit'ов, копирует их, и помещает копии вниз под тем commit'ом, который мы укажем. Преимущество rebase в том, что он приводит историю commit'ов к линейному виду. Commit log/history будет выглядеть гораздо более красивым, чем до применения rebase. Рассмотрим следующий пример.


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

git rebase master

Получится вот что:
Создался новый commit с фичей из ветки bugFix, являющийся продолжением commit'а C2. Теперь у нас красивая линейная история commit'ов. Заметим, что commit C3 до сих пор где-то болтается (где именно, не написано было), а commit C3' как бы является его копией.

Теперь введем команды 

git checkout master
git rebase bugFix

Получится вот что:
Т.к. ветка master находилась в дереве выше, чем bugFix, т.е. была менее актуальна, то она просто передвинулась.

Reset vs Revert

Reset отменяет изменения путем переноса ссылки на предыдущий commit. Это как бы переписывание истории commit'ов. Пусть у нас вот такая история commit'ов:


Наберем команду

git reset HEAD~1

(HEAD это относительная ссылка на commit, на котором мы сейчас находимся. Оператор ~ позволяет прыгать по дереву commit'ов вверх или вниз на указанное количество шагов, в данном случае на 1)

Получится вот что:


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

Пусть у нас опять такая же ситуация, и мы хотим отменить последний commit:


Введем команду

git revert HEAD
И получится вот что:


Т.е. создался новый commit, аналогичный commit'у C1, не содержащий изменений, которые были в commit'е C2.

Cherry-pick

Эта команда позволяет более гибко перемещать commit'ы. Имеет следующий формат: cherry-pick <Commit i1><Commit i2>...<Commit ik>. Рассмотрим пример. Пусть у нас имеется вот такой репозиторий

 И мы хотим перенести в master все фичи, которые есть в commit'ах C3, C4, C7. Наберем команду

git cherry-pick C3 C4 C7

И получим желаемый результат:

И вот еще, про гигиену commit'ов:
  • Коммитить нужно часто, но атомарно (т.е. только законченные функции), также нужно подробно описывать, что в commit'е (иначе будет трудно поймать состояние проекта, будет трудно понять, куда нужно откатиться, если текущая версия не работает).
  • Не использовать git как систему бэкапа!

пятница, 13 сентября 2013 г.

Как называть переменные и функции: rules of thumb

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

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

Плохо:
int d: //Прошедшее время
Хорошо:
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

Плохо:
Хорошо:

2. Избегайте дезинформации.

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

2.2. Не обозначайте группу учетных записей именем accountList,  если только она действительно не хранится в списке. Лучше подойдут такие имена, как accountGroup, bunchOfAccounts или даже просто accounts. 

2.3. Остерегайтесь малозаметных различий в именах. Сколько времени потребуется, чтобы заметить незначительное различие в XYZControllerForEfficientHandlingOfStrings и XYZControllerForEfficientStorageOfStrings?

2.4. Не используйте комбинации из "l" и "O" в именах переменных, потому что они похожи на 0 и 1.

Плохо:
int a = l;
if ( O == l )
    a = ol;
else
    l = ol;

3. Используйте осмысленные различия

Плохо:
Хорошо:
Вместо a1 и a2 лучше использовать source и destination.

Плохо:
-Классы Product, ProductInfo, ProductData  - разные имена, которые по сути обозначают одно и тоже. 
-Методы getActiveAccount(), getActiveAccounts(),  getActiveAccountInfo(). Как понять, какую из этих функций вызывать в конкретном случае?

4. Используйте удобопроизносимые имена.
Если имя невозможно нормально произнести, то при любом его упоминании в обсуждении вы выглядите полным идиотом. "Итак, за этим би-си-эр-три-си-эн-тэ у нас идет пи-эс-зэт-кью, видите?"

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

6. Имена классов и объектов должны представлять собой существительные и их комбинации: Customer, WikiPage, Account, AddressParser. Старайтесь не использовать в именах классов такие слова, как Manager,  Processor, Data, Info. Имя класса не должно быть глаголом. 

7. Имена методов представляют собой глаголы или глагольные сочетания: postPayment, deletePage, save и т.д. Методы чтения/записи и предикаты образуются из значения и префикса get, set, is.
Хорошо:
string name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted())

8. Выберите одно слово для каждой концепции. Например, существование в разных классах эквивалентных методов с именами fetch, retrieve и get неизбежно создаст путаницу. Аналогичным образом, использование терминов controller, manager и driver в одной кодовой базе тоже вызывает путаницу. Чем DeviceManager принципиально отличается от ProtocolController?

9. Старайтесь не использовать одно слово в двух смыслах. Допустим, программа содержит много классов с методами add, которые создают новое значение сложением или конкатенацией двух существующих значений. Вы пишете новый класс с методом, помещающим свой единственный параметр в коллекцию. Стоит ли присвоить этому методу имя add? Т.к. новый метод имеет другую семантику, то ему лучше присвоить имя insert или append.

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

пятница, 30 августа 2013 г.

Шаблон логин-формы

Сверстала вот такую центрированную форму входа на сайт, вдруг кому еще пригодится, ну и мне как шпаргалка. Выглядит вот так:



А вот код:

вторник, 27 августа 2013 г.

Предобработка картинок в административной панели (фреймворк Yii).

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

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



Решение.

1. Подключение необходимых библиотек
Нам помогут три библиотеки: imgAreaSelectWideImagejquery.form. Первая библиотека требует более нового jQuery, который поставляется с Yii, поэтому также пришлось отключать "старый" jQuery и  подключать "новый". Для этого в /protected/config/main.php под 'components' напишем следующее:
Далее, в /protected/views/layouts/main.php напишем следующее:
Вот мы и подключили нужную версию jQuery и заодно imgAreaSelect. WideImage будем подключать "на лету" прямо в контроллере. 
2. Изменение модели
Объявляем переменные (photo - ссылка на файл-картинку, остальные переменные - координаты обрезания картинки). У меня модель находится в файле /protected/models/News.php):
В этом же файле изменяем функцию rules (в ней прописываются правила, которым должны удовлетворять данные, вносимые в форму):
3. Изменение формы
В форме редактирования страницы (у меня это файл /protected/views/news/_form.php)  убираем текстовое поле Picture, где хранится путь к файлу и добавляем кнопку выбора файла:
Также добавляем место, куда будем подгружать картинку для предпросмотра и переменные, где будем хранить сведения о координатах для обрезки картинки и о пути к загруженному файлу:
В самый верх файла добавляем скрипты. Они делают следующее.
После выбора пользователем файла, он отправляется на сервер, где файл сохраняется в папке tmp, обратно приходит ссылка на него. Далее в область предпросмотра подгружается картинка из присланной ссылки. Подключается к работе плагин imgAreaSelect. При изменении параметров обрезки картинки  в скрытых данных формы сохраняются координаты обрезки. Эти скрытые данные будут также переданы при нажатии на кнопку "Отправить". Кстати, за возможность отправки файла без перезагрузки страницы стоит поблагодарить плагин jquery.form, у него есть полезнейший метод ajaxSubmit. Оказалось, что просто так без перезагрузки страницы файл на сервер не отправить (из соображений безопасности). Вот этот плагин и выручил.
4. Изменение контроллера
В контроллере (у меня это файл /protected/controllers/NewsController.php) изменяем функцию accessRules - для того, чтобы разрешить пользователям сайта запускать функцию preview (функция preview сохраняет выбранный файл в папку tmp и передает на клиент ссылку на него):
А вот и сама функция preview:
Она такая большая, потому что выполняет кучу проверок - на формат файла, на размер, и т.д. Генерирует уникальное имя файла, зависящее от времени загрузки, ужимает до макс. 600 px по ширине и высоте, сохраняет файл в папку tmp и сообщает клиенту его адрес.

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

четверг, 8 августа 2013 г.

Фреймворк Yii: продолжение

Продолжаю изучать Yii по этой книге. Удивило то, что надо "ручками" создавать таблицы БД, а затем автоматом генерировать ORM-модели (например в Django все наоборот, в models пишешь модели, а потом запускаешь syncdb и таблицы генерируются сами). Для создания таблиц скачала phpmyadmin, его можно очень просто поставить (распаковать в папку откуда запускаются веб-приложения, переименовать, переименовать пример файла настроек config.sample.inc.php просто в config.inc.php). Таблицы в нем тоже очень просто создаются, а вот с настройкой связей между ними сложнее. В книге , например, приводится вот такой код для настройки связей между двумя таблицами:

ALTER TABLE 'tbl_issue' ADD CONSTRAINT 'FK_issue_project' FOREIGN KEY
('project_id') REFERENCES 'tbl_project' ('id') ON DELETE CASCADE ON
UPDATE RESTRICT;

такой запрос никак не хотел выполняться из-за ошибок в синтаксисе. В итоге выполнился вот такой запрос (спасибо stackoverflow):

ALTER TABLE `tbl_issue`
  ADD CONSTRAINT `FK_issue_project` FOREIGN KEY (`project_id`) REFERENCES `tbl_project` (`id`);

Ну а запрос INSERT INTO ... принял такой вид:

INSERT INTO `tbl_user`
  (`email`, `username`, `password`)
VALUES
  ('test1@notanaddress.com','Test_User_One', MD5('test1')),
  ('test2@notanaddress.com','Test_User_Two', MD5('test2'));

Для набора этих кривых кавычек, оказывается, есть отдельная кнопка на клавиатуре :)

вторник, 30 июля 2013 г.

Фреймворк Yii: первые шаги

Решила поизучать Yii с помощью этой книги. Для начала пришлось установить PHP & Apache. Оказалось, что тут описан "путь Воина", при выборе которого исходники Apache компилируются на компьютере пользователя. Это полезно, когда нужно настроить всякие тонкости. Мне же пока тонкости не нужны, поэтому можно воспользоваться LAMP и написать:

sudo apt-get install lamp-server^

Далее в книге предлагалось скачать yii, скопировать ее в web-accessible folder и проверить, удовлетворяются ли в системе минимальные требования для ее работы. Насколько я поняла, такой папкой является /var/www. Но в эту папку оказалось не так-то просто скопировать файлы. Поэтому пришлось копировать через терминал:

sudo cp -r /home/natalia/yii-1.1.2.r2086/* /var/www

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

127.0.0.1/requirements/index.php

(то, что localhost по адресу 127.0.0.1 увидела в сообщениях при установке LAMP)

Минимальные требования были выполнены. Далее в книге предлагалось воспользоваться инструментом yiic, который автоматически создает demo-приложение. Но если писать так, как указано в книге, никак не получалось. Ответ нашелся в документации по этой версии, скачанной с сайта yii framework. Воспользовалась одним из альтернативных предложенных в документации способов. Перешла в папку /var/www/framework и набрала

php yiic.php webapp /home/natalia/demo.

Потом попыталась открыть http://localhost/demo/index.php и увидела ошибку 404.

Оказалось, что нужно было настроить еще несколько вещей (спасибо Дмитрию).

1. Для удобства и пущей безопасности было решено хранить веб-проекты в папке /home/natalia/projects, а yii framework поместить в папку /home/natalia/projects/lib/yii. Настроить права к папке projects: sudo chmod -R 777 /home/natalia/projects.

2. Изменить права к папке apache, чтобы без проблем можно было сохранять внесенные изменения: sudo chmod -R 777 /etc/apache2

3. Настроить DocumentRoot в файле /etc/apache2/sites-available/default.txt DocumentRoot - указывает apache на то, откуда брать файлы для сайтов. В файле default вместо папки /var/www в паре мест написать /home/natalia/projects.

3. В demo-приложении в файле index.php поправить строку (путь к yii)
$yii = dirname(_FILE_).'../lib/yii/framework/yii.php';

4. Перезапустить apache

sudo service apache2 stop
sudo service apache2 start

После этого демо-приложение заработало :)



понедельник, 29 июля 2013 г.

Права доступа в Linux

Эта шпаргалка (конспект) написана на основе книги "UNIX. Программное окружение" Б.Кернигана, Р. Пайка. Ее порекомендовал мне Дмитрий, сказав, что многое что там написано, актуально и до сих пор. И правда актуально, а еще актуально не только для Ubuntu, но и для терминала в Mac OS. Итак, сама шпаргалка.

В каждой системе существует специальный пользователь, называемый суперпользователем (super-user), который может читать и модифицировать любые файлы в системе. Права суперпользователя присвоены регистрационному имени root, под которым администраторы управляют системой. Существует команда su, предоставляющая статус суперпользователя каждому, кто знает пароль.

Пароли хранятся в файле /etc/passwd, там же находится вся регистрационная информация каждого из пользователей.

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

ls -l выводит информацию о правах доступа.

Чтение прав доступа файла и каталога: примеры

ls -l /etc/passwd
>> -rw-r--r-- 1 root root 1878 апр.   1 22:10 /etc/passwd

Тут написано, что этот файл принадлежит пользователю root bp из группы root, размер файл 1878 байт, приводится дата последнего изменения. Разберем строку -rw-r--r-- (тут находится информация о правах):

Первый - говорит о том, что это обычный файл. Если бы это был каталог, то в первой позиции находился бы символ d.
rw- - права владельца на чтение, запись и выполнение. Владелец может читать и писать, но не может выполнять. У исполняемых файлов вместо прочерка должен стоять символ x.

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

Любой пользователь может редактировать файл паролей, но только root может сохранять сделанные изменения. Как изменить свой пароль? На помощь приходит команда passwd.

ls -l /bin/passwd
>> -rwsr-xr-x
Команда доступна для выполнения всем, но только root может ее изменить. Символ s вместо x в поле прав на исполнение для владельца говорит о том, что команда во время выполнения приобретает права своего владельца, в данном случае root. А т.к. /bin/passwd получает при исполнении идентификатор пользователя root, любой пользователь с помощью этой команды может изменить файл паролей.

Теперь почитаем права каталога.
ls -ld .
>> drwxrwxrwx 2 root root 4096 июля  29 22:21

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

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

Изменение прав доступа к файлу

Команда chmod (change mode) изменяет права доступа к файлу. Синтаксис: chmod права доступа имена файлов. Права доступа описываются в восьмеричном или символьном виде. Восьмеричные значения образуются путем суммирования: 4 для чтения, 2 для записи, 1 для исполнения. Например: chmod 666 junk.txt (т.е. всем можно читать и писать в этот файл теперь). Символьный вид: + включает право доступа, - его выключает. Например chmod +x command позволяет всем выполнять command, chmod -w file отменяет право на запись для всех, включая владельца.

Если каталог доступен для записи, пользователи могут удалять файлы независимо от прав доступа к этим файлам. Чтобы исключить возможность удаления файлов кем-либо, следует отменить разрешение на запись в каталог.
chmod -w .
(запретить запись в текущий каталог).
chmod 775 .
(восстановить права доступа)

пятница, 10 мая 2013 г.

Различные тонкости Python


Оператор сечения

Чтобы извлечь подстроку, можно использовать оператор сечения s[i:j]. Он извлечет из строки s все символы с порядковыми номерами k в диапазоне i<=k<j. Если какой-либо из индексов опущен, то он полагается равным началу или концу строки соответственно.

Оператор повторения

print '_'*10 
>>_ _ _ _ _ _ _ _ _ _

Строковое представление списка

ls = ['aaa', 'bbb', 'ccc']
print '---'.join(ls)
>>aaa---bbb---ccc

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

Оператор with

Синтаксически выглядит так:

with выражение:
     блок кода

Упрощает код, который раньше использовал try...finally... для гарантированного исполнения завершающего кода. Например:

with open('f.txt', 'r') as f:
     for line in f:
     print line

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

Рекурсия

#пример вычисления факториала

def factorial(n):
     if n <= 1: return 1
     else: return n*factorial (n-1)

Существует ограничение на глубину рекурсии. По умолчанию предел равен 1000.

Итерация с индексами

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

my_list = ['a', 'b', 'c']
for i,char in enumerate(my_list):
     print i, char

>>0 a
>>1 b
>>2 c

Упорядоченный словарь

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

d = {2:3, 1:89, 4:5, 3:0}
od = collections.OrderedDict(sorted(d.items()))
for k,v in od.iteritems(): print k,v
>>1 89
>>2 3
...

Генераторы

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

def countdown(n):
    print "Обратный отсчет!"
    while n > 0:
        yield n
        n -= 1

Любая функция, которая использует инструкцию yield, называется генератором. Метод next() заставляет функцию-генератор выполняться, пока не будет достигнута следующая инструкция yield:

c = countdown(5)
print c.next()
>>"Обратный отсчет!"
>>5
print c.next()
>>4
...

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

for i in countdown(5):
    print i

>>"Обратный отсчет!"
>>5
>>4
...

Генераторы списков

Генераторы списков - конструкции, которые облегчают работу со списками. Генератор списка обычно имеет вид [выражение(x) for x in некоторый_список]. В результате его использования мы получаем список, каждый элемент которого равен результату применения некоторого выражения к соответствующему элементу некоторого списка.
К примеру, функцию, которая на вход получает список положительных чисел numlist и возвращает сумму их последних цифр, можно написать так:

ls = [str(l)[-1:] for l in numlist]
ls = map(int, ls)
print sum(ls)

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

Ключевое слово лямбда

Может использоваться для создания небольших анонимных функций. Анонимными называются функции, объявляемые в месте использования и не получающие уникального идентификатора для доступа к ним. Может возникнуть вопрос: зачем вообще нужны эти анонимные функции? Если использовать ключевое слово лямбда в связке с такими функциями как map, reduce и filter, то можно сократить код (и таким образом сделать его более легким для восприятия). Ниже приведены примеры полезности лямбды.

foo = [2,18,9,22,17,24,8,12,27]

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

print filter(lambda x: x%3 == 0, foo)
>> [18, 9, 24, 12, 27]

#преобразуем список foo, умножив каждый элемент на 2 и прибавив 10

print map(lambda x: x*2 + 10, foo)
>>[14,46,28,54,44, 58,26,34,64]

'''получим сумму всех элементов списка. Функция reduce при первом вызове делает что-то с двумя первыми элементами, потом что-то с результатом и третьим элементом, и т.д.'''

print reduce(lambda x,y: x+y, foo)
>>139

А вот еще пара примеров применения лямбды. 

#узнать длины слов в предложении

sentence = "It is raining cats and dogs"
words = sentence.split()
lengths = map(lambda word: len(word), words)

#вывести True, если число четное и False в противном случае

g = lambda x: not bool(x % 2)
print g(4)
>>True

Сопрограммы

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

def print_matches(matchtext):
     print "Поиск подстроки ", matchtext
     while True:
          line = (yield)
          if matchtext in line:
               print line

matcher = print_matches("Python")
matcher.next()
>> Поиск подстроки python
matcher.send("Hello world")
>>
matcher.send("Python is cool")
>> Python is cool

matcher.close()

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

Функции в Python

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

Пример 1. Передача функции в виде аргумента

def callf(func):
     return func()

def helloworld():
     print "Привет, мир!"

callf(helloworld)
>>Привет, мир!

Пример 2. Сохранение в структурах данных.

Следующий кусок кода 


def function1():
    print '1'
    
def function2():
    print '2'
    
def function3():
    print '3'

if value == 'one':
    function1()
elif value == 'two':
    function2()
elif value == 'three':
    function3()

можно переписать так:


switch = {
     'one': function1,
     'two': function2,
     'three': function3,
}

choice = raw_input('Enter one, two or three')
try:
     result = switch[choice]()  #получаем функцию и тут же вызываем ее
except KeyError:
     print "I didn\'t understand your choice"

Замыкание      

Замыкание - процедура или функция, в теле которой присутствуют ссылки на переменные, объявленные вне тела этой функции и не в качестве ее параметров (а в окружающем коде). Рассмотрим следующий пример.

#файл foo.py
x = 42
def callf(func):
     return func()

#какой-то другой файл 
import foo
x= 37
def helloworld():
     return "Привет, мир!" x = %d % x

foo.callf(helloworld)
>>Привет, мир! x=37

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

Args и Kwargs

* - синтаксис для распаковки кортежа, ** - для распаковки словаря.
Рассмотрим следующий пример.

def tuple_test(x,y):
     x += 1
     y += 1
     print x, y

Если мы попробуем подать на вход этой функции кортеж, то будет ошибка:
t = (1,2)
tuple_test(t) #ошибка!

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

tuple_test(*t)
>>2 3

То же самое для словаря:

d = {'x':1, 'y': 2}
tuple_test(d) #ошибка!

tuple_test(**d)
>>2 3

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

def foo(*args, **kwargs):
     return args if args else kwargs

print foo(1,2)
>>(1,2)

print foo(1,2,3)
>>(1,2,3)

print foo(x=1, y=2)
>>{'x': 1, 'y': 2}

Декоратор

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

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
     ...

Функция login_required - декоратор, служит оберткой для функции my_view. login_required делает следующее:

если пользователь не залогинен, перенаправляет его на страницу авторизации,
в противном случае вызывает функцию my_view, которая формирует соответствующую html-страницу.

Вот еще пример декоратора.

enable_tracing = True

def trace(func):
    if enable_tracing:
        print "tracing..."
    else:
        return func
@trace
def square(x):
    return x*x

try:
    print square(2)
except:
    pass

>>tracing...

Информационные источники:



Архив блога