« Поставить закладку » « Сделать стартовой »

« Форумы » « Блоги » « Статьи » « Новости » « Файлы » « Realcoding IRC » « Site map » « Поиск »


Главная Главная
Анонсы Анонсы
Форумы Форумы
Каталог Каталог
Поиск Поиск
Опросы Опросы
Книжный магазин Книжный магазин
Реклама на сайте
Публикации Публикации
Партнеры Партнеры
Карта Карта сайта
Рассылки Рассылки
RSS экспорт
Настройки Настройки
О нас пишут О нас пишут
Контакты Контакты
Гостевая книга Гостевая книга


ПнВтСрЧтПтСбВс
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
    Популярное
Система привилегий и безопасность в MySQL

PGP - лучший криптографический пакет

Код Web-службы на основе ATL Server: ATLServerWebService.h

Функция AccessResource

ГЛАВА 2 Создание и отображение вашего первого XML-документа

Справочник по Perl

Как получить путь псевдонима и таблицы 2

Как создать простой текст любой сложности: взаимодействие Microsoft Office 2007 + Mind Маnager

Создание трехмерных диаграмм

Глава 20. Библиотека MFC и базы данных




    Архив файлов



    Сообщества

    Документация

    Кто на сайте
Вы не зарегистрированы.
Имя:

Пароль:

Запомнить

Регистрация позволит Вам пользоваться дополнительными сервисами.
Сейчас на сайте:
Гостей: 61
Пользователей: 0

Статьи:: Интернет технологии :: Perl :: Изменение поведения хэша с использованием функции tie



отправить ссылку другу версия для печати  Обсудить на форуме

Изменение поведения хэша с использованием функции tie



Дэйв Гросс, перевод Alex Ten. PERL на providerZ.ru

Введение

Исходя из моего опыта, можно сказать, что хэш – самая полезная встроенная структура данных в Perl. Хэш полезен во многих случаях, начиная со справочных таблиц и заканчивая сложными структурами данных. И, конечно же, большинство объектов Perl имеют приведённый к ним хэш, как базовую структуру реализации самого объекта.

Тот факт, что хэши имеют столько широкое распространение, говорит о том, что Ларри (Larry Wall) и другие разработчики языка Perl не ошибались, когда разрабатывали эту структуру данных – хэши просты в использовании, интуитивно понятны и эффективны. Но сталкивались ли вы с ситуацией, когда бы вам хотелось изменить стиль и поведение работы с хэшем? Возможно, вы бы хотели, чтобы все хэши в данном модуле имели только строго зафиксированный набор ключей. Сталкиваясь с таким требованием, чрезвычайно соблазнительно полностью уйти от использования хэшей, в пользу объектов. Но обратная сторона такого решения такова, что при этом вы теряете классический стиль использования хэшей, простой и очень удобный. Однако использование связанных (tied) переменных позволяет создавать объекты и использовать их как хэши.

Связанные объекты

Связанные объекты, по моему мнению, являются одними из самых недооценённых возможностей Perl. Ознакомится с ними детально (вместе с отличными примерами) вы можете в perldoc perltie, расширенные варианты примеров находятся в главе «Связанные переменные (Tied variables)» книги Programming Perl. Несмотря на существование такой великолепной литературы, большинство людей почему-то уверены, что связывание (tieing) используется только при привязке хэша к файлу базы DBM. На самом деле, любая структура данных Perl может быть привязана к чему угодно. Это обычный случай создания объекта, который включает в себя некоторые заранее определённые методы. Если вы хотите создать связанный объект, который имитирует стандартное поведение объекта Perl, так это проще простого, так как стандартный дистрибутив Perl содержит модули, в которых определены объекты, копирующие поведение стандартных типов данных. Например, класс Tie::StdHash (в модуле Tie::Hash), который имитирует поведение обычного хэша. Для изменения поведения в целом, мы должны всего лишь перекрыть действие интересующих нас методов Tie::StdHash своими.

Использование связанных объектов

В своих программах, для связывания объектов вы можете пользоваться функцией tie. Функции tie необходимо два обязательных параметра: переменная, которую вы привязываете(tie) и имя класса, к которому она привязывается(tieing). Далее могут следовать сколько угодно необязательных параметров. Например, если мы написали хэш с фиксированным количеством ключей, о котором говорили ранее, мы можем использовать данный класс следующим образом:

  use Tie::Hash::FixedKey;

  my %person;

  my @keys = qw(forename surname date_of_birth gender);

  tie %person, 'Tie::Hash::FixedKey', @keys;

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

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

my $obj = tied(%person);

