Tag Archives: робота

Як написати собі маленький Google Translate на Grammatical Framework за 15 хв

І з якістю трохи кращою ніж в Google Translate, але з обсягом можливих перекладів набагато меншим.

Що я роблю на роботі

Зараз я працюю в Zalando – компанії яка продає різний крам в купу країн Європи. Відповідно, треба підтримувати сайт для багатьох країн. Я працюю у відділі який займається інструментами що допомагають створювати наповнення сайту. Процес додавання нового вмісту на сайт дуже трудомісткий, бо потрібно сфотографувати продукт, на столі, на моделі, перевірити розміри, матеріали і інструкції з догляду, написати опис, заголовок і т.п.. А потім ще перекласти це на 8 мов і кілька локалей, бо деякі країни DACH (німецькомовний простір) не входять до єврозони і використовують швейцарські франки наприклад. Це велика купа часу, і великий простір для автоматизації.

Google Translate не підходить, тому що наприклад ми продаємо скатертини, і німецький менеджер пише “Tischdecken” (множина від Tischdecke), Google Translate думає що це дієслово (бо є таке дієслово), і перекладає “Set the table” (накривайте столи!).

Насправді задача стоїть простіша ніж повний машинний переклад, бо дані отримуються не якоюсь конкретною мовою, а набором параметрів – “що”, “коли”, “яке”, “для кого”, “по чому” і т.п.. Тобто якщо я отримую що=взуття, яке=класичне коли=зима для кого=хлопчики, по чому=<100 € то я (в сенсі моя програма), пише:

'de_CH': 'Klassische Winterschuhe für Jungs - unter CHF 107',
'de_DE': 'Klassische Winterschuhe für Jungs - unter 100 €',
'en_GB': 'Classic Winter Shoes for Boys - under £85',

Все просто – перекладаємо слова за словником і підставляємо в шаблон для конкретного набору компонентів. З німецькою лише один виклик – з’єднання слів. Тобто якщо я з’єдную Winter з чимось – просто конкатеную, а от коли Fruhling – додаю між Fruhling і тим словом яке приєдную ще “s”. Є ще інші правила.

Складність з’являється в слов’янських мовах. Там є відмінювання прикметників у множині.

На роботі я багато думаю як би то не робити роботу яку вже хтось зробив за мене

Програмісти ліниві створіння. Нащо писати код якщо код вже є написаний на StackOverflow і Github? Треба лише знати де той код лежить. І я почав собі шукати як інші люди це робили. До того як з’явилось машинне навчання і статистичні методи, всі пробували підхід на основі правил. І проміжної мови.

З проміжною мовою процес розбивався на дві частини – спершу проводився синтаксичний аналіз тексту на мові джерела, тоді з побудованого представлення тексту на проміжній мові (зазвичай дерево) будувався текст іншою мовою.

Моя задача вже простіша, бо ніякого синтаксичного аналізу робити не треба, треба просто згенерувати текст 8-ма мовами на основі структури даних. Це як два пальці відрендерити контекст за допомогою шаблону, ми веб-деви тільки таке й робимо.

Тільки виявляється що шаблон для генерації простого заголовку на зразок: “Mindestens 500 € sparen – Premium Herbstmode” або “Premium Herbstmode unter 999 €” займає мало не ввесь екран.

Я подумав що може нарешті настав той момент коли я на Кубику не даремно вчився, пам’ятаю Opa Chomsky Style, і треба застосувати якусь контекстно-вільну граматику, чи яка там наступна за потужністю, бо контекстно-вільна здається не потягне слов’янську морфологію.

І раптом в своїх пошуках надибав Grammatical Framework. Це штука яку вже більше ніж 20 років пишуть на Хаскелі, значить люди вміють точно більше ніж я. І автор приходить з доповіддю в Google, в якій кілька слайдів на початку – суцільний тролінг команди Google Translate:

Але мене підкупило речення з їх сайту, http://www.grammaticalframework.org/

GF is easy to learn by following the tutorial. You can write your first translator in 15 minutes.

До роботи!

Ну що ж, заводимо таймер, і спробуємо наприклад написати перекладач, що може перекласти набір шахматних словосполучень /(black|white) (queen|king)/ з англійської на українську. Шахмати, тому що одягом я й на роботі можу зайнятись, а блог я пишу вдома і хочеться трохи змінити контекст.

Для початку варто встановити компілятор/інтерпретатор gf. На сторінці завантаження є пакети для різних ОС, з інструкцією про те як їх поставити. Ще можна поставити плагін підсвітки синтаксису для вашого редактора, перелік є на цій сторінці.

