Тестовые задания для программистов. Издание исправленное и дополненное

Я просто оставлю это здесь.

[user@domain ~]# cc -Wall -Werror -pedantic Binary.c
BinaryPrint.c:6:5: error: C++ style comments are not allowed in ISO C90
BinaryPrint.c:6:5: error: (this will be reported only once per input file)
cc1: warnings being treated as errors
BinaryPrint.c: In function 'BinaryPrint':
BinaryPrint.c:13: warning: ISO C90 forbids mixed declarations and code

diff -r afee20b80dff BinaryPrint.c
--- a/BinaryPrint.c     Tue Feb 22 09:11:12 2011 +0300
+++ b/BinaryPrint.c     Tue Nov 15 18:51:10 2011 +0300
@@ -3,31 +3,31 @@
 
 void BinaryPrint( long n )
 {
-    // Частный случай - n равно нулю
+    unsigned long mask = 1 << (sizeof(n) * 8 - 1);
+
+    /* Пропускаем бесполезные нули слева */
+    int skipZero = 1;
+
+    /* Частный случай - n равно нулю */
     if (n == 0)
     {
         printf("0\n");
         return;
     }
 
-    unsigned long mask = 1 << (sizeof(n) * 8 - 1);
-
-    // Пропускаем бесполезные нули слева
-    int skipZero = 1;
-
     while (mask)
     {
-        // Определим текущий бит
+        /* Определим текущий бит */
         unsigned char c = (n & mask) ? 1 : 0;
 
-        // Отключим пропуск нулей в дальнейшем
+        /* Отключим пропуск нулей в дальнейшем */
         if (skipZero && c)
             skipZero = 0;
 
         if (!skipZero)
             printf("%d", c);
 
-        // Сдвинем маску на один бит вправо
+        /* Сдвинем маску на один бит вправо */
         mask >>= 1;
     }
     printf("\n");
[user@domain ~]# cc -Wall -Werror -pedantic RemoveDups.c
RemoveDups.c:8:5: error: C++ style comments are not allowed in ISO C90
RemoveDups.c:8:5: error: (this will be reported only once per input file)
cc1: warnings being treated as errors
RemoveDups.c: In function 'RemoveDups':
RemoveDups.c:16: warning: value computed is not used
RemoveDups.c:22: warning: value computed is not used
RemoveDups.c:24: warning: value computed is not used
diff -r afee20b80dff RemoveDups.c
--- a/RemoveDups.c      Tue Feb 22 09:11:12 2011 +0300
+++ b/RemoveDups.c      Tue Nov 15 18:56:00 2011 +0300
@@ -5,25 +5,25 @@
     const char * src = pStr;
     char * dst = pStr;
 
-    // Пока не дойдем до конца строки
+    /* Пока не дойдем до конца строки */
     while (*src)
     {
-        // Записываем в pStr текущий символ, если указатели разные
+        /* Записываем в pStr текущий символ, если указатели разные */
         if (dst != src)
             *dst = *src;
 
-        // Сдвигаем dst вправо
-        *dst++;
+        /* Сдвигаем dst вправо */
+        dst++;
 
-        // Пока не конец строки и следующий символ такой же
+        /* Пока не конец строки и следующий символ такой же */
         while ((*src + 1) && *(src + 1) == *src)
         {
-            // Сдвигаем указатель src вправо
-            *src++;
+            /* Сдвигаем указатель src вправо */
+            src++;
         }
-        *src++;
+        src++;
     }
-    // Запишем ноль в конец строки
+    /* Запишем ноль в конец строки */
     *dst = 0;
 }

Читайте также: «Вопросы программисту на собеседовании с ответами».

Тестовые задания для программиста

Так случилось, что в марте 2011 года я потерял работу в связи с закрытием фирмы. Взгрустнулось. Недолго думая, накатал резюме и выложил его на разные сайты: hh.ru, habrahabr.ru, job.ru и dtf.ru. В течение месяца ответило довольно много работодателей. Некоторые высылали тестовые задания для проверки. Делал я их бесплатно, в свое личное время, так что имею полное право выложить решение.

Итак, погнали: решение тестового задания от фирмы «Saber Interactive».

Задача

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

Решение

 
#include <stdio.h>
#include <limits.h>
 
