company_banner

Задачи на собеседованиях в Яндексе

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

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

    image

    Чего ждать на собеседовании


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

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

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

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

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


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

    Пишем код


    Но главное для разработчика – это, конечно, умение писать хороший код. И принимать программиста на работу только на основе его теоретических знаний, мягко говоря, странно. Тут можно вспомнить отрывок из книги Тома де Марко и Тимоти Листера «Человеческий фактор»:
    Менеджер: «Как давно вы жонглируете?»
    Кандидат: «Ну, примерно шесть лет».
    Менеджер: «Тремя, четырьмя, пятью шарами умеете жонглировать?»
    Кандидат: «Да, да и да».
    Менеджер: «Работаете с горящими предметами?»
    Кандидат: «Конечно».
    Менеджер: «…ножами, топорами, открытыми коробками с сигарами, мягкими широкополыми шляпами?»
    Кандидат: «Мне всё равно, чем жонглировать».
    Менеджер: «А какую-нибудь весёлую скороговорку знаете?»
    Кандидат: «Онабесподобна».
    Менеджер: «Что ж, мне нравится. Думаю, мы вас возьмём».
    Кандидат: «Аааа… Не хотите посмотреть, как я жонглирую?»
    Менеджер: «Хм, мне как-то это не пришло в голову».

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

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

    По условию задачи у вас есть формула с цифрами, операциями +-*/ и скобками. Нужно написать программу, которая ее вычисляет. Формула может быть большой. Хочется, чтобы программу можно было легко дорабатывать, вводя функции, параметры и т.д.

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

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

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

    Но так как скобки присутствуют, дерево может иметь неограниченную вложенность. Наивное решение задачи выглядит следующим образом: находим скобки и полностью выкусываем их из получившейся строки и заменяем, например, на имена переменных a1, a2, a3, a4… После разбора получаем двухуровневое дерево. Затем в каждом узле дерева, где осталась переменная, проводим разбор того, что было в скобках, и вставляем результаты вместо соответствующих кусков поддерева.

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

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

    Код менеджера
    #include <string>
    #include <cassert>
    #include <memory>
    #include <stdexcept>
    #include <vector>
    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    struct Token {
            enum Type { value, operation, opening_bracket, closing_bracket};
            Type type;
            string text;
    };
    
    struct Tokenizer {
            //I am too lazy to write real tokenizer, it is very simple. I hope formula generator for fake tokenizer will be ok. 
    public:
            Tokenizer() { content=generate(); pos=content.begin(); } ;
            const Token* peek() { return pos!=content.end() ?&*pos:0; } ;
            const Token* get() { if (pos!=content.end()) { cout << pos->text << " "; } return pos!=content.end()?&*pos++:0; } ;
    private:
        vector<Token> generate(int level=0);
        
    private:
            vector<Token>::iterator pos;
            vector<Token> content;
    };
    
    //To be honest using classes for expression parsing is a bit overkill, old style could save some code. 
    class Expression;
    typedef struct auto_ptr<Expression> ExpressionPtr;
    
    //Represents abstract expression
    class Expression {
    public:
            Expression() {}
            virtual ~Expression() {}
            //actually this static parse functions should be constructor in most classes. I.e. this is violation of principle 'Resource Acquisition Is Initialization'.
            //but some functions return different classes depending on context, i.e. this is some kind of 'virtual constructor' (see Operation::parse for example)
            //so I made decision to make static construction function in all classes, just for uniformity
            static ExpressionPtr parse(Tokenizer& tokens);
            virtual float calc()=0;
            virtual void debug(string prefix)=0;
    };
    
    
    //Represents single value: for example 3.1415
    class Value: public Expression {
    public:
            Value() {}
            virtual ~Value() {}
            static bool isItYou(Tokenizer& tokens);
            static ExpressionPtr parse(Tokenizer& tokens);
            virtual float calc() { return _value; }
            virtual void debug(string prefix) { cout << prefix << "Value(" <<  calc() <<"): " << _value << endl; } 
    protected:
            float _value;
    };
    
    //Represents operation: + or -
    class Operation: public Expression {
    public:
            Operation() {};
            virtual ~Operation() {}
            static ExpressionPtr parse(Tokenizer& tokens, ExpressionPtr& l);
            virtual float calc();
            virtual void debug(string prefix) { cout << prefix << "Operation(" <<  calc() <<"): " << _operation << endl; if ( _left.get() ) _left->debug(prefix + "\t"); if ( _right.get() ) _right->debug(prefix + "\t"); } 
    protected:
            std::auto_ptr<Expression> _left;
            std::auto_ptr<Expression> _right;
            string _operation;
    };
    
    //Represents operation: * or /
    class PriorityOperation: public Operation {
    public:
            PriorityOperation() {};
            virtual ~PriorityOperation() {}
            static ExpressionPtr parse(Tokenizer& tokens, ExpressionPtr& left);
            //virtual float calc(); inherit it
            virtual void debug(string prefix) { cout << prefix << "PriorityOperation(" <<  calc() <<"): " << _operation << endl; if ( _left.get() ) _left->debug(prefix + "\t"); if ( _right.get() ) _right->debug(prefix + "\t"); } 
    };
    
    
    //Represents bracketed expression: (expr)
    class BracketExpression: public Expression {
    public:
            static bool isItYou(Tokenizer& tokens);
            static ExpressionPtr parse(Tokenizer& tokens);
            virtual float calc() { return _internal->calc(); } ;
            virtual void debug(string prefix) { cout << prefix << "Brackets(" <<  calc() <<"): "  << endl; _internal->debug(prefix + "\t"); } 
    protected:
            std::auto_ptr<Expression> _internal; 
    };
    
    
    ExpressionPtr Expression::parse(Tokenizer& tokens)
    {
        //cout << "Expression::parse" << endl;
        
            if (!tokens.peek()) return ExpressionPtr();
    
            if ( BracketExpression::isItYou(tokens) )
            {
                    return BracketExpression::parse(tokens);
            }
            else
            if ( Value::isItYou(tokens) )
            {        
                    return Value::parse(tokens);
            }
            else
            {
                    throw logic_error("(Expression) Wtf is that: " + tokens.peek()->text );
            }
    }
    
    bool Value::isItYou(Tokenizer& tokens) 
    {
            const Token* t = tokens.peek();
            if ( !t || t->type != Token::value ) return false; 
    
            char* endptr;
            strtod( t->text.c_str(), &endptr);
            return *endptr == 0;
    }
    
    ExpressionPtr Value::parse(Tokenizer& tokens)
    {
        //cout << "Value::parse" << endl;
        
            std::auto_ptr<Value> foo( new Value );
    
            const Token* t=tokens.get();
            assert( t && t->type == Token::value ); 
    
            char* endptr;
            foo->_value = strtod( t->text.c_str(), &endptr);
            return ExpressionPtr(foo.release()); //lack of heterosexual auto_ptr conversions is killing me
    }
    
    bool BracketExpression::isItYou(Tokenizer& tokens) 
    {
            return tokens.peek() && tokens.peek()->type == Token::opening_bracket;
    }
    
    
    ExpressionPtr BracketExpression::parse(Tokenizer& tokens)
    {
        //cout << "BracketExpression::parse" << endl;
            const Token* t=tokens.get();
            assert ( t->type == Token::opening_bracket );
    
            auto_ptr<BracketExpression> result ( new BracketExpression );
            ExpressionPtr null;
            result->_internal = Operation::parse(tokens, null);
    
            t = tokens.get();
            if ( t ==0 || t->type != Token::closing_bracket )
            {
                    throw logic_error("(BracketExpression) mismatched brackets ");
            }
    
            return ExpressionPtr(result.release());
    }
    
    
    ExpressionPtr Operation::parse(Tokenizer& tokens, ExpressionPtr& l)
    {
        //cout << "Operation::parse:" << l.get() << endl;
            ExpressionPtr left;
    
            if (l.get()) 
            {
                    left=l;
                    // left is assigned for us.
            }
            else
            {
                    left=Expression::parse(tokens);
            }        
    
            const Token *t=tokens.peek();
            if (!t || t->type == Token::closing_bracket  ) return left; //just return Value, sorry no operation guys
    
            if (t->type == Token::operation && (t->text=="*" || t->text=="/") )
            {
                    ExpressionPtr result = PriorityOperation::parse(tokens, left);                 
                    //we got exit after priority operations will end, parse position will be on + or - or at end
                    left = result;        
    
                    t=tokens.peek();
                    if (!t || t->type == Token::closing_bracket  ) return left; //just return Value, sorry no operation guys
            }
    
        //cout << "Operation::parse again" << endl;
            if (t->type == Token::operation && (t->text=="+" || t->text=="-") )
            {
                    tokens.get(); //just commit previous peek
    
                    auto_ptr<Operation> result ( new Operation );
                    result->_operation = t->text;
                    result->_left=left; //smart ptr giveup ownership
            
            //cout << "Operation::parse before token" << endl;
            ExpressionPtr foo=Expression::parse(tokens);
            //cout << "Operation::parse after expression" << endl;
            
            const Token *t=tokens.peek();
            
            if (t != 0 && (t->text=="*" || t->text=="/"))
            {
                //cout << "Operation::parse to priority" << endl;
                foo = PriorityOperation::parse(tokens,foo);
            }
            
            result->_right=foo;
            
            ExpressionPtr bar(result.release());
            return Operation::parse(tokens, bar);
                    
            }
            else
            {
                    throw logic_error("(Operation) Wtf is that: " + tokens.peek()->text);
            }
    }
    
    ExpressionPtr PriorityOperation::parse(Tokenizer& tokens, ExpressionPtr& left)
    {
        //cout << "PriorityOperation::parse" << endl;
        
        // left is already assigned for priority operation
            const Token *t=tokens.peek();
            if (!t || t->type == Token::closing_bracket  ) return left; //just return Value, sorry no operation guys
    
            if (t->type == Token::operation && (t->text=="*" || t->text=="/") )
            {
                    tokens.get(); //commit previuos peek
    
                    auto_ptr<PriorityOperation> result ( new PriorityOperation ); 
                    result->_operation = t->text;
                    result->_left=left;
                    result->_right=Expression::parse(tokens);
                    ExpressionPtr rs(result.release());
    
                    return PriorityOperation::parse(tokens, rs);
    
            }
            else if (t->type == Token::operation && (t->text=="+" || t->text=="-") )
            {
                    return left;
            }
            else
            {
                    throw logic_error("(PriorityOperation) Wtf is that: " + tokens.peek()->text );
            }
    }
    
    
    float Operation::calc()
    {
            if (_operation == "+")
            {
                    float l=_left.get()?_left->calc():0.0f;
                    float r=_right.get()?_right->calc():0.0f;
                    return l+r;
            }
            else
            if (_operation == "-")
            {
                    float l=_left.get()?_left->calc():0.0f;
                    float r=_right.get()?_right->calc():0.0f;
                    return l-r;
            }        
            else
            if (_operation == "*")
            {
                    float l=_left.get()?_left->calc():1.0f;
                    float r=_right.get()?_right->calc():1.0f;
                    return  l*r; 
            }        
            else
            if (_operation == "/")
            {
                    float l = _left.get()?_left->calc():1.0f;
                    float r = _right.get()?_right->calc():1.0f;
                    return l/r;
            }
            else
            {
                    throw logic_error("Wft: operation udefined");
            }                
    }
    
    //returning vector by value, will be slow( O(n*n) actually ), but it is just testing code.
    vector<Token> Tokenizer::generate(int level)
    {
        //be careful with this value - formula size is approx 2^level
        if (level > 6)
        {
            Token t;
            char f[100];
            snprintf(f,100,"%d",int(rand() % 100));
            t.text=f;
            t.type=Token::value;
            return vector<Token>(&t,&t+1);
        }
    
        if (rand() % 10 == 0)
        {
                vector<Token> result;
                Token t1,t2;
                t1.type=Token::opening_bracket;
                t1.text="(";
                t2.type=Token::closing_bracket;
                t2.text=")";
                result.push_back(t1);
                vector<Token> r=generate(level+1);
                result.insert(result.end(),r.begin(),r.end());
                result.push_back(t2);
    
                return result;
        }        
    
        char op = "+-*/"[rand()%4];
        Token t;
        t.type=Token::operation;
        t.text=op;
    
        vector<Token> result=generate(level+1);
        result.push_back(t);
        vector<Token> r2=generate(level+1);
        result.insert(result.end(),r2.begin(),r2.end());
    
        return result;
    }
    
    int main()
    {        
            try
            {
                    //create fake tokenizer
                    Tokenizer tk;
    
                    //parse it
                    ExpressionPtr null;
                    ExpressionPtr foo = Operation::parse(tk,null);
                    cout << endl;
                    foo->debug("");
                    float result = foo->calc();        
                    cout << "result = " << result << endl;
            }
            catch(exception& e)
            {
                    cout << e.what() << endl;
                    return 1;                  
            }
    
            return 0;
    }


    Код разработчика
    #include <stdlib.h>
    #include <string.h>
    #include <iostream>
    #include <string>
    #include <vector>
    #include <stdexcept>
    
    struct token {
        enum { E_UNDEF, E_NUMBER, E_OPERATOR, E_LEVEL  } type;
    
        union {
            double d_val;
            int i_val;
            char c_val;
        } data;
    
        token() {
            type = E_UNDEF;
        }
    
        token(double val) : type(E_NUMBER) {
            data.d_val = val;
        }
        token(int val) : type(E_LEVEL) {
            data.i_val = val;
        }
        token(char val) : type(E_OPERATOR) {
            data.c_val = val;
        }
    };
    
    typedef std::vector<token> tokens;
    
    void push_level(tokens &pr, int level) {
        if (pr.empty() || pr.back().type != token::E_LEVEL) {
            pr.push_back(token(level));
        } else {
            pr.back().data.i_val += level;
        }
    }
    
    void push_operator(tokens &pr, char op) {
        pr.push_back(token(op));
    }
    
    void push_number(tokens &pr, int num) {
        if (pr.empty() || pr.back().type == token::E_LEVEL || (pr.back().type == token::E_OPERATOR && pr.size() > 1 && pr[pr.size() - 2].type == token::E_NUMBER) ) {
            pr.push_back(token((double)num));
        } else if (pr.back().type == token::E_OPERATOR && (pr.size() == 1 || pr[pr.size() - 2].type == token::E_LEVEL) ) {
            if (pr.back().data.c_val == '*' || pr.back().data.c_val == '/') {
                throw std::domain_error("unexpected operator");
            }
            if (pr.back().data.c_val == '-') {
                num = -num;
            }
            pr.pop_back();
            pr.push_back(token((double)num));
        } else {
            throw std::domain_error("unexpected number");
        }
    }
    
    token calc3(tokens &pr) {
        token s2 = pr.back(); pr.pop_back();
        token op = pr.back(); pr.pop_back();
        token s1 = pr.back(); pr.pop_back();
    
        if (s1.type != token::E_NUMBER || op.type != token::E_OPERATOR || s2.type != token::E_NUMBER) {
            throw std::domain_error("unexpected closing brace");
        }
    
        switch (op.data.c_val) {
            case '+':
                s1.data.d_val += s2.data.d_val;
                break;
            case '-':
                s1.data.d_val -= s2.data.d_val;
                break;
            case '*':
                s1.data.d_val *= s2.data.d_val;
                break;
            case '/':
                s1.data.d_val /= s2.data.d_val;
                break;
            default:
            throw std::domain_error("internal error");
        }
    
        return s1;
    }
    
    void pop_level(tokens &pr, int level) {
        if (level == 0) {
            if (pr.size() > 3) {
                pr.push_back(calc3(pr));
            }
            return;
        }
        if (pr.empty() || pr.back().type == token::E_LEVEL || pr.back().type == token::E_OPERATOR) {
            throw std::domain_error("unexpected closing brace");
        } else if (pr.size() > 1 && pr[pr.size() - 2].type == token::E_LEVEL) {
            if (pr[pr.size() - 2].data.i_val > level) {
                pr[pr.size() - 2].data.i_val -= level;
            } else {
                int delta = level - pr[pr.size() - 2].data.i_val;
                token tmp = pr.back();
                pr.pop_back(); pr.pop_back();
                pr.push_back(tmp);
                pop_level(pr, delta);
            }
        } else if (pr.size() > 3) {
            token s1 = calc3(pr);
    
            if (pr.back().type == token::E_LEVEL) {
                if (pr.back().data.i_val > level) {
                    pr.back().data.i_val -= level;
                    pr.push_back(s1);
                } else {
                    int delta = level - pr.back().data.i_val;
                    pr.pop_back();
                    pr.push_back(s1);
                    pop_level(pr, delta);
                }
            } else if (pr.back().type == token::E_OPERATOR) {
                pr.push_back(s1);
                pop_level(pr, level);
            } else {
                throw std::domain_error("unexpected closing brace");
            }
        } else {
            throw std::domain_error("unexpected closing brace");
        }
    }
    
    double process(const std::string &str) {
        tokens program;
    
        push_level(program, 3);
        for (std::string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
            switch (*cit) {
                case '(':
                    push_level(program, 3);
                    break;
                case ')':
                    pop_level(program, 3);
                    break;
                case '*':
                case '/':
                    pop_level(program, 1);
                    push_operator(program, *cit);
                    push_level(program, 1);
                    break;
                case '+':
                case '-':
                    if (cit == str.begin() || strchr("(+/-*", *(cit-1))) {
                        push_operator(program, *cit);
                    } else {
                        pop_level(program, 2);
                        push_operator(program, *cit);
                        push_level(program, 2);
                    }
                    break;
                case ' ':
                    break;
                case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
                    {
                        int curnum = 0;
                        while (cit != str.end()) {
                            curnum = 10*curnum + (*cit - '0');
                            if ((cit + 1) == str.end() || !isdigit(*(cit+1))) {
                                break;
                            }
                            ++cit;
                        }
                        push_number(program, curnum);
                    }
                    break;
                default:
                    throw std::domain_error("unexpected symbol");
            }
        }
        pop_level(program, 3);
    
        if (program.size() == 0 || program.size() > 1) {
            throw std::domain_error("incomplete expression");
        }
    
        return program.back().data.d_val;
    }
    
    int main() {
        std::string line;
        while (!std::cin.eof()) {
            std::getline(std::cin, line);
    
            if (line.length() > 0) {
                try {
                    std::cout << process(line) << std::endl;
                } catch (std::exception &e) {
                    std::cout << "error: " << e.what() << std::endl;
                }
            }
        }
    
        return 0;
    }
    

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

    Если говорить об алгоритмах разбора, то классическим считается shunting yard algorithm. Также помимо Recursive descent parser популярными является LR-парсер.

    Кроме кода


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

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

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

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

    В конечном итоге, мы принимаем решение, подходит нам кандидат или нет. Однако окончательно ясно, приживется ли человек в Яндексе, становится после испытательного срока. Если бы о работнике все можно было понять исключительно по собеседованию, мир бы выглядел совсем иначе.

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

    Список литературы для разработчика на С++
    • Вирт, «Алгоритмы + структуры данных = программы»
    • Кормен, Ривест, и другие «Алгоритмы: построение и анализ»
    • Липпман «Основы программирования на С++»
    • Scott Meyers. Effective C++. More Effective C++. Effective STL
    • Herb Sutter «Exceptional C++» «More Exceptional C++»


    (Кстати, все вакансии Яндекса можно найти здесь.)
    Метки:
    Яндекс 666,53
    Как мы делаем Яндекс
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 329
    • +1
      Жаль на хабре не придумали заметки :(
      • +2
        Какие заметки? Заметки есть для пользователей, а ещё Вы можете добавить статью в избранное и указать собственные теги.
      • +134
        Проходил собеседование в Yandex на вакансию Android-разработчика. Целый час все разговоры были только теоретические: контейнеры, сложность алгоритмов и т.д. Вопросов по синтаксису или по компонентам, собственно, самой платформы задано не было.
        По истечению часа прервали собеседование, мол «на собеседование отводится не более часа».

        Пара неприятных моментов:
        1) По прошествии какого-то времени пришла стандартная отписка. Такая же практика и в mail и других подобных компаниях, которые не могут по-нормальному написать письмо с отказом. В небольших компаниях HR или тех. специалист даже сам может позвонить и сообщить о решении. Видимо, в больших компаниях людей не особо ценят.
        2) На мои вопросы о том, чем придётся заниматься, какая команда, какие условия, какая з/п, ответа не получил. Отнекивались мол «мы не имеем права разглашать эту информацию». Выходит, хотите нанять хорошего разработчика, который в итоге получает кота в мешке.
        3) То что, на собеседовании не спрашивают про саму платформу/язык, а задают какие-то абстрактные теоретико-алгоритмические вопросы, для меня навсегда останется загадкой.

        • НЛО прилетело и опубликовало эту надпись здесь
          • +2
            И не только в Я. Практически во всех компаних (по моему опыту) спрашивают только теорию (которая, как правило, не особо полезна на практике)
            • +6
              Странная у вас практика. В моей практике сплошные NP-hard задачи, а тут без теории не обойтись.

              Да ведь спрашивают ведь не что-то навороченное, а базовые вещи которые должен знать разработчик. Про Яндекс могу сказать, что у них объемы данных такие, что даже классические алгоритмы пробуксовывают. Вот тут вы почувствуете разницу между O(n*log(n)) и O(n^2).
              • +3
                Вы программист в академической сфере или в производственной? Вторые как бы это странно не звучало не пишут алгоритмы а лишь пользуются трудами первых. Проще говоря используют готовые решения.
                • +1
                  Думаю разрабоку компиляторов можно отнести к производственной сфере.
                  У меня есть опыт создания и Web-приложений, и библиотек, то есть того что используется в продакшене. Во всех случаях знание алгоритмов и структур данных требовалось. Почему-то все с кем я работал хотели, чтобы их приложения работали быстро.
                  • +1
                    Почему-то все, с кем я работал, хотели, чтобы их приложения были быстро написаны. Avoid premature optimization.
                    • 0
                      Avoid premature optimization.
                      Это немножко из другой области. При разработки архитектуры приложения у нас принято проводить анализ предметной области. Например, если количество данных не больше 2^32, мы спокойно используем битовый вектор. Если данные равномерно распределны, то хэшь таблицу. Знаем какие у нас данные и соотвественно выбираем алгоритмы и структуры данных. Где здесь преждевременная оптимизация?

                      Под преджевременной оптимизацией обычно понимают, инлайнинг всего чего не поподя, оптимизацию операций внутри цикла, типа вместо деления на 2, битовый сдвиг вправо. Использование массивов, якобы для уменьшения кэш миссов. И прочее.
                      • +1
                        Под преджевременной оптимизацией обычно понимают любые изменения кода «чтобы было быстрее/меньше по размеру» без предварительной оценки, где тормозит/где толсто, и, порой, вообще без насущной необходимости.

                        То, что вы перечислили, при определённых условиях, вполне может выступать в роли реальной оптимизации.
                  • 0
                    Программистов вокруг много и готовых результатов, которым можно пользоваться — тоже. Одну и ту же задачу разные фреймворки\утилиты решают по-разному. И не зная теоретических основ, можно легко выбрать неподходящее в вашем случае решение. Так что вопрос очень и очень спорный.
              • +2
                Мне как-то девушка позвонила их HR Яндекса из Питерского офиса и тоже на позицию Андроид разработчика. Сказала, что вышлет мне тестовое задание. Если мне не изменяет память, надо было написать архиватор аля gzip за 1 час (ну я так задание это понял и выглядело оно именно так) и это в рабочий день. Само собой я за это время на работе врядли смог бы что-то сделать, кроме предполагаемых интерфейсов класса…
                • +2
                  Почитайте эту статью (http://habrahabr.ru/post/178037/). ZeptoLab просит написать игру в тестовом задании.

                  Я тоже подумывал к ним устроиться, но когда ты только переехал в чужой город и ищешь работу, то тратить недели/месяцы на тестовое задание не видится возможным. К тому же, судя по истории zagayevskiy, шансы туда устроиться даже с готовым заданием малы.
                  • +1
                    Кстати, они все еще ищут девелоперов, начали поднимать базы претендентов годовой давности.
                    • +4
                      Вдруг кто-нибудь за год написал эту чёртову игру.
                      • 0
                        Я написал пакмана, и чуть-чуть не дописал их текущую хотелку — Астероиды. =)
                    • 0
                      На сайте теперь нормально указывают, что нужен человек, который напишет за 10-12 часов.

                      И что нужны строго плюсы, а не Obj C

                      где — то год назад я написал, так что даже позвали на собеседование, но требования высоковаты оказались — нужно было польше опыта именно работы, а я скорее для себя программирую
                  • +132
                    Обычно на собеседованиях спрашивают то, что сами недавно узнали.
                    • +8
                      Я с Вашего позволения это затвиттил. Само собой, с указанием копирайта.
                      • +52
                        Чем хорошь хабр так это тем, что никогда не угадаешь за что могут слить карму.
                        • +8
                          Не знаю, не знаю. Мне, как программисту, гораздо больше нравится логичная предсказуемость вычислительных систем. Никогда не знаешь, чего ждать от этих загадочных людей.
                        • +6
                          А также тем, что непонятно за что её добавляют.
                          • +2
                            Если кармой дорожишь, до такой степени, что изливаешь свою боль публично, — потеряешь. Пиши хорошие статьи, карма после перехода границы в 20-30 уже не важна.
                            • 0
                              Дело не в этом. Дело в непредсказуемости что ли. Карма здесь (ну мне лично) нужно лишь для возможности публикации. Есть штук пять работ, которые оформляю в виде статей. У меня, если честно первый раз так — комментарий и слитая карма. Ведь важна не карма, а понимание того, что, если кто-то слил, значит твой коммент кого-то обидел или покоробил. А это приводит к непониманию и самокопанию. Наверное, не надо быть таким мнительным, да.
                        • +22
                          На самом деле есть еще более продвинутая концепция, нужно на собеседования задавать вопросы, на которые вообще ответа никто в компании не знает и уже несколько месяцев все ищут :)

                          Часто так делаем на собеседованиях людей уровня эксперта.

                          Если что, это не на случай, что вдруг сейчас скажут правильное решение решение, так ни разу не было(хотя каждый раз надеемся). А просто если человек придет он тоже будет искать ответ на этот вопрос, и будет круто проверить, что он на одной волне, в смысле понимает тему и может в команде работать над поиском ответов.
                          • +5
                            В двух местах (я сейчас говорю не о яндексе) мне предложили в качестве «домашнего задания» довольно сложные алгоритмические задачи. Обе задачи весьма «синтетические». Я думаю 95% кандидатов просто берут самоотвод, увидев условие. Особенно обидно, когда решаешь задачу (пусть не идеально, но тратишь на нее какое-то весьма ненулевое время, а в ответ потом тишина)

                            Хотите испытать себя? Могу продиктовать условие.
                            На вход алгоритму дается последовательность беззнаковых 48-битных чисел.
                            Программа должна отбросить все составные (то есть не простые) числа и найти наидлиннейшую возрастающую последовательность из простых чисел. ( 2, 12, 3, 4, 11, 5, 7 -> ответ: { 2, 3, 11 } )
                            Размер исходных данных неизвестен. Естественно, числа там большие, так что проверять на простоту каждое встреченное число скорее всего долго. Ограничение по памяти — программа 32-битная. Должна использовать несколько ядер проца.

                            На вторую подобную задачу сейчас жду ответа. Честно говоря, в третий раз подобным аниматься не захочется
                            • +5
                              Можно найти все простые до sqrt(2^48)=2^24 решетом Эратосфена на битовом массиве, понадобится 2^24 / 8 = 2^21 байт, каких-то жалких 2 метра. Потом классический алгоритм для поиска наидлиннейшей возрастающей подпоследовательности. Только фиг знает, куда здесь запихать многоядерность.
                              • +10
                                В результате прогона решета у вас будет все простые числа до 2^24, но между 2^24 и 2^48 тоже наверняка встречаются простые числа и их тоже будет нужно найти, для этого достаточно будет поделить их на все простые числа из решета. «Вот тут то мышка и пригодилась....» (с) анекдот. В смысле куда деть несколько CPU теперь понятно(да и решето на них можно считать, если аккуратно).

                                Ход мыслей imho очень правильный, хотите к нам в Яндекс?
                                • +1
                                  Вы приглашаете? )
                                  А если серьёзно, мне бы выучиться сначала. Злобный физтех не пускает переводников посередине года, но если преуспею в Хитром Плане™ попасть туда через семестр, то ждите через пару-тройку лет :)
                                • 0
                                  Да можно просто исключать в несколько потоков.
                                  • 0
                                    По-моему, решето даже не нужно. Можно просто параллельно каждое число факторизовывать за O(sqrt(N)) (то есть если мы нашли делитель до корня, то грустно — число не подходит, иначе оно простое). Потом да, классика за O(n*logn), так как итак есть O(n*sqrt(N) / ядра), то возрастающая последовательность не даст крутой вклад в производительность.

                                    Вообще, я бы из задачи убрал возрастающую последовательность и получил бы неплохую задачу. Так как про проверку на простоту кандидат по идее знать должен, а вот про поиск последовательности — это уже число олимпиадная фишка :)
                                  • +4
                                    а почему ответ в вашем примере не {2, 3, 5, 7}? я что-то невнимательно прочёл?
                                    • 0
                                      потому что 11 нельзя выбросить. это же простое
                                      • +2
                                        Если «нельзя выбросить», то слово «наидлиннейшая» будет неуместно, потому что последовательность выбирается единственным образом.
                                      • 0
                                        Когда просят найти «наидлиннейшую возрастающую подпоследовательность», надо переспрашивать — элементы из подпоследовательности должны идти в исходной последовательности подряд, или не обязательно? Похоже, что в этой задаче — подряд.
                                        • 0
                                          Я прошу прощения за то, что написал условие задачи несколько невнятно. Писал по памяти, как раз в примере отразил особенность — нельзя игнорировать простые числа. Иначе задача сводилась бы к тривиальной — проходим последовательность «с конца», находим в ней простые числа меньше предыдущего найденного
                                          • 0
                                            Задача «найти самый длинный монотонно возрастающий фрагмент» на порядок проще, чем «найти самую длинную монотонно возрастающую подпоследовательность с возможными пропусками». Если первая легко решается за линейное время и за один проход, то для второй нужна дополнительная память O(N), и не удивлюсь, если в худшем случае потребуется квадратичное время (O(n*sqrt(n)) уж точно бывает).
                                            • НЛО прилетело и опубликовало эту надпись здесь
                                              • 0
                                                А, там возможен бинарный поиск по длине. Тогда конечно.
                                      • +2
                                        Мы аккуратно обращаемся с объемными задачами на дом, ровно потому, что понимаем что хорошие разработчики обычно более-менее заняты.

                                        Могу вам посочувствовать, и предложить мне мне anatolix@yandex[-team].ru заслать вашу реализацию про поиск 48-битных простых чисел, на которую вам не ответили, если хорошо написано мы вам ее зачтем ее в карму на собеседовании.
                                        • +2
                                          Придерусь к «Программа должна отбросить все составные (то есть не простые)».
                                          Единицу надо отбрасывать?
                                          • 0
                                            это несущественно. единицу и ноль (буде таковые встретятся) можно считать как угодно.
                                            в условии это явно не было указано, но на сложность и структуру алгоритма это не повлияет
                                          • +4
                                            Проверять на простоту можно вероятностным тестом за O((log n)^3) битовых операций.
                                          • +1
                                            Ха, мы тоже так делали во внутренних сервисах :) Например, спрашивали верстальщика как бы он сверстал сетку занятости переговорок, которая ехала в «Опере». Самый кайф был, когда один верстальщик подсказал идею как это можно было сделать!
                                            • +13
                                              Взяли его хоть?
                                              • +22
                                                Зачем, если он и так все выболтал?
                                                • 0
                                                  Взяли, вроде :) Не помню, несколько лет прошло :)
                                            • +2
                                              Самоутверждение за счет претендентов? :-)
                                            • +13
                                              Это вполне могло произойти, особенно если было давно. Мы сейчас пытаемся с этим бороться, обучая людей которые ведут собеседования, так быть не должно, но не могу гарантировать, что везде уже хорошо.

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

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

                                              p.s. Если мне в anatolix@yandex[-team].ru напишите ФИО, email и примерную дату собеседования, могу поузнавать подробности и попробовать что-нибудь в косерватории починить, не смотря на то android от меня совсем далеко.

                                              p.p.s Предложение, кстати относится не только к вам лично, но и вообще ко всем кто по какой-то причине остался недоволен собеседованием в Яндексе.
                                              • +12
                                                Расскажу свой опыт собеседования в Я. Собеседовался 2 раза за 2 года — 1 раз очно, 1 раза удаленно. 1 раз все закончилось оффером, 1 раз — отказом. В целом очень не равномерно получилось, первое собеседование было очное, достаточно живо прошло с обоюдным интересом. Два человека меня собеседовали четыре часа, вопросы, задачи и прочее.
                                                Второе собеседование было достаточно печально, 4 собеседования по 1 часу на скайпе, все испортило первое собседование (по профилю вакансии — Java, т.к. на java-developer`а шел), где три анонимных собеседователя (знал только их имена), весело и довольно не сдержанно, смеялись над моими решениями задач, это задело, я задал вопрос, что такого смешного, пускай в возможно неверном решении. Такое замечание видимо их задело, и дальше разговор не сложился :) Оставшиеся 3 собеседования и собеседники были на уровне, но я понимал, что уже нет смысла, основное завалилось, ну и настроения было уже ниже не куда. Закономерный результат — отказ.
                                                Понимаю, что раз на раз не приходится, но случай засел в памяти, видимо до тех пор, пока не наткнусь на еще более «интересный» способ проведения собеседования.
                                                • +2
                                                  Зря вы свой HR ругаете :). Нормальный он у вас. Менеджерам за auto_ptr нужно сказать «ая-яй-яй».

                                                  В качестве примера, идеального собеседования, могу привести собеседование в Амазон. HR занимался организацей процесса собеседования, отвечал на все возникающие вопросы, запрашивал feedback на собеседующих. Кроме того HR задавал воросы нетехнического характера: почему Амазон, про опыт и т.п. Когда в ответе было много технической информации, честно говорили, что они не специалисты в данной области. Люди, которые проводили собеседования, были вежливы. Вопросы задавали по существу, а не с целью завалить.
                                                  • +1
                                                    Я не свой ругаю, а вообще, HR без программиста не может рекрутить. Да вот мы сейчас пытаемся такую же систему сделать как вы описали в amazon.

                                                    Ну вот я менеджер использовавший auto_ptr, скажите мне «ая-яй-яй», за что кстати?
                                                    • 0
                                                      :) Использование auto_ptr в ответе на тестовую задачу не проблема. Использование в продакшен коде — проблема. Вы создаете новые типы. Где гарантия, что у другого разработчика не возникнет желание воспользоваться ими. А если возникнет, то ему нужно будет знать об опасности и значит о внутренней реализации.

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

                                                      В Амазоне фокус был на алгоритме решения задачи. Если при ее решении нужны, например стек, дерево или граф, то можно описать из через интерфейсы.
                                                      • +4
                                                        > Вы создаете новые типы.
                                                        Где это, std::auto_ptr это очень даже стандартный тип, плюс по-моему это почти единственный способ их функции что-то вернуть по указателю а не значением, и не уточнять кто теперь главный за освобождение этого указателя.

                                                        Можно сравнить чем голый указатель опасен, или shared_ptr опасен.

                                                        Такие задачи мы тоже даем. В основном устно.
                                                        • +5
                                                          В новом стандарте auto_ptr объявлен deprecated из-за ряда проблем. Вместо него теперь unique_ptr.
                                                          • 0
                                                            Да, это в старом стандарте написано.
                                                          • 0
                                                            typedef struct auto_ptr<Expression> ExpressionPtr;
                                                            class Operation;
                                                            class BracketExpression;

                                                            Вот новые типы, использующие auto_ptr.

                                                            Современные компиляторы умеют делать RVO, что уменьшает накладные расходы возврата значения.
                                                            • 0
                                                              И что чем это плохо? Плохо плодить сущности, в которых надо разбираться(если их бояться надо писать на plain C), в те которые устроены стандартным способом и все знаю как не вызывают проблем.
                                                              • 0
                                                                Если это локализовано внутри одного файла и гарантировано, что используется только внутри и не является публичным API, то проблем никаких. Иначе гарантировано найдеться умник, который отстрелит себе ногу или даже две :). Ведь люди начинают вчитываться в документацию только после того, как что-то не работает или же перестает работать.
                                                                С auto_ptr придется раскрывать детали внутренней реализации, описывать это в документации, причем очень большими буквами. А потом может получиться что поменяли внутренную реализацию, а документацию не проапдейтили.
                                                                Работа в большом проекте научила, что нужно быть крайне осторожным с тем, что делать публичным API. К сожалению в головы многих разработчиков вбито: не пиши свое, переиспользуй что есть. При этом как-то забывают заострить внимание, что нужно проверять будет ли то, что написано работать в 100% случаев твоей задачи. И в итоге получается, что время тратится не на написание кода, а на разбор багов. Причем багов нетривиальных. А переиспользованный код обрастает кучей неочевидных костылей. А потом тот кто саппортит исходный модуль очень удивляется этим костылям, в его картине мира эти случаи не возникают. Тут ему объясняют, что это костыли, чтобы поддержать корректную работу другого модуля.
                                                                • +1
                                                                  Как я уже писал мне кажется что неопределенность с тем кто должен освобождать то что из программы вернули сильно хуже чем неочевидность autoptr. Новым типом его не считаю то такой non-syntax sugar
                                                                  Понятно что move semantics и uniqptrв cxx11 наверное более лучшее решение той же проблемы
                                                            • +1
                                                              auto_ptr не рекомендуется использовать т.к. он может покрашить память где не надо. Простой пример:
                                                              std::vector<std::auto_ptr<int>> vec;
                                                              ...
                                                              std::auto_ptr<int> tmp = vec[0];
                                                              

                                                              Вот в этом месте среда отдает все права владения куском памяти в котором хранится vec[0] переменной tmp, при этом элемент вектора становится невалидным. А это довольно сложно отлавливаемая ошибка. В стандарте C++11 вместо него рекомендуется использовать std::unique_ptr… в мне пофиксили проблему с неявным изменением прав владения, т.к. в нем нет конструктора копирования. А для явной передачи прав владения надо использовать std::move…
                                                              Т.ч. за std::auto_ptr в продакшене надо делать ай-яй-яй)
                                                              • 0
                                                                не делайте так ©
                                                                • 0
                                                                  вы сейчас серьезно?:)
                                                                  • +1
                                                                    чуть-чуть серьезно. Смысле в том что в том контексте как был использован auto_ptr он использован правильно и по делу. Аргументация, что вы его можете случайно положить в vector она очень странная, сроду что вы зря взяли язык C++ на нем программа может упасть.
                                                                    При этом недостатки auto_ptr-а я конечно понимаю.
                                                      • 0
                                                        Я как-то писал тестовое задание на стажировку (честно признаюсь, что желания устраиваться у меня не было, область все-таки другая, да и на программиста я не учился. Но нужны были относительно простые задачки для изучения языка). Так вот там была задача пропарсить кучу исходников на предмет глобальных переменных, я тогда решил загнать их в компилятор и посмотреть вывод, однако задумка провалилась, в куче исходников использовались всякие функци вроде printf без включения библиотеки stdio, компилятор плевался. Я спросил, можно ли включить в таких файлах библиотеку, или ее выпилили специально для поиска иного решения, на что получил пространный ответ, что это задание построенно на основе проблем, с которыми часто сталкиваются программисты, и что все исходники проверялись на компиляцию. Все с тех пор хочу спросить (как не программист, конечно), чем вы там компилируете?
                                                        А еще одна задачка была на переформатирование списка файлов/директорий между разными форматами. Я ее решил, скормил вебморде автотестера, код прошел пару десятков тестов, потом сломался, причем вебморда мне сообщила, что на вход было подано больше текста, чем она может вывести, ну и на выходе соответсвенно тоже. Как дебажить — непонятно, ну и на этот вопрос сопровождающий мне так и не ответил…
                                                        • +2
                                                          Дебажить — вдумчиво и зорко :)
                                                          Вам еще повезло, я делал эти же три задания когда-то давно, и два из них были на тестовом сервере неправильно реализованы, мне пришлось писать длинные поэмы в саппорт, какие у них баги и как их, скорее всего, можно починить.
                                                          • +2
                                                            Все с тех пор хочу спросить (как не программист, конечно), чем вы там компилируете?
                                                            Любым нормальным компилятором C (не C++!). По стандарту функции типа printf объявлять, как ни странно, не обязательно.

                                                            Хотя это, конечно, и бардак.
                                                            • 0
                                                              Без указания подробностей кто и когда выдал сложно ответить на этот вопрос точно. Как вам написали скорее всего если нет include <stdio.h> это была C программа, а не C++, там это делают сплошь и рядом.
                                                            • +1
                                                              О собеседованиях в Яндексе у меня и знакомых такой опыт:
                                                              Если собеседование на C++-позицию то впечатление о собеседовании достойное.(человек 10)
                                                              Если собеседование на Java-позицию то не очень.(человек 5).

                                                              На моей памяти только один Java-ист который нашел общий язык с Java ребятами на собеседовании в Яндексе. Но в Яндекс он так и не пошел.

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

                                                              Замечу что все Java-исты были не-Android. Все ребята из Москвы и речь о очных собеседования(5 часов вопроса + задача на месте в следующий раз)
                                                              • +2
                                                                На последней встрече с Java -командой, инициированной ими, вообще разговор не завязался. Они с трудом понимали что за задачи они хотят мне предложить, и не могли ответить на простые вопросы по постановке задачи.

                                                                Через час бессмысленного разговора прервала присутствовавшая на нем HR, и сообщила размер компенсации. Был крайне удивлен суммой в 2.5 раза меньшей чем моя зарплата на тот момент. При том что на HH была ясна указана сумма, с которой я начинаю рассматривать предложения.
                                                                • 0
                                                                  можешь pls мне написать подробности на anatolix@yandex[-team].ru, хочу узнать в чем прикол был.
                                                            • +3
                                                              На мои вопросы о том, чем придётся заниматься, какая команда, какие условия, какая з/п, ответа не получил

                                                              Когда я устраивался — получил ответы на все эти вопросы. А команда собственно была на собеседовании.
                                                              • +2
                                                                Обычно собеседование проводит тимлид
                                                                • 0
                                                                  На самом деле у Яндекса несколько команд которые занимаются разными проектами на Андроиде, и если это было первое собеседование то это мог быть скрининг сразу во все проекты. На очном уже всегда присутствуют представители всез команд, просто до очного можно не дойти
                                                              • 0
                                                                Правильно, они оценивают ваши способности к алгоритмическому мышлению — основной навык программиста. Знание конкретной платформы или языка программирования не является ценностью — «правильный» программист, обложившись книжками и конкретными задачами, сможет освоить любую платформу/язык за пару недель.
                                                                • 0
                                                                  И они будут ждать, пока этот человек будет с 0 осваивать новую платформу/язык?
                                                                  • 0
                                                                    В Яндексе, как и любой другой крупной IT компании, есть несколько уровней собеседований. На первом этапе, как правило, отсеиваются все, кто не знает алгоритмы и самого языка.

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

                                                                      Так как что-то придётся осваивать всё равно, так что знание вами готовых систем — всего лишь небольшой плюс, неумение достаточно быстро осваивать новое — профнепригодность.
                                                                    • +3
                                                                      Ну в чем проблема, освойте язык и приходите, мы пару недель то уж собеседования подождем :)

                                                                      p.s. Глобально вы конечно правы. Для хорошего программиста знание языка программирования ничто. Проблема в том что есть много людей которые за много лет вообще ни одного языка не освоили хорошо, и чтобы их отсечь про язык надо таки спрашивать.
                                                                      • +2
                                                                        Мое мнение (не исключаю, что оно в корне неправильное) заключается в том, что главное — это умение решить прикладную задачу, а не знание различия между static_cast и reinterpret_cast (хотя это тоже не вредно знать). В реальной жизни у нас возникают задачи с нечеткими условиями. Или даже если сегодня условие задачи звучит четко — надо понимать, что завтра мир немного изменится и задача изменится вместе с миром. Умение написать код так, что он будет работать завтра и послезавтра в изменившихся условиях (ну или хотя бы чтобы его можно было бы поправить не переписывая с нуля) важнее, imho, споров о безопасности auto_ptr.

                                                                        Допустим, надо в исходную задачу добавить вычисления синуса, косинуса, логарифма и квадратного корня. Чей код (разработчика или манагера) вы возьмете а основу если вам надо решить теперь эту задачу? Или напишете свое решение?
                                                                        • 0
                                                                          Мое мнение, что хороший программист должен уметь и то и другое, и если он не умеет это подозрительно. можно любой взять.
                                                                      • 0
                                                                        Освоить можно, а вот насколько правильно он смоет ее использовать после такого экспресс освоения?
                                                                        • +1
                                                                          Смутно представляю, как можно обложившись книжками за пару недель стать спецом по Java или .Net. Или начать сколько-нибудь качественно писать на C++.
                                                                          Надеюсь, что ваше сообщение — это ирония над «правильными» программистами, а не искренняя убежденность в том, что исключительно алгоритмическое мышление делает человека ценным разработчиком.
                                                                          • 0
                                                                            Ну понятно, что если человек всю жизнь на Java писал, то, например, Erlang он за пару дней не освоит. Но обычно у хорошего программиста есть хороший кругозор. Скажем тот же Java программист наверняка пробовал Python или Ruby. Скорее всего использовал JS и Bash.

                                                                            Проблема в другом. Программисты нужны для чего? Чтобы создавать ПО. У ПО есть сроки и критерии качества. Если программист может создать ПО с этими критериями качества за установленное время, то не всё ли равно как он знает язык программирования?
                                                                            • 0
                                                                              Согласен. Но качество ПО больше зависит от знаний фреймворков, архитектуры приложений и их принципов их взаимодействия.
                                                                              Математические алгоритмы нужны гораздо реже, большинство из них уже оформлены в библиотеки, и изобретение велосипеда не требуется.
                                                                              В Яндексе и подобных компаниях, могут быть свои особенности, но даже у них 90% разработчиков заняты в совсем не «творческих» задачах.
                                                                              • 0
                                                                                Кстати, Erlang как раз и освоит.
                                                                          • +1
                                                                            Разгадка в том, что в разных группах собеседование проводится по-разному, никакого общего стиля нет.
                                                                            • 0
                                                                              Мне кажется они не разработчиков ищут а натаскивают интервьюверов. С таким колличеством желающих копаться с легасикоде крупной компании можно организовывать платные тренинги для интервьюверов.
                                                                              • +2
                                                                                Ну это получается как в том анекдоте:
                                                                                Приходит профессор домой и говорит жене: «Я сегодня лекцию прочитал так хорошо, что даже сам понял»
                                                                              • 0
                                                                                1) К сожалению, обычная практика, кстати, хорошо хоть отписка была, многие вообще никакой обратной связи не дают.
                                                                                2) В таких случаях, я думаю можно не рассматривать это собеседование, как возможность устроится на работу, жаль времени, но это не дело. Я обычно начинаю задавать вопрос по Д. Спольски и подсчитывать количество набранных баллов, в данном случае это 0 баллов.
                                                                                3) Это дань уважения традициям для 80% вакансий, не более того.
                                                                                • +1
                                                                                  Disclaimer: я не знаю, в какой отдел вы собеседовались, когда и кто это делал. И я не могу ничего сказать по первым двум вопросам т.к. с этой областью не связан. Но могу предположить про «теоретико-алгоритмические вопросы» на собеседовании. Возможно в вашем случае ситуация была следующей:

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

                                                                                  У команды, про которую я говорю, стояла задача написать качественное приложение, чтобы его потом было легко поддерживать. Причем приложение с большим сроком жизни. Яндекс большая компания и готова сейчас потратить много ресурсов на разработку, в рассчете на то, что эти затраты окупятся в будущем. Насколько я понимаю, это отличается от наиболее распространенного подхода к разработке мобильных приложений.
                                                                                • +33
                                                                                  Когда ты уверен в себе и знаешь сколько ты стоишь, то собеседование обычно начинается с того, что они могут предложить тебе. А уже потом, если тебя это устроит, то ты уже рассказываешь, что ты можешь предложить им.

                                                                                  Чем это хорошо? Да тем, что на первом этапе может тебя не устроит что-то, а может их и смысла узнавать о твоих знаниях уже не имеет. Ну или же услышав от тебя, что ты хочешь 100500млн денег, они уже зададут вопросы тебе на эти деньги, а не вопросы, которые стоят 100 денег :)
                                                                                  • +6
                                                                                    Полностью согласен.
                                                                                    Как-то странно получается — человек приходит, тратит свое время, что-то доказывает, а ему даже не готовы сообщить, для чего он нужен и на какую сумму может рассчитывать.
                                                                                    Пора формулировать встречные вопросы для HR:
                                                                                    1. Кто вам нужен?
                                                                                    2. Зачем?
                                                                                    3. Сколько вы готовы за это платить?
                                                                                    Имхо, после ответа на эти вопросы будет гораздо проще разобраться с требованиями к кандидату.
                                                                                    • 0
                                                                                      Я не совсем HR, но попробую ответить. Проблема в том, что дать вот эти 3 конкретных ответа можно на одну конкретную вакансию, и нельзя дать про весь Яндекс, в котором сейчас вакансий наверное штук 300.

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

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

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

                                                                                      • +1
                                                                                        Да, согласен. В больших компаниях, где куча открытых вакансий такая проблема есть. Ищут не на конкретное место, а просто определенных специалистов, которые могли бы пригодиться.
                                                                                        Я имел в виду, что если есть серьезные разногласия между ожиданиями претендента и компании, то лучше прояснить их как можно раньше, не тратя ни чьего времени.
                                                                                        Например, пришел человек, претендует на позицию D3, заявляет какие-то навыки в резюме. Было бы здорово, если бы ему еще до собеседования сообщили вилку зарплаты, на которую он может рассчитывать, и хотя бы примерный круг ответственности.
                                                                                        Дело в том, что уровень зп и обязанностей разработчиков одного грейда между разными компаниями сильно отличается.
                                                                                        Возможно, что у вас другой подход и разброс по зп и обязанностям в рамках грейда очень большой в самой компании. Тогда было бы очень интересно почитать, отчего так получается и какие преимущества это дает.
                                                                                    • +1
                                                                                      Это кажется все лечится указанием желаемой зарплаты в резюме или на сайте типа HH, где это резюме лежит.
                                                                                      • +1
                                                                                        К сожалению это не не так, не раз сталкивался с ситуацией, когда на первом собеседовании называешь свои ожидания, на что получаешь ответ, что это в их бюджете, потом собеседования, задания, ожидание, а в итоге получаешь предложение на 20-50% меньше оговоренного, оказывается бюджет у них ниже, а вдруг человек согласиться и на такой?
                                                                                        • +1
                                                                                          Да это даже не звоночек, а оглушающий колокол. Зачем связываться с конторой которая начинает сотрудничество с вранья?
                                                                                          • 0
                                                                                            Я и не связываюсь, но время и силы уже потрачены, а мне было куда их тратить и без них, к сожалению в последний раз когда искал работу несколько раз натыкался на такой подход.
                                                                                    • 0
                                                                                      Добрый день.
                                                                                      А собеседования на позицию стажёра проходят по этой же схеме?
                                                                                      • 0
                                                                                        Да, только задание не одно, а больше
                                                                                        • 0
                                                                                          Скажем так, эта схема это примерно, то что мы хотим от разработчика базового уровня. Поэтому вопросы обычно все те же(но возможно сложных не спросят, задачу дадут попроще), но по ответам обычно предполагается скидка за отстутствие опыта.
                                                                                          • 0
                                                                                            Поделюсь опытом — у меня было три тестовых задачи и две недели времени. Задачи вроде бы несложные на первый взгляд, но прежде чем решение пройдёт все тесты, приходится достаточно долго думать над оптимизацией.

                                                                                            Потом по результатам смотрят, рассматривать дальше кандидатуру или нет.
                                                                                            • 0
                                                                                              Понятно, спасибо за ответ.
                                                                                          • +42
                                                                                            Очень приятно работать зная как закодить синтаксичекий парсер разгребая кучу легаси кода в авральном режиме.
                                                                                            • +3
                                                                                              можете пояснить мысль, пожалуйста.
                                                                                              • +23
                                                                                                Вы не знаете, что такое Legacy-code? Устаревший код, который более не поддерживается и не обновляется, но используется (ну, либо левый код от каких-то сторонних разрабов).

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

                                                                                                Собственно, разрабу, который знает что такое LR-парсер, и как можно вашу задачу решить вообще без построения деревьев, очень грустно и печально интегрировать в систему код десятилетней давности, купленный у каких-то индусов. О чём и писал автор предыдущего комментария.
                                                                                                • +2
                                                                                                  Я знаю что такое legacy код. Я попросил автора комментария уточнить, что он имел ввиду, вдруг это как-то касается Яндекса. Код у индусов мы вроде не покупаем, весь код в рабочем состоянии.
                                                                                                  Если вы можете написать синтаксический парсер, зачем работать над кодом который написали люди которые не могут?
                                                                                                  • +15
                                                                                                    Во время работы над каким то большим проектом львиную долю времени отнимает рутинная работа и очень мало — написание нового кода. А уж разработка алгоритма, решающего какую-то сложную и нетривиальную задачу (проектирование) — просто как манна небесная.

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

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

                                                                                                    В больших фирмах считают что они дают тебе работу и ты им должен до конца дней, поэтому на собеседованиях такое отношение.
                                                                                                    В фирмах поменьше с тобой общаются на равных, и предлагают тебе сотрудничество.
                                                                                                    • +2
                                                                                                      В условиях задачи же написано «в авральном режиме». Извините, вы не проходите.
                                                                                                • 0
                                                                                                  Хотя я не поддерживаю этот подход к отбору сотрудников, предположу, что такие знания помогут разобраться в чужом сложном коде с хитрыми алгоритмами. Плюс, покажет общий уровень профессионального интеллекта кандидата.
                                                                                                  • 0
                                                                                                    Аплодирую стоя!
                                                                                                  • +9
                                                                                                    Мне код менеджера больше нравится. Дело не в классах, просто он понятнее :-)
                                                                                                    • +3
                                                                                                      +1, я вообще приятно удивлен кодом менеджера. А вот код разработчика мне не нравится ни разу, я бы подумал что это джуниор.
                                                                                                      • +7
                                                                                                        Спасибо за комплимент менеджеру, код на самом деле мой :)
                                                                                                        Я в меру условно менеджер с 10 годами бекграунда в C++ просто долго не тренировался

                                                                                                        На самом деле с точки зрения практики код программиста мне нравится больше, в задаче парсинга классы это некий оверкилл, прочитать его все-таки не проблема, он просто такой более «конкретный» без архитектурного рассусоливания. Если посмотреть канонические примеры этих алгоритмов в wikipedia они примерно так же выглядят.
                                                                                                        • +1
                                                                                                          Вы простите конечно, у вас замечательный код, но вот только «из пушки по воробьям».
                                                                                                          Объясню почему: у вас грамматика из четырех элементов, без возвратов. Даже для людей которые на помнят таблицу конечного автомата на память для мат. операций, все равно ее записать — 10-15 минут потратить + 3 минуты в коде массив набить. Далее простая машина состояний которая переходит в нужную ячейку в зависимости от входного символа — линейно. Далее обход листьев с созданием ОПЗ — линейно. Ну а потом уже стек машина для вычисления результата — опять же зависимость только от длины цепочки.
                                                                                                          А вы на пару с программистом чуть ли не целый парсер пишите :(
                                                                                                          • 0
                                                                                                            > у вас грамматика из четырех элементов, без возвратов.
                                                                                                            с возвратом конечно, там есть peek это на самом деле то же самое, что вытащить элемент и его вернуть потом.

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

                                                                                                            Я в статье перечислил правильные способы это сделать, но сам не сделал т.к. для меня это означало бы сначала почитать теорию.
                                                                                                            • +1
                                                                                                              Нет. В данной грамматике нет возвратов. При очередном входном символе у вас не может создаться ситуация, что вам нужно вернуться на N шагов назад и перестроить дерево. Парсинг входной последовательности состоящей из скобочек, бинарных операций и констант — линейно однопроходной по определению.
                                                                                                              • 0
                                                                                                                Вообщем давайте терминологию уточним, я в русской не силен. Если под парсером с возвратом вы имеете только LL(*), т.е. парсеры с бесконечным возвратом, то вы правы, если все которые вообще иногда выполняют lookahead(он же возврат), то приведенный это LL(1) т.е. с возможностью вернуть не больше 1 токена.
                                                                                                            • +3
                                                                                                              p.s. стандарный комментарий для всех кто написал умную вещь: приходите к нам в гости :)
                                                                                                      • +2
                                                                                                        лучше через абстрактное синтаксическое дерево делать, мне кажется, и код проще и быстро работает.
                                                                                                        • +3
                                                                                                          Лучше вообще без дерева, использовать алгоритм рекурсивного спуска.
                                                                                                          • 0
                                                                                                            кажется с точки зрения реализации нет особой разницы строить дерево или считать сразу. Дерево лучше тем что если будет поддержка переменных это можно один раз разобрать а потом подставлять.
                                                                                                            • +1
                                                                                                              При спуске естественным образом строится полиз. Переменные поддерживаются на раз-два.
                                                                                                          • 0
                                                                                                            ну по сути с АСД так и получается, парсящие процедуры взаимно вызываются и возвращают узел дерева, но правда при генерации кода например нужно будет сделать обход этих узлов, чтобы сгенерировать код.
                                                                                                        • 0
                                                                                                          Какие смелые у вас манагеры, через пару лет отсутствия практики использовать std::auto_ptr не боясь случайно покрашить где-нибудь память)
                                                                                                          А к списку литературы я-бы еще посоветовал книгу Дракона прибавить — тоже стоящая вещь для алгоритмистов.
                                                                                                          • +3
                                                                                                            А у меня есть целая коллекция задач с собеседований в Яндекс. Ребята, вы классные!
                                                                                                            • +1
                                                                                                              Поделитесь же :)
                                                                                                              • 0
                                                                                                                Насколько я понимаю, я не могу этого сделать. Замечу лишь, что в коллекции есть действительно интересные задачи на алгоритмы и машинное обучение
                                                                                                                • +6
                                                                                                                  «У меня для вас посылка, только я вам её не отдам.»
                                                                                                            • +1
                                                                                                              По поводу примера задания: извиняюсь за тавтологию, но лично мне, как визуалу, не хватает примера, наглядного. Надеюсь, на собеседованиях он (пример) есть, иначе визуал лишается возможности использовать одну из своих сильных сторон — визуальное восприятие.
                                                                                                              • 0
                                                                                                                Кстати, первое, что подумал, прочитав задачу: Взять пример, нарисовать по нему дерево на листе бумаги, а потом уже думать над алгоритмом.
                                                                                                                • +6
                                                                                                                  Если Яндексу интересны самостоятельные разработчики, то, думаю, он вполне может ожидать, что кандидат сам сочинит для себя необходимое окружение.
                                                                                                                  А то кому-то картинки показать, кому-то — песенку спеть, кого-то тоннами тестовых примеров завалить…

                                                                                                                  А в статье бы, да, картинки не помешали, соглашусь.
                                                                                                                  • +3
                                                                                                                    Вы не поняли, но ладно.
                                                                                                                  • +1
                                                                                                                    На самом деле визуальное изображение это некий способ решения, т.е. промежуточный этап который где-то есть а где-то нет. Если вам поможет, то «код менеджера» рисует текстовое представление синтаксического дерева, просто скомпилируйте и запустите.
                                                                                                                  • +1
                                                                                                                    Я не очень хорошо знаю си, может проглядел это решение в коде, но не проще ли использовать алгоритм выделения отдельных действий, когда каждому действию выделяется приоритет. Мы перемещаемся по строке и отслеживаем 3 действия, идущих подряд.
                                                                                                                    В случае, если у второго действия приоритет больше или равен третьему и первому- заменяем эту пару на число.
                                                                                                                    Так преобразуем строку пока не получится конечное число.
                                                                                                                    Со скобками решается тем, что первая скобка и закрывающая(если стоит третьей) имеют нулевой приоритет, закрывающая с действием(когда стоит в середине) -тоже нулевой
                                                                                                                    И когда в скобках остается конечное число -они убираются.
                                                                                                                    • +2
                                                                                                                      Скорее всего, то, что Вы предлагаете, будет работать за квадратичную сложность