Hello, world!

Тепер, в файлі Chess.gf пишемо таку граматику:

-- це, до речі, коментар
abstract Chess = {

  flags startcat = Piece ;

  cat PieceType ; Color ; Piece;

  fun
    piece : Color -> PieceType -> Piece ;
    Black, White : Color;
    Queen, King: PieceType;
}

abstract означає що ми описуємо граматику проміжної мови, тобто не якоїсь конкретної людської, а мови якою ми описуватимемо сенс тексту.

flags startcat = Piece ; означає що початковою категорією (коренем дерева) буде фігура.

cat перелічує можливі категорії (типи вузлів дерева).

Секція fun описує функції. Функції описують як будується дерево, і можуть бути будь-якої арності, в тому числі нулярні. Наприклад тут функції Black та White не приймають аргументів але повертають колір. (По суті – константи). Зате функція piece приймає дві категорії і повертає категорію для фігури.

Тепер про те як перетворити дерево на послідовність токенів якоюсь мовою. Це називається лінеаризацією, і описується конкретною граматикою:

concrete ChessEng of Chess = {

  lincat Piece, Color, PieceType = {s : Str} ;

  lin
    piece color type = {s = color.s ++ type.s} ;
    Black = {s = "black"} ;
    White = {s = "white"} ;
    Queen = {s = "queen"} ;
    King = {s = "king"} ;
}

Зберігаємо її в файлі ChessEng.gf. Для GF важливо щоб кожна граматика була в своєму файлі і щоб він називався так само як називається граматика, інакше він дає помилки.

lincat описує які типи токенів будуть відповідати категоріям абстрактної категорії. В нашому випадку це структура з одним полем типу Str, тому що пізніше нам знадобляться інші поля.

lin описує функції лінеаризації типи яких були описані в абстрактній граматиці. Для унарних функцій – як власне пишеться слово, для інших – як скомбінувати інші лінеаризації.

Щоб потестувати як все працює, запускаємо команду gf Chess*, яка завантажує обидві граматики і починає інтерактивну сесію.

Тут ми можемо наприклад перевіряти наші речення на правильність:

Chess> parse "black queen"
piece Black Queen

Chess> parse "snow queen"
The parser failed at token 1: "snow"

Правильно, ми тут пишемо граматику для шахмат а не для казок. Ще можна попросити згенерувати випадкове дерево:

Chess> generate_random
piece White Queen

Або його лінеаризацію:

Chess> generate_random | linearize
black queen

Або всі можливі речення:

Chess> generate_trees | l
black king
black queen
white king
white queen

Морфологія

Тепер давайте зробимо переклад на українську! Для цього треба описати граматику української. Додаємо переклад з англійської в файл ChessUkr.gf:

concrete ChessUkr of Chess = {

  lincat Piece, Color, PieceType = {s : Str} ;

  lin
    piece color type = {s = color.s ++ type.s} ;
    Black = {s = "чорний"} ;
    White = {s = "білий"} ;
    Queen = {s = "королева"} ;
    King = {s = "король"} ;
}

Завантажуємо і тестуємо перекладач в консолі:

Chess> parse -lang=ChessEng "black king" | linearize -lang=ChessUkr
чорний король

Дуже добре!

Chess> parse -lang=ChessEng "white queen" | linearize -lang=ChessUkr
білий королева

От засада! Навіть Google Translate вміє краще. Треба ввести в українську граматику поняття роду. Для цього до іменників треба додати інформацію про те якого вони роду, а кольори мають описувати як вони відмінюються залежно від роду:

concrete ChessUkr of Chess = {

  param Gender = Masc | Fem ;
  lincat Piece, PieceType = {s : Str; gender : Gender} ;
  lincat Color = {s : Gender => Str} ;

  lin
    piece color type = {s = color.s ! type.gender ++ type.s; gender=type.gender} ;
    Black = {s = table {
        Masc => "чорний";
        Fem => "чорна"
    } };
    White = {s = table {
        Masc => "білий";
        Fem => "біла"
    } };
    Queen = {s = "королева"; gender=Fem} ;
    King = {s = "король"; gender=Masc} ;
}

Тут нам і стало в нагоді те що наші категорії – це структури, а не просто рядки. Чіпляємо до кожної фігури інформацію про стать.

Gender => Str задає тип таблиці (це майже як функція, тільки описується чимось типу хеша). Кожен колір тепер – це таблиця. Операція “!” – це вибір значення з таблиці.

Тестуємо нову граматику української:

Chess> generate_trees | l
black king
чорний король
black queen
чорна королева
white king
білий король
white queen
біла королева


Chess> parse -lang=ChessUkr "білий король"
piece White King

Chess> parse -lang=ChessUkr "біла король"
The parser failed at token 2: "\1082\1086\1088\1086\1083\1100"

Хаха, він думає що ми неправильно вжили слово король, бо після біла може стояти лише королева. Але помилку знайшов!

Рефакторинг

Накодили, потестували, тепер пора зробити код гарнішим.

Писати словник в форматі:

    White = {s = table {
        Masc => "білий";
        Fem => "біла"
    } };

Коли ми можливо захочемо додати ще слів які можливо матимуть ще середній рід і множину – доволі трудозатратно. Але ми можемо написати функцію. Точніше оператор, бо в GF функцією ми вже назвали штуку яка будує дерево.

Ще нам знадобиться згадати школу, в якій нам казали що прикметники твердої групи в чоловічому роді завжди закінчуються на -ий, а в жіночому на -а.

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

  -- hard adjective
  oper ha: Str -> {s: Gender => Str} = \stem -> {
      s = table {
          Masc => stem+"ий";
          Fem => stem+"а"
      }
  };

Він приймає рядок і повертає токен в якого s – це таблиця що відображає рід на написання.

І тепер ми можемо швидко додавати багато прикметників:

    Black = ha "чорн";
    White = ha "біл";
    Fast = ha "швидк";
    Defenceless = ha "беззахисн";

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

Тепер додайте решту слів і маєте свій перекладач!

Теорія взаємодії процесів (насправді про IT-Arena)

Я не дуже хотів йти на Львів ІТ арену, бо то настільки понтово що задорого. Крім того на вузькоспеціалізованих конференціях на зразок PyCon я мало що розумію, навіть якщо сам доповідаю. 🙂 Хоча, знаєте, ото щойно передивився одну доповідь – і ніби все зрозумів (а що ще краще, виявляється що викладені там ідеї я зараз використовую в Angular, хоч і забув про них). Крім того, нащо йти на платну конференцію, якщо ти навіть не встигаєш читати всі блоги і дивитись всі безкоштовні відео доповідей з інших конференцій в інтернеті?

Але я пішов, і не пожалів. Познайомився з Естер Дайсон. Вона великий фанат здорового способу життя, і інвестор в наш проект.

Пішов на доповідь про мікросервіси оцього чоловіка. Там дізнався що всі системи які містять багато взаємодіючих компонентів можна описувати наприклад пі-численням. Але так як книжки з пі-числення страшенно дорогі, ось вам безкоштовна про математичну теорію названу “Взаємодія послідовних процесів”, і написана не аби-ким, а Сером Чарлзом Ентоні Річардом Гоаром. Тепер залишилось знайти час прочитати.

А ще поміж іншим дізнався про те що програмне забезпечення це лайно (точніше завжди знав), але існує стрібна куля. Називається LangSec, коли ми вхідні параметри описуємо якоюсь формальною мовою. Чим це відрізняється від Логіки Хоара і наприклад статичної типізації з алгебраїчними типами даних – ще треба подумати.

А ще зустрів хлопців з Quintagroup, вони зразу такі “О, це ти той пітонщик з SoftServe що пише на Zope”. Я такий – той, але вже не пітонщик і не з SoftServe. 🙂 Зараз вони багато працюють над проектом Prozorro, і шукають нових людей. Тому якщо знаєте Pyramid (чи який там фреймворк у https://github.com/openprocurement), шукаєте роботу – напишіть їм.


Filed under: Кодерство, Нещоденник Tagged: математика, робота, Python

Вступ до PowerShell

Зробив на роботі доповідь по PowerShell:

І не переживайте, там з восьмої хвилини в конференції нарешті настає тиша. :)

Презентація зроблена на основі вікіпідручника з PowerShell, який порізано на слайди і показано за допомогою deck.js (хоча про технологію якось іншим разом).

Це приклад того як доповіді робити не варто. Не варто змішувати підручник і презентацію, на презентації повинен бути лише код, картинки і максимум одне-два слова тексту. І якщо все таки якийсь текст вже є – не можна його читати. Ваші слухачі грамотні все таки.

З іншого боку – краще зробити щось неідеально, ніж не зробити ідеально. :)

Є інший приклад: Александр Соловьев — Functional Reactive Programming & ClojureScript. Чувак не париться, лише 16 слайдів, видно що тема його вставила, але пояснити він її може заледве на пальцях. Проте я таки зрозумів що FRP – це як в Excel, і при цьому не заснув.