void BinaryPrint( long n )
{
    // Частный случай - n равно нулю
    if (n == 0)
    {
        printf("0\n");
        return;
    }
 
    unsigned long mask = 1 << (sizeof(n) * 8 - 1);
 
    // Пропускаем бесполезные нули слева
    int skipZero = 1;
 
    while (mask)
    {
        // Определим текущий бит
        unsigned char c = (n & mask) ? 1 : 0;
 
        // Отключим пропуск нулей в дальнейшем
        if (skipZero && c)
            skipZero = 0;
 
        if (!skipZero)
            printf("%d", c);
 
        // Сдвинем маску на один бит вправо
        mask >>= 1;
    }
    printf("\n");
}
 
int main()
{
    BinaryPrint(0);
    BinaryPrint(1);
    BinaryPrint(2);
    BinaryPrint(-2);
    BinaryPrint(-1);
    BinaryPrint(LONG_MAX);
    BinaryPrint(LONG_MIN);
    return 0;
}

Задача

Напишите функцию, удаляющую последовательно дублирующиеся символы в строке: («AAA BBB ААА» -> «A B А»)
void RemoveDups (char *pStr);

Решение

#include <stdio.h>
 
void RemoveDups( char * pStr )
{
    const char * src = pStr;
    char * dst = pStr;
 
    // Пока не дойдем до конца строки
    while (*src)
    {
        // Записываем в pStr текущий символ, если указатели разные
        if (dst != src)
            *dst = *src;
 
        // Сдвигаем dst вправо
        *dst++;
 
        // Пока не конец строки и следующий символ такой же
        while ((*src + 1) && *(src + 1) == *src)
        {
            // Сдвигаем указатель src вправо
            *src++;
        }
        *src++;
    }
    // Запишем ноль в конец строки
    *dst = 0;
}
 
int main()
{
    char s[] = "AAA BBB AAA";
    RemoveDups(s);
    printf("%s\n", s);
    return 0;
}

Задача

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

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

Разработайте структуры хранения данных, наилучшим образом подходящие для реализации этой функциональности. Постарайтесь учесть непредвиденные ситуации, которые могут возникнуть, а также организовать доступ к данным наиболее быстрым и эффективным образом. Представьте ответ в виде .h файла. Вы можете использовать классы С++, а также контейнеры STL или их аналоги.

Решение

Исходники программы на C/C++
#ifndef _LIBRARY_H_
#define _LIBRARY_H_
 
/*
 Каждой книге и каждому читателю при регистрации присваивается уникальный 
 числовой идентификатор. Доступ к книгам и читателям происходит только 
 с помощью идентификаторов.
 
 База книг/читателей преставляет собой деревья, где ключи - это идентификатор,
 а значения - книга/читатель. 
 
 Для каждого поля, по которым мы можем осуществить поиск, создается
 индексная таблица. По образу и подобию индексных таблиц в mysql.
 
 Класс Index представляет собой обертку над std::multimap. 
 std::multimap выбран потому, что значения ключей могут повторяться.
 один автор -> много книг, одно название -> много книг и т.д.
 
 Все методы поиска возвращают список идентификаторов, поскольку ключевые поля не уникальны.
*/
 
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <assert.h>
 
// Уникальный идентификтор объекта
typedef unsigned long Ident;
// Список идентификаторов
typedef std::vector<Ident> IdentVector;
 
namespace detail
{
    // Шаблонный класс для удобства работы с multimap
    template <class T>
    class Index
    {
    public:
        void append( const T & value, Ident ident )
        {
            m_values.insert(std::make_pair(value, ident));
        }
 
        void remove( Ident ident )
        {
            for (Values::const_iterator i = m_values.begin(); i != m_values.end();)
            {
                if (i->second == ident)
                {
                    i = m_values.erase(i);
                }
                else
                {
                    ++i;
                }
            }
        }
 
        IdentVector get( const T & value ) const
        {
            IdentVector result;
 
            Values::const_iterator i = m_values.find(value);
            while (i != m_values.end() && i->first == value)
            {
                result.push_back(i->second);
                ++i;
            }
 
            return result;
        }
 
    private:
        typedef std::multimap<T, Ident> Values;
        Values m_values;
    };
 
} // namespace detail
 
// Описание книги
struct Book
{
    // Автор
    std::string author;
    // Название
    std::string title;
    // Год выпуска
    size_t year;
    // Количество экземпляров
    size_t count;
 
    Book( const std::string & author, const std::string & title, size_t year, size_t count )
        : author(author), title(title), year(year), count(count)
    {
    }        
};
 
// База данных книг
class BookDatabase
{
public:
    BookDatabase()
        : m_nextIdent(0)
    {
    }        
 