вернёт нам объект Tie::Hash::FixedKeys, который привязан к нашему хэшу %person. Иногда это используется для расширения стандартной функциональности хэшей. В примере с фиксированным количеством ключей, мы, возможно, захотим, чтобы пользователь смог добавить или удалить какие-то ключи. Сделать это стандартными способами возможности нет, поэтому придётся писать новые методы, допустим – add_keys и del_keys, которые могут быть вызваны следующим образом:

tied(%person)->add_keys('weight', 'height');

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

untie %person;

возвращает %person в состояние обычного хэша. Для привязки объекта к хэшу, вашему объекту необходимо задать следующий набор методов. Обратите внимание, что все названия стоят в верхнем регистре. Это обычное явления для имён функций, который Perl будет вызывать для вас.

TIEHASH

  • Это функция-конструктор. Она вызывается, когда пользователь вызывает функцию tie. Передаёт имя класса и список переменных, которые были переданы для tie. Должна возвращать ссылку на новый связанный объект.

    FETCH

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

    STORE

  • Данный метод вызывается, когда пользователь пытается сохранить значение ключа в связанном хэше. Метод передаёт ссылку на объект вместе с парой ключ-значение.

    DELETE

  • Этот метод вызывается, когда пользователь вызывает функцию delete для удаления одной из пар ключ-значение в связанном хэше. Метод передаёт ссылку на связанный объект и ключ, который пользователь удаляет. Возвращаемое значение получается путём вызова «настоящей» функции delete. Для имитации «настоящей» функции delete, ключ должен быть уже существующим.

    CLEAR

  • Данный метод вызывается, когда пользователь очищает весь хэш (как правило, путём присвоения хэшу значения пустого списка). Передаёт ссылку на связанный объект.

    EXISTS

  • Этот метод вызывается, когда пользователь запрашивает подтверждение на существование данного ключа. Передаёт ссылку на связанный объект и ключ. Возвращает значение ключа, если он найден, в противном случае возвращает false.

    FIRSTKEY

  • Данный метод вызывается, когда один из итераторов хэша (each или keys) вызывается в первый раз (в цикле). Передаёт ссылку на связанный объект и должен возвращать первый элемент хэша.

    NEXTKEY

  • Этот метод вызывается, когда вызывается один из итераторов. Передаёт ссылку на связанный объект и имя ключа, который был обработан перед ним. Должен возвращать имя следующего ключа или undef, если ключей больше не существует.

    UNTIE

  • Данный метод вызывается, при вызове функции untie. Передаёт ссылку на связанный объект.

    DESTROY

  • Этот метод вызывается, когда связанная переменная выходит из области видимости. Передаёт ссылку на связанный объект.

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

    Первый пример: Tie::Hash::FixedKeys

    Давайте посмотрим на реализацию модуля Tie::Hash::FixedKeys. Данный модуль доступен на CPAN ( http://cpan.org ), если вы хотите ознакомиться с ним более подробно.

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

        use Tie::Hash;
    
        my %hash;
    
        tie %hash, 'Tie::StdHash';
    

    Отлично. Но если честно, то многого мы пока не добились. Хэш %hash теперь связанный объект, но мы пока ничего не изменили в его функциональности. Tie::StdHash работает гораздо лучше в качестве базового класса, от которого объекты наследуют своё поведение. Например, начало класса Tie::Hash::FixedKeys выглядит так:

        package Tie::Hash::FixedKeys;
    
        use strict;
    
        use Tie::Hash;
    
        use Carp;
    
        use vars qw(@ISA);
    
        @ISA = qw(Tie::StdHash);
    

    Это стандартное начало для объектов Perl, но заметьте, что мы загрузили модуль Tie::Hash (используя use Tie::Hash) и «сказали» нашему модулю наследовать поведение от Tie::StdHash, добавив в его @ISA.

    Если бы мы остановились на достигнутом, то наш Tie::Hash::FixedKeys имел бы одинаковое поведение с обычным хэшем Perl. Это является следствием того, что каждый раз, когда Perl пытается найти стандартные методы tie (такие как FETCH или STORE) в нашем модуле, он терпит неудачу и вызывает методы из родительского класса, т.е. из Tie::StdHash.

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

        sub TIEHASH {
    
          my $class = shift;
    
          my %hash;
    
          @hash{@_} = (undef) x @_;
    
          bless %hash, $class;
    
        }
    

    Функция TIEHASH передаёт имя класса в качестве первого параметра, мы внесём его в переменную $class. Остальными параметрами, переданными в @_, можно располагать как угодно, в данном случае мы использовали их как ключи в хэше. Таким образом, мы взяли список ключей и (используя срез хэша) инициализировали хэш, поставив значением каждого ключа undef. В заключении, мы взяли ссылку на хэш и привели (bless) его к заданному классу, после чего возвратили ссылку.

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

    Итак, сейчас мы имеем хэш, со списком фиксированных ключей и этот хэш инициализирован (все значения – undef). Но пока это не спасёт нас от добавления новых ключей, для этого необходимо переписать метод STORE.

        sub STORE {
    
          my ($self, $key, $val) = @_;
    
          unless (exists $self->{$key}) {
    
            croak "invalid key [$key] in hashn";
    
            return;
    
          }
    
          $self->{$key} = $val;
    
        }
    

    Три следующих параметра передаются методу STORE: ссылка на связанный объект и пара ключ-значение. Нам необходимо предотвратить добавление новых ключей в базовый хэш, этого мы добьёмся проверкой каждого добавляемого значения ключа путём сверки его со списком допустимых (уже существующих) ключей. Отметьте, что наш базовый объект является хэшем, поэтому мы можем проверить существование ключа простым вызовом метода exists. Если ключ не существует, то мы напечатаем ошибку и вернём неизменённый хэш.

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

        sub DELETE {
    
          my ($self, $key) = @_;
    
          return unless exists $self->{$key};
    
          my $ret = $self->{$key};
    
          $self->{$key} = undef;
    
          return $ret;
    
        }
    

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

       %hash = ();
    

    Исполнение данной строки приведёт к вызову метода CLEAR. Стандартное поведение этого метода состоит в удалении всех данных из хэша. Необходимо заменить такое поведение на заменяющее все значения ключей на undef, без удаления ключей.

        sub CLEAR {
    
          my $self = shift;
    
          $self->{$_} = undef foreach keys %$self;
    
        }
    

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

    Другой пример: Tie::Hash::Regex

    Давайте взглянем на другой пример. Этот модуль был написан в ходе обсуждения на сайте Perlmonks ( http://www.perlmonks.org/ ). Кто-то спросил, возможно, ли проверять соответствие ключей приблизительно. Я предложил, что хэши, которые проверяют совпадения ключей через регулярные выражения могут решить эту проблему и тут же написал черновой вариант такого модуля. Я благодарен Джеффу Пиньяну, который помог мне улучшить работу модуля.

    Для изменения поведения нам необходимо переписать следующие методы: FETCH, EXISTS и DELETE. Вот реализация метода FETCH:

      sub FETCH {
    
        my $self = shift;
    
        my $key = shift;	
    
        my $is_re = (ref $key eq 'Regexp');
    
        return $self->{$key} if !$is_re && exists $self->{$key};
    
        $key = qr/$key/ unless $is_re;
    
        /$key/ and return $self->{$_} for keys %$self;
    
        return;
    
      }
    

    Зная то, что мы уже изучили по связанным объектам, чрезвычайно просто разобрать предыдущий пример. Сначала мы получили ссылку на связанный объект (это ссылка на хэш) и требуемый ключ. Далее мы проверяем, является ли ключ ссылкой на прекомпилированное регулярное выражение (которые было откомпилировано с помощью qr//). Если ключ не является регулярным выражением, тогда мы проверяем, существует ли такой ключ в хэше. Если существует – мы вернём значение ключа. Если ключ не найден, в таком случае мы предположим, что это регулярное выражение, которое надо искать. Теперь мы компилируем регулярное выражение, если оно ещё не было откомпилировано. Потом мы проверяем каждый ключ на совпадение с заданным регулярным выражением, в случае совпадения мы возвращаем значение соответствующего ключа. Если совпадения не найдены, то возвращается 0.

    Здесь вы, должно быть поняли, что было бы совсем неплохо возвращать все совпадения, вместо одного, как если бы мы вызвали метод FETCH в скалярном контексте для каждого подходящего ключа (т.е. получали бы назад список значений). Это отличная идея, но в текущей версии Perl строка $hash{$key} всегда вызывает FETCH в скалярном контексте (а строка @hash{@keys} вызывает FETCH единожды для каждого элемента @keys), поэтому ничего не получится. Чтобы обойти этот подводный риф можно использовать достаточно странную, на первый взгляд, строку – @vals = tied(%hash) - FETCH($pattern) и это сработает с данным модулем из CPAN.

    Метод EXISTS использует похожую обработку, но в данном случае необходимо возвращать 1, вместо значения ключа.

      sub EXISTS {
    
        my $self = shift;
    
        my $key = shift;
    
        my $is_re = (ref $key eq 'Regexp');
    
        return 1 if !$is_re && exists $self->{$key};
    
        $key = qr/$key/ unless $is_re;
    
        /$key/ && return 1 for keys %$key;
    
        return;
    
      }
    

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

      sub DELETE {
    
        my $self = shift;
    
        my $key = shift;
    
        my $is_re = (ref $key eq 'Regexp');
    
        return delete $self->{$key} if !$is_re && exists $self->{$key};
    
        $key = qr/$key/ unless $is_re;
    
         for (keys %$self) {
    
          if (/$key/) {
    
            delete $self->{$_};
    
          }
    
        }
    
      }
    

    Существует ещё один похожий модуль, который доступен на CPAN – Tie::RegexpHash, написанный Робертом Ротенбергом (Robert Rothenberg). Несмотря на то, что модули предназначены для схожих целей, модуль Tie::RegexpHash делает обратную вещь: при сохранении значения в хэше ключ становится регулярным выражением и когда вы смотрите значение ключа, вы получите обратно значение ключа, которое соответствует первому подходящему регулярному выражению-ключу. Интересный факт, но Tie::RegexpHash не основывается на Tie::StdHash и как следствие – имеет значительно больше кода.

    Ещё одно добавление на CPAN – Tie::Hash::Approx, написанное Бриаком Пильпре (Briac Pilpre). Модуль решает те же проблемы, что и наш модуль, однако вместо использования регулярных выражений опирается на модуль String::Approx Яркко Хиетаниеми (Jarkko Hietaniemi).

    Вместо эпилога: Tie::Hash::Cannabinol

    В качестве последнего примера рассмотрим «пьяный» модуль. Этот хэш будет забывать практически всё, что мы будем сдавать ему на хранение. Функции exists лучше тоже не доверять.

        package Tie::Hash::Cannabinol;
    
        use strict;
    
        use vars qw(@ISA);
    
        use Tie::Hash;
    
        $VERSION = '0.01';
    
        @ISA = qw(Tie::StdHash);
    
    
    
    
        sub STORE {
    
          my ($self, $key, $val) = @_;
    
          return if rand > .75;
    
          $self->{$key} = $val;
    
        }
    
    
    
        sub FETCH {
    
          my ($self, $key) = @_;
    
          return if rand > .75;
    
          return $self->{rand keys %$self};
    
        }
    
    
    
        sub EXISTS {
    
          return rand > .5;
    
        }
    

    Как вы видите, не так уж сложно внести радикальные изменения в поведение хэшей в Perl используя функцию tie и класс Tie::StdHash, в качестве базового. Как я уже сказал в начале статьи – это позволяет с легкостью создавать новые «объекты» без полного перехода к объектной модели программирования.

    Причём изменять можно не только поведение хэшей. В стандартном дистрибутиве Perl идут такие модули как Tie::StdArray, Tie::StdHandle и Tie::StdScalar.

    Желаю вам удачи с ними.




  • Рубрика: Perl




    HTML 5: пять вещей вызывающих особый интер....

    Html

    HTML 5 — это грядущее обновление гипертекстового языка разметки, основного способа создания контента для размещения его во всемирной паутине. Разработка HTML остановилась в 1999 году, на версии HTML 4.01 и с тех пор web-содержимое изменилось так, что текущие спецификации HTML перестали соответствовать сегодняшним требованиям. HTML 5 нацелен на то, чтобы увеличить функциональную совместимость HTML и соответствовать растущим требованиям разнообразного и смешанного web-контента. HTML 5 так же нацелен на устранение недостатков четвертой версии. В этой статье мы взглянем на 5 новых интересных вещей в HTML 5.


    Подробнее... | Рубрика: Html | Добавлено: 22.12.2008

    asp.net: ListView с разных сторон.

    .NET компоненты

    Элемент управления ListView был представлен в .Net Framework 3.5 как замена устаревшему GridView. Новый элемент имеет более расширенный функционал, чем его предшественник, но в тоже время лишен некоторых внутренних механизмов, что впрочем целиком следствие из расширенной универсальности ListView. Среди отличий ListView и GridView можно назвать и гибкую настройку разметки, что позволяет выводить данные не только в табличном виде, но и вообще в любом каком пожелает программист. Благодаря шаблонам ItemTemplate, EditItemTemplate, InsertItemTeplate можно настроить внешний вид при любом из состояний ListView: редактировании или выборе элемента.


    Подробнее... | Рубрика: .NET компоненты | Добавлено: 22.12.2008

    Создание кросс-таб отчета в Stimulsoft Rep....

    .NET компоненты

    Компания Стимулсофт предоставляет для разработчиков мощный набор инструментов для создания отчетов для Microsoft Visual Studio .Net 2005 и 2008; эти инструменты доступны как для Windows Forms, так и для Web Forms. Это генератор отчетов Stimulsoft Reports.Net. Генератор отчетов Stimulsoft Reports.Net имеет ряд особенностей: простая работа с дизайнером отчетов, полная поддержка экспорта в PDF, Word, Excel и многие другие форматы. Crystal Report и Microsoft Reporting Service – очень хорошие программные продукты для повседневной работы, но, если Вам необходимо создать отчеты с поддержкой кросс-табов, drill down, Ajax, штрих-кодов и возможностью подключения одновременно более одного источника данных, то Stimulsoft Reports.Net поможет Вам сэкономить массу времени. Также, данный генератор отчетов позволяет пользователям создавать свои собственные отчеты любой сложности. И все эти особенности делают Stimulsoft Reports.Net хорошим выбором в сфере программных продуктов для Business Intelligence.


    Подробнее... | Рубрика: .NET компоненты | Добавлено: 22.12.2008

    Остальные статьи:

    VivaMP - инструмент для OpenMP
    Создаем контекстно-зависимое WPF-приложени...
    Windows Vista SP2: что внутри и что важно?
    Вышел MySQL 5.1.30, первый стабильный рели...
    Тестирование параллельных программ
    Архитектура AMD64 (EM64T)
    Платформа 2009. Определяя будущее
    Windows Vista Bridge Sample Library - упра...
    Оптимизация 64-битных программ
    Подгрузка через AJAX HTML-кода, содержащег...
    Обзор нового релиза самой мощной Ajax библ...
    Firebug 1.3 и 1.4 alpha — что нового и инт...
    Релиз Microsoft Silverlight 2.0. Что новог...
    XML документация в C#
    Курсоры в MySQL 5
    Microsoft опубликовала подробности о сесси...
    Microsoft делится подробностями о том, что...
    Тестируем новый javascript от нового брауз...
    MySQL Query Cache
    Использование провайдеров компиляции в As...


    Цитата дня (все,добавить):

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

    работа на дому


        Рубрикатор

    Программирование

    C/С++
    Обучение
    Windows API
    XAML
    Моделирование
    Паттерны
    Visual Basic 7 .NET
    WxWidgets
    Функции WinApi
    Функции С++
    Разработка под Mac OS
    Eiffel
    Visual Studio 2008
    UI дизайн
    Алгоритмы
    Конкурсные статьи
    Turbo Pascal
    Visual Studio
    CASE-средства
    Visual Studio 2005
    Без VCL
    Delphi
    Тех. документация
    Тестирование
    Software Testing
    ООП
    TCP/IP
    Google Android
    Windows Installer
    .NET Framework
    Драйвера
    C# C Sharp
    Справка
    Проектирование
    Информ. системы
    Visual Basic
    Assembler
    Оптимизация кода
    Gtk+
    Компоненты
    Реинжиниринг
    Управление проектами
    Extreeme programming
    Lotus Notes
    Алгебраическое проектирование


    Интернет технологии

    PHP
    Perl
    ASP
    WAP
    Cookies
    SSI
    CGI
    Web Servers
    VB Script
    DNS
    CSS
    XML
    Html
    Java Script
    Java2ME
    Firewall
    Flash
    .htaccess
    Apache
    VRML
    Протоколы
    Поисковые системы
    Технология JAVA
    Учебник по PHP
    Учебник по JavaScript
    Учебник по XML
    Java Q&A
    AJAX
    DHTML
    XHTML
    Dreamweaver
    Web 2.0
    Python
    Вебмастеру
    Cisco
    Ruby on Rails
    Silverlight

    Базы данных

    Access
    InterBase
    MySQL
    Oracle
    ADO .NET
    Основы SQL
    Учебник по Access 2002
    MS
    Microsoft FoxPro
    Доступ к данным
    XML в MS SQL Server 2000
    ODBC и MyODBC
    Обучение
    Caché
    DB2
    PostgresSQL
    Sybase
    Теория
    Хранилища данных
    Безопасность
    Реляционные данные
    MySQL и mSQL

    Остальное:

    Разное
    Обзоры книг
    Безопасность
    Графика и дизайн
    Юмор
    Linux
    Фракталы
    Microsoft Axapta
    Многоядерность
    Сети
    Microsoft Office
    Работа
    MS-DOS
    Криптография
    Графика и игроделание
    Новости SDK
    Системы защиты
    Учебник по AutoCad
    CVS
    Windows XP
    Windows Server 2003
    Windows Vista
    Windows 7
    Мероприятия