P.S. Наступна доповідь буде кращою. :)


Filed under: Кодерство, Конспекти Tagged: освіта, робота, windows

Ітеративно і інкрементно

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

Тому тут просто процитую те що написано в підвалі блогу Едді Османі:

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

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

Створіть новий gist чи fiddle, відкрийте консоль і експериментуйте. Це, бляха, весело!

І справді, я тут подумав, що програмування перестає приносити задоволення, коли ти власне перестаєш втілювати ідеї і фіксиш баги, фіксиш баги, сидиш на нарадах, фіксиш баги.

Наступний “мінус” пов’язаний з тим, що розробка грандіозних ідей – це задоволення, а пошук паршивих маленьких “жучків” – це всього лише робота. В кожній творчій справі бувають жахливі періоди одноманітної й клопіткої праці, і програмування не є винятком.

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


Filed under: Кодерство, Нещоденник Tagged: психологія, робота, розробка

Красиво друкуємо xml в термінал

XML то жахіття ще те, але іноді його таки треба читати, нікуди не дінешся. Аби зробити своє життя трішки кращим можна написати таке:

def pprint_xml(text):
    import xml.dom.minidom
    from pygments import highlight
    from pygments.lexers import XmlLexer
    from pygments.formatters import TerminalFormatter
    xml = xml.dom.minidom.parseString(text)
    print highlight(xml.toprettyxml(), XmlLexer(), TerminalFormatter())

Потребує pygments.

Разом з Інтроспектором та іншими утилітами для дебагу це вже тягне на якусь лібу. Напевне наступного тижня заведу собі таку десь на github.


Filed under: Кодерство, Розмітка Tagged: робота, Python

Журнал роботи в Google Sheets та розширення його функцій з JavaScript

Коли я два тижні тому на роботі признався начальнику що вже все зробив, мій начальник вирішив поділитись мною з іншим начальником. Тепер я не можу все зробити, тому що один начальник проводить по дві наради на день на якій мене питають що я зробив, а інший начальник проводить одну нараду на день, на якій теж питають що я зробив. Крім того треба щодня писати звіти, по кожному проекту окремо, бо це впливає на бюджет проектів і на мою зарплатню. І щоб мені було легше пояснити одному начальнику що я сидів на нараді в іншого, та навпаки, я вирішив записувати що роблю протягом дня. Ну звісно виявилось що не багато роблю, але ви коли-небудь пробували записувати що робите? Якщо більше ніж 5 годин корисної роботи на добу – ви мій герой, навчіть мене.

Власне спочатку я хотів завести табличку по методології Pomodoro. Але, виявилось що Pomodoro – це занадто складно коли тебе три наради на день, якщо не рахувати переписок зі співробітниками в Skype. Тому я вирішив просто записувати час початку виконання і час закінчення.

В Google Sheets є дві корисні комбінації клавіш:

  1. Ctrl + ; – вставляє в клітинку дату
  2. Ctrl + Shift + ; – вставляє в клітинку час. В мене чомусь в форматі AM/PM.

Цього було б досить, але рахувати тривалість задач доводиться вручну. Незручно. Якщо є табличка:

A B C D
1 Time started Task Time finished Duration
2 1:32 PM Project A daily stand-up 1:51 PM 0:19

Хочеться щоб колонка D заповнювалась формулою. Але якщо написати в D2 =C2 - A2, воно видасть: “#VALUE!” – Помилка: не число: 1:51 PM. Тому треба використати функції.

Десь я чув фразочку що електронні таблиці – це функціональна мова програмування. Ах. :) Функцій там купа. Клітинок аби зберігати результати їх роботи – теж досить. Але вчити всі ті функції – ну їх.

На щастя Google Docs можна скриптувати за допомогою JavaScript! Ну й правда, не Visual Basic-ом ж користуватись у браузерному застосунку.

Як ми це робимо? Натискаємо “Інструменти” -> “Редактор сценаріїв”. Відкриється онлайновий редактор коду. Вставляємо туди щось таке:

// (c) Salman A     http://stackoverflow.com/a/1788084/816449
function date_diff(date1, date2) {
  if (date2 < date1) {
    date2.setDate(date2.getDate() + 1);
  }
  return date2 - date1;
}

function fmtmsec(msec) {
  var hh = Math.floor(msec / 1000 / 60 / 60);
  msec -= hh * 1000 * 60 * 60;
  var mm = Math.floor(msec / 1000 / 60);
  msec -= mm * 1000 * 60;
  return hh + ':' + mm;
}