    virtual ~BookDatabase() {}
 
    // Добавление книги в библиотеку (возвращает идентификатор)
    Ident registerBook( const Book & book )
    {
        Ident ident = getNextIdent();
        m_books.insert(std::make_pair(ident, book));
        m_authors.append(book.author, ident);
        m_titles.append(book.title, ident);
        return ident;
    }
 
    // Удаление книги из библиотеки 
    void unregisterBook( Ident ident )
    {
        Books::const_iterator i = m_books.find(ident);
        if (i != m_books.end())
        {
            i = m_books.erase(i);
        }
        m_authors.remove(ident);
        m_titles.remove(ident);
    }
 
    // Получение всех идентификаторов книг с заданным авторов
    IdentVector getBooksByAuthor( const std::string & author ) const
    {
        assert(!author.empty());
        return m_authors.get(author);
    }
 
    // Получение всех идентификаторов книг с заданным названием
    IdentVector getBooksByTitle( const std::string & title ) const
    {
        assert(!title.empty());
        return m_titles.get(title);
    }
 
    // Получение всех идентикаторов книг с заданным автором и названием
    IdentVector getBooksByAuthorAndTitle( const std::string & author, const std::string & title ) const
    {
        assert(!author.empty() && !title.empty());
 
        IdentVector authors = getBooksByAuthor(author);
        IdentVector titles = getBooksByTitle(title);
 
        IdentVector result;        
        std::set_intersection(authors.begin(), authors.end(), titles.begin(), titles.end(), result.begin());
        return result;
    }
 
    // Получение данных книги по идентификатору (возвращает false, если книга не найдена)
    bool getBook( size_t ident, Book & book ) const
    {
        Books::const_iterator i = m_books.find(ident);
        if (i != m_books.end())
        {
            book = i->second;
            return true;
        }
        return false;
    }
 
    // Увеличение кол-ва книг
    void increaseBooksCount( Ident ident )
    {
        Books::iterator i = m_books.find(ident);
        if (i != m_books.end())
        {
            i->second.count++;
        }
    }
 
    // Уменьшение кол-ва книг (возвращает false, если кол-во равно нулю
    bool decreaseBooksCount( Ident ident )
    {
        Books::iterator i = m_books.find(ident);
        if (i != m_books.end())
        {
            if (i->second.count > 0)
            {
                i->second.count--;
                return true;
            }                
        }
        return false;
    }
 
    // Вернуть список всех книг, которые можно взять в библиотеке
    IdentVector getFreeBooks() const
    {
        IdentVector result;
 
        for (Books::const_iterator i = m_books.begin(); i != m_books.end(); ++i)
        {
            if (i->second.count > 0)
            {
                result.push_back(i->first);
            }
        }
 
        return result;
    }
 
private:
    Ident getNextIdent()
    {
        return m_nextIdent++;
    }
 
    // Следующий идентификатор
    Ident m_nextIdent;
 
    // Список книг
    typedef std::map<Ident, Book> Books;
    Books m_books;
 
    // Индексная таблица по авторам
    detail::Index<std::string> m_authors;
    // Индексная таблица по названиям книг
    detail::Index<std::string> m_titles;
};
 
// Читатель
struct Reader
{
    // ФИО
    std::string fullName;
    // Адрес
    std::string address;
    // Телефон
    std::string phone;
 
    Reader( const std::string & fullName, const std::string & address, const std::string & phone )
        : fullName(fullName), address(address), phone(phone)
    {
    }        
};
 
// База читателей
class ReaderDatabase
{
public:
    ReaderDatabase()
        : m_nextIdent(0)
    {
    }
 
    virtual ~ReaderDatabase() {}
 
    Ident registerReader( const Reader & reader )
    {
        Ident ident = getNextIdent();
        m_readers.insert(std::make_pair(ident, reader));
        m_fullNames.append(reader.fullName, ident);
        return ident;
    }
 
    void unregisterReader( Ident ident )
    {
        Readers::const_iterator i = m_readers.find(ident);
        if (i != m_readers.end())
        {
            m_readers.erase(i);
        }
        m_fullNames.remove(ident);
    }
 
    IdentVector getReadersByFullName( const std::string & fullName ) const
    {
        return m_fullNames.get(fullName);
    }
 
    IdentVector getReadersByPartName( const std::string & partName ) const
    {
        IdentVector result;
        for (Readers::const_iterator i = m_readers.begin(); i != m_readers.end(); ++i)
        {
            const Reader & reader = i->second;
            if (reader.fullName.find(partName) != std::string::npos)
            {
                result.push_back(i->first);
            }
        }
        return result;
    }
 