function parsetime(cell) {
    var part = cell.match(/(\d+):(\d+)(?: )?(am|pm)?/i);
    var hh = parseInt(part[1], 10);
    var mm = parseInt(part[2], 10);
    var ap = part[3] ? part[3].toUpperCase() : null;
    if (ap === "AM") {
        if (hh == 12) {
            hh = 0;
        }
    }
    if (ap === "PM") {
        if (hh != 12) {
            hh += 12;
        }
    }
    return new Date(2000, 0, 1, hh, mm, 0);
}

function time_diff(arg1, arg2) {
  try {
    arg1 = parsetime(arg1);
    arg2 = parsetime(arg2);
  } catch (e) {
    return '';
  };
  return fmtmsec(date_diff(arg1, arg2));
};

function test() {
  Logger.log(time_diff('9:12 PM', '10:30 PM'));
}

Даємо модулю назву, на зразок Time diff, зберігаємо. В нашій табличці відкриваємо “Інструменти” -> “Менеджер сценаріїв” та переконуємось що наш сценарій там видно.

Тепер, в клітинці D2 можна писати =time_diff(A2;C2). І не поставте кому між аргументами. Буде синтаксична помилка яку я довго шукав в JavaScript, в той час як виявилось що проблема була в записі формули. І це ніби все.

Так просто що мені аж сподобалось.


Filed under: Інструменти, Кодерство, Павутина Tagged: Лайфхаки, JavaScript, робота

Це не так просто – розуміти.

Публікація про те як це, коли код доводиться читати а не писати, і що тоді робити. Тобто, як читати код?

Neo: Do you always look at it encoded? Cypher: Well you have to. The image translators work for the construct program. But there's way too much information to decode the Matrix. You get used to it. I...I don't even see the code. All I see is blonde, brunette, red-head. Hey, you uh... want a drink?

Нео: Ви завжди дивитесь не неї зашифрованою?

Сайфер: Ну, ми змушені. Транслятори зображення працюють над програмою конструювання. Але тут надто багато інформації щоб розкодувати Матрицю. Ти звикнеш. Я… Я навіть не бачу код. Все що я бачу – блондинка, брюнетка, руденька. Ей, ти еее… не хочеш випити?

Передмова

На моїй новій роботі (я не впевнений що мені дозволяє розповідати договір про нерозголошення, тому абстрагуватимусь як можу), проект задля якого мене найняли (там має бути Python, який як відомо легко читати і який я знаю досить добре, і проект новий) ще не почався. Але тим часом аби я не нудьгував, мені дали задачу з іншого проекту, там JavaScript, LeafLet (о, він чудовий), але моя задача – не така проста як видавалась спочатку.

Проблема перша – я не знаю ООП в JavaScript. Дякуючи документації до LeafLet, і тому що ООП там реалізовано через нього – проблема майже розв’язана. Ну й звісно JavaScript я в терміновому порядку підтягую.

Проблема друга – проект з галузі Business Intelligence. А я про схему даних “сніжинка”, OLAP-куб та інші подібні речі чую вперше. Але я почитав вікіпедію, подивився всілякі відеопояснення і тепер маю певне поняття. Якось ним поділюсь, може хтось скаже що моє поняття про BI хибне.

Третя, і головна проблема – купа коду без жодної документації. І на відміну від попередньої роботи (prom.ua), де проект пишуть роками і далі будуть, тут проект писали пів року (судячи з логів СКВ), дедлайн вже в кінці місяця, тому рефакторингом мене попросили не займатись. Ще до того як я запитав чи можна. Про документацію я не питав, тому що документація – це нереально, якщо ви звісно не пишете якусь бібліотеку з відкритим кодом. Зовсім нереально. Але юніт тести – в нашому випадку теж дуже важко, через специфіку середовища. Хоча менеджер сказав “Хочеш тести? Дуже добре!”, і при цьому якось дуже підозріло посміхнувся.

Хоча код знадобилось би трішки підчистити, тому що:

nested_code

Тому мій єдиний вихід – ввімкнути щось схоже на “Push it to the limit“, і поринути в читання. Але код – це не книжки (я читав книжку про те як читати книжки, а про те як читати код – не читав), крім того автор книжки зазвичай хоче аби ви прочитали книжку, а автор коду – лише аби комп’ютер той код зміг виконати, та й досвіду читання коду в мене набагато менше, тому думаю треба дізнатись як це роблять інші. Переберу-но я кілька статтей:

Мотивація

Елі Бендерський пише про те як розуміти власний код. Код який неможливо зрозуміти, на його думку – це такий самий, або можливо ще більший гріх, ніж код який не працює. А програмісту який з гордістю заявляє що не розуміє код який писав тиждень тому, треба давати в писок аби вибити з нього ту гордість. За цим посиланням мені “дають в писок”. Хоча я гордився не тим що написав незрозумілий код, а тим що я його таки зрозумів і навіть трішки спростив. :)

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

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

Зібрані поради

  • Зрозуміти що від програми вимагається і для чого її пишуть. Перед тим як робити все інше.
  • Спитати автора.
  • Пройтись за допомогою зневадника.
  • Вставляти багато print та assert.
  • Записувати всі відкриття в письмовій формі.
  • Спробувати щось змінити і подивитись що зміниться, а що зламається.
  • Парне програмування з розумним колегою чи автором. Спробуйте думати вголос. Може виявитись що в вас різні думки і почнеться продуктивна суперечка.
  • Код не читають рядок за рядком. Якщо щось не розумієте – можливо ви ще не прочитали код в кінці який потрібен для того щоб розуміти код посередині. Пропускаємо, ставимо закладку і повертаємось до нього потім.
  • Знайти функцію main, чи як там називається точка входу і читати з неї. Якщо точок входу багато (наприклад багато обробників подій) – виписати їх список.
  • Прочитати документацію викликів зовнішніх бібліотек. (На щастя сторонні бібліотеки документуються частіше.)
  • Починати з простих частин. Якщо ти не можеш розуміти навіть ті частини що здаються простими – код або занадто заплутаний, або ти недостатньо знаєш мову чи фреймворк.
  • Рефакторинг. Наприклад дайте нормальні імена ідентифікаторам, кілька разів застосуйте extractMethod
  • Додавання коментарів туди ж… Тільки будьте певні що вони не введуть когось хто прийде після вас в оману.
  • Якщо поняття не маєте як почати писати коментарі – описуйте для функції трійки Хоара.
  • Почати читати з тестів. Якщо таких нема – покривати тестами. (А тих хто написав код без тестів – покривати матами :D ). Подивитись чи тести проходяться. Якщо не проходяться – значить ви неправильно зрозуміли як повинна працювати програма (і добре що ви це дізнались раніше поки ще є час зрозуміти її правильно), або вона працює неправильно (Співчуваю ви знайшли баг. Якщо ще не впевнені що зможете пофіксити – додайте в багтрекер.)
  • Намалювати граф викликів.
  • Роздрукувати код на кольоровому принтері з підсвіткою синтаксису, розкласти його на підлозі, взяти в руки кілька маркерів та олівців і лазити виділяючи точки входу, важливі виклики, дописуючи коментарі на полях і позначаючи важкодоступні місця.
  • Намагатись зрозуміти лише необхідний мінімум і припускати що решта коду працює так як і повинна. (А як дізнатись як повинна? :) )
  • Згенерувати за допомогою ctags та cscope чи що там у вас є, індекс для прискорення навігації по коду. Хороше IDE – головний інструмент програміста-археолога. Також є інший софт, на зразок LXR (для читання коду Linux), Doxygen, Resharper, купа всякого…
  • Намагатись поміщати елементи в чорний ящик, тобто старатись зрозуміти ЩО вони роблять, а не ЯК вони це роблять. Правда тут є виверт 22 – код містить опис того як щось робиться, а що робиться – нам якраз потрібно зрозуміти. Тим не менш – треба абстрагуватись як тільки зрозуміємо якусь частину і переходити до наступної.
  • Спитати досвідчених користувачів програми, які не обов’язково повинні бути її творцями. Можливо якщо повністю зрозуміти як люди працюють з програмою – не доведеться її читати, можна буде написати свою версію. :)
  • Виділити найважливіші прецеденти, зрозуміти їх.

Посилання

Також варто сказати про те що тут – як з підтягуванням. Знання про те що не варто скрипіти зубами, напружувати прес чи інші м’язи (якщо в вас коліна підтягуються до грудей – це не добре). І не забувати видихати при скороченнях і видихати при розслабленні. І мати стабільний ритм. Але тільки це не допоможе вам підтягуватись 20 разів поки ви не спробуєте 2, потім 3, потім 4 і так далі, регулярно та постійно.

Література


Filed under: Кодерство, Нещоденник Tagged: книжки, робота, розробка

Найпростіший спосіб вимірювання найгіршої метрики

Або іншими словами – примітивний облік робочого часу в лінуксах.

Для цього є команда – last. Дозволяє подивитись в логах час останніх входів і виходів з системи. Якщо наприклад дати команду