    // Информация о читателе
    bool getReader( Ident ident, Reader & reader ) const
    {
        Readers::const_iterator i = m_readers.find(ident);
        if (i != m_readers.end())
        {
            reader = i->second;
            return true;
        }
        return false;
    }
 
private:
    Ident m_nextIdent;
 
    Ident getNextIdent()
    {
        return m_nextIdent++;
    }    
 
    typedef std::map<Ident, Reader> Readers;
    Readers m_readers;
 
    detail::Index<std::string> m_fullNames;
};
 
// Библиотека
class Library : public ReaderDatabase, public BookDatabase
{
    struct Rent
    {
        Ident book;
        Ident reader;
        size_t days;
    };
 
public:
    // Взять книгу bookIdent читателем readerIdent на days дней (возвращает false в случае неудачи)
    bool popBook( Ident bookIdent, Ident readerIdent, size_t days )
    {
        if (decreaseBooksCount(bookIdent))
        {
            Rent rent;
            rent.book = bookIdent;
            rent.reader = readerIdent;
            rent.days = days;
 
            m_rents.push_back(rent);
 
            return true;
        }
        return false;
    }
 
    // Вернуть книгу bookIdent читателем readerIdent
    void pushBook( Ident bookIdent, Ident readerIdent )
    {
        for (Rents::const_iterator i = m_rents.begin(); i != m_rents.end(); ++i)
        {
            if (i->book == bookIdent && i->reader == readerIdent)
            {
                m_rents.erase(i);
                increaseBooksCount(bookIdent);
                break;
            }
        }
    }
 
    // Через сколько дней будет возвращена книга bookIdent читателем readerIdent
    size_t getRentaDays( Ident bookIdent, Ident readerIdent ) const
    {
        for (Rents::const_iterator i = m_rents.begin(); i != m_rents.end(); ++i)
        {
            if (i->book == bookIdent && i->reader == readerIdent)
            {
                return i->days;
            }
        }
    }
 
    // Возвращает список читателей, взявший данную книгу
    IdentVector getReadersByBook( Ident bookIdent ) const
    {
        IdentVector result;
        for (Rents::const_iterator i = m_rents.begin(); i != m_rents.end(); ++i)
        {
            if (i->book == bookIdent)
            {
                result.push_back(i->reader);
            }
        }
        return result;
    }
 
private:
    typedef std::vector<Rent> Rents;
    Rents m_rents;
 
};
 
#endif // _LIBRARY_H_


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

Читайте также: «Вопросы программисту на собеседовании с ответами»

Крутая система логгирования

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

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

logger::info() << "Тестовое сообщение номер " << 1;

Самая главная проблема была в том, чтобы воткнуть ‘\n’ в конце сообщения. Для этого в info() был примерно следующий код:

InfoStream info()
{
    static InfoStream stream;
    // std::endl пишет в ostream '\n' и делает flush()
    stream << std::endl;
    return stream;
}

Последняя строка завершалась только при закрытии файла (в деструкторе класса InfoStream). Первый flush игнорировался. Это все кажется простым, если мы пишем лог в обычный текстовый файл.

Когда лог стал в формате html, начались пляски с бубном.

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

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

Вот как это было реализовано:

class logger
{
public:
    class internal_stream
    {
    public:
        template <class T>
        internal_stream & operator <<(const T & value)
        {
            logger << value;
            return *this;
        }
 
        ~internal_stream() {}
 
    private:
        // Может использоваться только классом logger
        internal_stream(logger & stream) 
            : mStream(stream)
        {};
 
        logger & mStream;
    };
 
    internal_stream info()
    {
        return internal_stream(*this);
    }
};

Как это все работает:

  • в конструкторе logger — подготавливаем файл для записи, в текстовом ничего не делаем, в html — записываем заголовок
  • в деструкторе logger — закрываем файл, в html формате закрываем теги body, html и т.д.
  • в конструкторе internal_stream — начало строки, в html формате пишем tr, td и все что нравится
  • в деструкторе internal_stream — окончание строки, в текстовом это просто ‘\n’, в html закрываем теги tr и т.д.

Если сделать logger шаблонным классом, то можно реализовать разные буферы вывода. Начиная от текстового, html и xml и заканчивая сервером для приема и клиента для отправки сообщений по сети.

Блог Евгения Жирнова