last 7 # еквівалентно last tty7

Дізнаємось хто коли і скільки користувався сьомим терміналом:

bunyk    tty7         :0               Thu Feb  2 08:30 - down   (09:32)

Вдома попробував – не працює. А виявляється все тому, що є певна конфігурація ротації логів /etc/logrotate.conf, в якій написано – monthly.

Але команду я дізнався на початку місяця, коли ще жодних логів не було навіть на роботі. Якось неправильно ця ротація працює. Вдома логи чомусь все одно ведуться за один день, хоча я й написав що хочу місячну ротацію. Може не в ті логи написав.

А ще збацав коротенький скриптик, який бере дані з last, і рахує середній робочий час:

#!/usr/bin/python3
import re
import subprocess 

def hours(s):
    s = s.split(':')
    return float(s[0]) + float(s[1]) / 60

def to_str(x):
    h = int(x)
    m = 60 * (x - h)
    return '%s:%s' % (h, int(m))

output = subprocess.check_output(['last', '7']).decode('utf-8')

data = (re.findall(r'\((\d\d:\d\d)\)', x) for x in output.splitlines())
data = [hours(x[0]) for x in data if x]

print(output)
print('Average time: ', to_str(sum(data)/len(data)))

bunyk@bunyk-laptop:~/workspace/uaprom$ /home/bunyk/experimental/timestat.py 
bunyk    tty7         :0               Thu Mar 22 09:32   still logged in   
bunyk    tty7         :0               Wed Mar 21 10:37 - 19:51  (09:13)    
bunyk    tty7         :0               Tue Mar 20 11:48 - down   (08:16)    
bunyk    tty7         :0               Mon Mar 19 11:38 - down   (07:14)    
bunyk    tty7         :0               Sat Mar 17 09:45 - down   (08:36)    
bunyk    tty7         :0               Fri Mar 16 10:35 - 19:23  (08:48)    
bunyk    tty7         :0               Thu Mar 15 10:22 - down   (08:47)    
bunyk    tty7         :0               Tue Mar 13 10:02 - 19:44  (09:42)    
bunyk    tty7         :0               Mon Mar 12 12:08 - 20:34  (08:25)    
bunyk    tty7         :0               Wed Mar  7 10:29 - down   (07:42)    
bunyk    tty7         :0               Tue Mar  6 09:54 - down   (09:50)    
bunyk    tty7         :0               Mon Mar  5 11:15 - 19:32  (08:16)    
bunyk    tty7         :0               Sat Mar  3 11:21 - down   (07:03)    
bunyk    tty7         :0               Fri Mar  2 12:08 - down   (08:00)    

wtmp begins Thu Mar  1 18:46:07 2012

Average time:  8:27

Виходить що я трохи більше ніж пів-години на добу недопрацьовую. Правда шпигунський роман Тоні ДеМарко розповідає про те що до програмістів краще не застосовувати метрики, бо ті хитрі зарази знайдуть спосіб максимізувати метрику не покращуючи продукт. Якою гарною ця метрика не була б (хай навіть зміною відношення кількості вад до кількості функціоналу). А Тоні ДеМарко – виявляється в вікіпедії стоїть поруч з такими гігантами як Вард Каннінгам, Мартін Фаулер і навіть Тоні Хоар. Напевне варто познайомитись детальніше. Правда часу нема. Але якщо він не важливий, тоді важливе лише те що я вважаю важливою дією в кожен момент часу.


Filed under: Інструменти, Кодерство Tagged: linux, робота, Python

Небезпечні змінні об’єкти як значення аргументів функції за замовчуванням

Якось пишу я один сайт (та все той же) і натикаюсь на отаку грізну помилку:

Через деякий час знайшлась і причина помилки, і те падло яке цю причину написало:

Bunyk Fri Oct 07 13:45 2011:   def __init__(self, label=u'', validators=[
Bunyk Fri Oct 07 18:31 2011:       validators.NumberRange(min=decimal.Decimal('0.01'),
Bunyk Fri Oct 07 13:45 2011:       message=_(u"Значение должно быть больше чем %(min)s"))
Bunyk Fri Oct 07 13:45 2011:       ], **kwargs
Bunyk Fri Oct 07 13:45 2011:   ):

Ну справді, хто так пише. Дужку варто закривати на тому ж рівні вкладеності на якому вона відкривалась це раз:

def __init__(self, label=u'',
    validators=[validators.NumberRange(
        min=decimal.Decimal('0.01'),
        message=_(u"Значение должно быть больше чем %(min)s")
    )],
    **kwargs
):

І два – списки, словники та інші змінні (mutable) об’єкти не надто бажані як значення аргументів за замовчуванням.

Чому так? Тому що це уможливлює ефекти про які я зараз розповім. А ці ефекти можуть здивувати, як і повідомлення на картинці.

Як відомо, в Python все є об’єктом. Навіть функції і класи. А про будь-який об’єкт найважливіше що варто знати – це коли і як він створюється.

Тепер давайте розглянемо який-небудь сферичний код в вакуумі. Наприклад такий:

def next(line=[]):
    line.append(len(line) + 1)
    print line
    
next()
# OUT: [1]
next()
# OUT: [1, 2]
next()
# OUT: [1, 2, 3]
next(range(10))
# OUT: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11]
next()
# OUT: [1, 2, 3, 4]
next(line=[])
# OUT: [1]
next()
# OUT: [1, 2, 3, 4, 5]

Так от, щоб зрозуміти чому він так поводиться, варто знати, що змінні (і аргументи функцій) в Python це насправді не значення а посилання. А [] – конструктор порожнього списку.

В першому рядку коду створюється об’єкт функції, разом з ним створюється об’єкт списку, і стандартним значенням параметра функції призначається посилання на цей список.

Можливий варіант переписування коду так щоб він працював більш “очікувано”:

def next(line=None):
    if not line:
        line = []
    line.append(len(line) + 1)
    print line

Ну а зараз строгостатичні чистолямбдасамці можуть срати цеглою нижче. ;)


Filed under: Кодерство Tagged: помилки, робота, Python

Парадокс роботи програміста

Полягає в тому що вона таки виконується. І код, хоча містить сотні помилок, все таки працює.

Захід є Захід, а Схід є Схід, і їм не зійтися вдвох,
Допоки Землю і Небеса на Суд не покличе Бог;
Та Сходу і Заходу вже нема, границь нема поготів,
Як сильні стають лицем у лице, хоч вони із різних світів!

Р. Кіплінг.

Основна відома мені різниця між сходом і заходом – захід більше орієнтований на ціль, а схід – на процес. І того я не можу зрозуміти як в китайців з японцями і південнокорейцями взагалі щось виходить з роботами, Ruby і айфонами. Філософічка щось там казала про те що китайський воєначальник застосовує принцип недіяння коли починає атаку на ворога, хоча тоді і тепер я такий дзен не можу збагнути.

Далі. На моєму рідному факультеті дехто таки зібрався, і таки видали новий номер газети “Кубик”. (Не соромтесь, візьміть й собі копію. ~9 Мб).

І порівняно з попередніми номерами цей – огого! Він не вийшов в паперовій версії, але думаю це й на краще. Там 40 сторінок дрібним шрифтом! Мій факультет розорився б, якби надрукував це хоча б в 100 екземплярів. А pdf крім того ще й зберігає true color і поліграфічну якість. Я взагалі мрію про те, що на офіційному сайті факультету з’явився RSS. Але скоріше вже збудують метро на Теремки.

Так от, окрім статей про те як на факультеті все погано, і як ніхто не хоче писати в газету, мучать дівчат (головного редактора), і ніхто не хоче вчитись (ну окрім одного наївного першокурсника, який теж свої враження там описав), є багато цікавих речей. І думаю цікавих не тільки нашим студентам.

Наприклад на сторінці 11 є коротенька стаття про те як керувати програмістами. Якої звісно мало щоб навчитись це робити, але уявлення про предмет вона дає.

Коротко: програмістам треба платити достатньо аби вони не думали про гроші. Як відомо гроші не мотивують. Зате їх нестача і стереотип про те що програмістам багато платять мотивує знайти кращу роботу.

Тоді чого, якщо не грошей хоче програміст? Він, як творча людина хоче творити, отримувати від цього задоволення, саморозвиватись. Йому сам процес подобається. Програміст – зі сходу. Але це трохи не те що потрібно проекту.

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

І потім згадується методологія. Це така штука яка допомагає залишати програміста задоволеним, і окрім того добиватись того щоб він таки завершував те що повинен зробити.

І це, знаєте, мотивує розібратись з методологіями, і управлінням програмістами. Бо з’являється підозра що мене обманом заставляють робити те що я не хочу :D .

А якщо ввести назву методології яку використовують в нас, у пошуку картинок, то серед знайдених обов’язково зустрінете подібну на цю:
Mêlée ASM-MHRC

От так ми й працюємо. Ті двоє зліва – продакт менеджер і скрам мастер.. :)


Filed under: Кодерство Tagged: кубик, психологія, робота, розробка