вторник, 4 августа 2015 г.

Каждому по способностям, или причуды разработчиков

Однажды, пришел к администратору базы данных сотрудник и сказал:
 - о великий админ, организуй мне, пожалуйста, в нашей базе вот такие вот таблички!
И админ вопросил его:
- А зачем тебе такие таблички?
- Я буду записывать в ключевую таблицу караваны верблюдов и вот в эти красивые таблички различные отчеты по их работе: как торговых, как фото-услуг, как прокатных.
 - Ты не познал науку проектирования баз данных, сотрудник. Почему в этой таблице у тебя сразу две сущности? Ведь в одном караване может быть более одного верблюда? Или забыл ты, что "одна сущность - одна таблица" должны быть?
 - Не забыл я, о великий админ, но связь между сущностями этими - один к одному. А потому смело объединяю я их, во благо моих запросов.
 - Помнишь ли ты, сотрудник, что в прошлый раз втвои верблюды могли объединяться в караваны произвольной длины?
 - Помню, о админ, но это другие верблюды, другие караваны, другие товары и другие работы и я хочу для них одну таблицу!

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

Прошел год

 - о великий админ базы данных, я хочу иметь возможность построить отчет не только по каждому каравану, но и по каждому верблюду!
 - Но ведь говорил  мне ты, что в каждом твоем караване лишь один верблюд? В чем же твоя проблема?
 - Неразумный пользователь не хочет оформлять много маленьких караванов. Он утверждает что хочет вместо этого иметь дело с одним длинным.
 - И?
 - И я хочу, чтобы ты вставил между ключевой таблицей караванов и данными по их деятельности таблицу с верблюдами. И чтобы я вел деятельность по каждому из верблюдов, а не по каравану.
 - Ну я же говорил тебе, о неразумный сотрудник?
 - Но я был так уверен. Кстати, они уже год работали так, поэтому мне нужно, чтобы все данные сохранились в новой структуре.
 - Я выполню и это твое желание, - вздохнул администратор базы данных.
 - А кому сейчас легко? - развел руками сотрудник и ушел.

И призадумался молодой админ:
Ну структуру-то я новую создан, но... как сохранить ему его данные?.. Да еще и раскидать их по новой структуре?

Так админ узнал о новой штуке:

CREATE TABLE  test (a1 integer, a2 varchar(16));
INSERT INTO test(a1, a2) SELECT b1, b2 FROM source_table_name;

Примечание:
http://www.w3schools.com/sql/sql_select_into.asp
http://stackoverflow.com/questions/25969/sql-insert-into-values-select-from



воскресенье, 14 июня 2015 г.

ООП и JavaScript

Последние несколько недель свободного времени мне пришлось потратить на осознание одного простого факта: "Не все объекты одинаково полезны."

Идея была проста: "А давайте сделаем js игру-тамагочу?" Пусть это будет... хомяк?..

И сегодня расскажу вам о том, как си-шарпер пытается познать ООП в малознакомом ему яваскрипте.

Если не художественно, то материалы, которые спасли мою психику находятся здесь: http://javascript.ru/tutorial/object .

Тем не менее, здесь будет еще и краткий конспект и без того краткого мануала.

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

Поскольку ТЗ мое весьма неподробно в систему надо закладывать максимальную гибкость.

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

Поэтому... я открываю гугл и забиваю что-то вроде "классы в js". И сразу узнаю, что я могу создать объект конструкцией вроде:

window.Hamster = {

    status = "good",    health = 50,    age = 0
}

Что, в целом, неплохо.
Я могу добавить методы:

window.Hamster = {

    ... ,
    EatSmth : function(n) { this.health =+ this.health + n; },
    CheckHealth : function() { if (this.health < 50) this.status = "ill"; }
}

И вот тут я думаю, а что бы мне не вызвать функцию проверки здоровья собственно сразу при изменении здоровья?.. И... и тут начинаются проблемы. Потому, что как это не удивительно для меня, старого си-шарпера, код...

this.CheckHealth();

...удивительным образом не работает. 

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

Примечание: зато работает:

EatSmth : function(n) 

    this.health =+ this.health + n; 
    window.Hamster.CheckHealth();
},

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

Что же делать мне, при желании реализовать свой объектный замысел?

Факт первый, чтобы создавать одинаковые структуры нужно создать функцию. Любая функция в js может создать объект, если вызывается через  new.  Полями объекта являются все определенные в функции через this поля.
Чтобы создать тип объекта нам нужна своего рода порождающая функция.

function Hamster() { 
    this.status = "good";    this.health = 50;
    this.EatSmth = function(n) 
    { 
       this.health =+ this.health + n; 
       this.CheckHealth();
    }
    this.CheckHealth = function() {
       if (this.health < 50) this.status = "ill"; 
    }
}

var MyNewHam = new Hamster();
MyNewHam.EatSmth(5);

И, вуаля, я могу иметь сколько угодно хомяков c какой мне угодно сложностью цепочек вызова.

И хотя задача решена... Но это не самое интересное, "что я узнал сегодня". Тем более, что углубляясь в задачу у меня возник вопрос наследования

Факт второй.
В js нет разницы между классом и переменной (как можно было уже заметить на самом первом примере, который оказался реализацией singleton). По крайней мере в явном виде, как я привыкла в C#.

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

function BaseParam() { 
    this.status = "good";    this.value = 50;
}

И унаследовать от него свои параметры, которые уже буду вставлять в хомяка. Например, здоровье. И, собственно, для таких случаев, когда очень хочется вставить в свою архитектуру немножко наследования в js есть прототипирование! У нас нет возможности создавать классы в явном виде... зато мы можем наследоваться от другой переменной. Для этого внутри каждой функции есть свойство prototype, через которое происходит наследование создаваемыми функцией объектами. По умолчанию, все создаваемые объекты наследуются от класса Object.

Вообще говоря, каждый раз когда мы создаем новый объект-структуру: var a = {}  неявно вызывается функция конструктор  var a = new Object();

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

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

function HealthParam(current_health) {
    this.value = current_health;

    this.EatSmth = function(n) 
    { 
       this.value =+ this.value + n; 
       this.CheckHealth();
    }

    this.CheckHealth = function() {
       if (this.value < 50) this.status = "ill"; 
    }
}
HealthParam.prototype = new BaseParam();

function Hamster() { 
    this.Health = new HealthParam(50);
    this.Strength = new StrengthParam(50);
    this.EatSmth = function(n)
    {
        this.Health.EatSmth(n);
        this.Strength,EatSmth(n);
        //все что еще мне нужно по событию кормления хомяка
    }
}

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

В попытке создать хомяка использованы:
1. http://javascript.ru/tutorial/object
2. Хабрахабр: Использование паттернов проектирования в javaScript: Порождающие паттерны

А также для более хитрых и англоязычных читателей:
3. http://shichuan.github.io/javascript-patterns/
4. http://addyosmani.com/resources/essentialjsdesignpatterns/book/

четверг, 30 апреля 2015 г.

CodeStars. Track 05.

Задача
Чтобы твой трек попал в ротацию на online радио для айтишников, реши следующую задачу: Билет на твой первый концерт в Лужниках содержит 6 цифр от 0 до 5. Сколько среди всех билетов «счастливых», т.е. таких, для которых сумма первых 3 цифр равна сумме последних 3 цифр?

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

Наш номер билета можно представить как AB, где А сумма первой половины билета, В - сумма второй половины билета. А и В входят в множество Х. Счастливыми будут билеты AA, где А = В.

Минимальная сумма у размещения: "000" = 0, максимальная у "555" = 15, т.е. всего Х = 16 различных сумм. Каждую сумму можно собрать из заданных чисел различными способами, назовем  эти способы функцией S(x). Тогда наш счастливый билет для суммы    х   можно представить как:

AB => AA => S(x) S(x)

Т.е. счастливых билетов для каждой суммы это количество перестановок функции S(x) по двум позициям = S(x)^2

Осталось найти сколькими же способами можно собрать каждую сумму и просуммировать это безобразие.

Тут есть два способа: перебором и математикой. Я, пожалуй, воспользуюсь формулой из вот этой статьи , с учетом того что у нас n = 3 позиции и цифры от 0 до 5

 5
 N3(x) =  N2(x – l),
l=0

при этом  N1(x) =  1 (x от 0 до 5) или 0 (x > 5)
Если l > x, слагаемое возвращает 0.

Решение.

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

public partial class MainWindow : Window
{
    List<int> ValidNumbers = new List<int>() { 0, 1, 2, 3, 4, 5 };
    int Positions = 3; 

    public MainWindow()
    {
        InitializeComponent();
    }

    private void EncryptBtn_Click(object sender, RoutedEventArgs e)
    {
       int result = 0;

       int min_sum = ValidNumbers.Min() * Positions;
       int max_sum = ValidNumbers.Max() * Positions;
       for (int x = min_sum; x < max_sum + 1; x++)
       {
          result = result + Convert.ToInt32(Math.Pow(GetNByX(x, Positions), 2));
       }
       this.ResultTB.Text = result.ToString();
    }

    private int GetNByX(int x, int division)
    {
       if ((division == 1) && (!ValidNumbers.Contains(x)))
          return 0;
       else if ((division == 1) && (ValidNumbers.Contains(x)))
          return 1;
       else
       {
          int result = 0;
          for (int l = 0; l < ValidNumbers.Max() + 1; l++)
          {
              if (l <= x)
                 result = result + GetNByX(x - l, division - 1);
          }
          return result;
       }
    }
}
Примечание:
Использовано:

  • Википедия
  • Подборка статей о вычислении количества счастливых билетиков: http://www.ega-math.narod.ru/Quant/Tickets.htm

Картинка, музычка и проектик:






среда, 11 марта 2015 г.

Если данные есть, но их нет...

Загадка:
SELECT * from t1 where t1_id = 99; - пустота.
SELECT * from t2 where t1_id = 99; - две строчки.
SELECT t1_id FROM t2 WHERE t1_id NOT IN (SELECT t1_id FROM t1); - пустота.

Как это возможно?

История.

Не разворачивается dbimport базы. Создает таблицы, грузит данные, начинает разворачивать схему и... "525 Failure to satisfy referential constraint" - не могу повесить на эти данные внешний ключ, потому что в данных нарушена целостность. (в таблице на которую я ссылаюсь нет такого значения первичного ключа, какое есть в столбце ссылок)

Удивляемся. Лезем в данные и ищем как же так вышло:
SELECT t1_id FROM t2 WHERE t1_id NOT IN (SELECT t1_id FROM t1); - две строчки.
Не целостные ссылки - это ссылки на t1_id = 98 и 99.
Ищем данные по их первичному индексу: SELECT * from t1 where t1_id in (98,99); - пустота.
Ищем данные, которые ссылались на них: SELECT * from t2 where t1_id in (98, 99); - две строчки.
Удивляемся. Проверяем наличие каскадного удаления по внешнему ключу в таблице t2. - все хорошо, каскадное удаление присутствует.

Еще больше удивляемся и лезем в базу, с которой сделан экспорт.

SELECT * from t1 where t1_id in (98,99); - пустота.
SELECT * from t2 where t1_id in (98, 99); - две строчки.
SELECT t1_id FROM t2 WHERE t1_id NOT IN (SELECT t1_id FROM t1); - пустота...
Каскадное удаление присутствует.

В итоге данных в таблице t1 - нет, но они есть...
Поздравляем себя - у нас сыпется жесткий диск. Данные были, их никто не удалял, но теперь субд их не видит.

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

воскресенье, 1 марта 2015 г.

Шифрование на уровне столбцов таблиц в Informix Dynamic Server:

http://www.ibm.com/developerworks/ru/library/dm-0711mohan/

Неожиданный и потом полезный для меня взгляд на суррогатные ключи в БД:

http://habrahabr.ru/post/107834/

GraphSharp + QuickGraph или CodeStars. Track 04. Визуализация хоть чего-нибудь

Я выбрала для решения своей задачи QuickGraph потому что не нашла ему альтернатив, кроме самостоятельной реализации алгоритма.
И я хотела добавить своему решению лоска визуализацией. У QuickGraph есть сборка QuickGraph.Glee  Но, судя по всему, ее надо было ставить дополнительно.
А у GraphSharp есть WPF контроллер... и он использует QuickGraph. "Сказка же!" - думаю я: минимум зависимостей, все аккуратненько. С контроллером. Легко и просто...

Но пришлось создавать отдельный пост, потому что просто не получилось.

Все, казалось бы просто. У меня уже есть граф. Я его посчитала и теперь хочу отобразить.
Тем временем, чтобы отобразить граф с помощью Graph# есть два способа.

Визуализация графа Graph#

Простой способ.
Мануал: http://www.youtube.com/watch?v=VTbuvkaPGxE
Встроенный WPF контроллер GraphLayout  позволяет привязать к себе IBidirectionalGraph<object, IEdge<object>>. Что и делают в имеющемся мануале.
Но только его.
Нюанс в том, что, например BidirectionalGraph<object, Edge<object>> не может быть преобразован  к данному интерфейсу никаким приведением.
В то же время алгоритмы кратчайшего пути из QuickGraph работают именно с графами, а не интерфейсами.

В случае, если нас это не устраивает и хочется чего-то более сложного, мы можем воспользоваться вторым вариантом.

Полный и абсолютно настраиваемый способ. 
Мануал:  https://sachabarbs.wordpress.com/2010/08/31/pretty-cool-graphs-in-wpf/
Мы описываем с самого начала тип данных вершины, тип данных ребра, тип графа (наследуем от Bidirectional...), свой Layout для отображения (наследуем от встроенного) и далее по тексту.

Это трудоемко, но можно попробовать.

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

if (!graph.ContainsVertex(language_from))
   graph.AddVertex(language_from);

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

Визуализация произвольного графа с базовыми типами в Graph#

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

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

Поэтому я не нашла ничего лучше, чем перед отображением копировать граф в новую структуру, приспособленную к GraphLayout:

private void ShowGraph(BidirectionalGraph<object, Edge<object>> graph)
{
  var graphForVis = new BidirectionalGraph<object, IEdge<object>>();

  foreach(object node in graph.Vertices)
  {
     graphForVis.AddVertex(node);
  }
  foreach (Edge<object> edge in graph.Edges)
  {
     graphForVis.AddEdge(new Edge<object>(edge.Source, edge.Target));
  }

  //this.LanguageGraphCanvas - мой LayoutGraph контроллер
  this.LanguageGraphCanvas.Graph = graphForVis;
}       

Буду считать это такой разновидностью контроллера между моделью данных (QuickGraph) и их представлением (Graph#). 

Плюсы такого решения:

  1. Я, в процессе экспериментов, изменила тип графа на Bidirectional. Но могла бы этого и не делать. Как следствие: мне не пришлось ради отображения менять данные и механику работы с ними.
  2. Минимальное количество дополнительного кода. Описание классов вершины, ребра, графа... - очень объемно и трудоемко, т.е. избыточно, при том что мне никаких особых свойств не надо.
  3. Так можно отобразить если не любой граф, то достаточное их количество и при этом решить проблему с совместимостью типа графа с мат. методами QuickGraph.
Минусы:
  1. this.LanguageGraphCanvas.Graph = graphForVis; 
    Привязка данных к WPF контроллеру из кода скорее моветон, чем норма. Но чтобы привязка была человеческая тип графа должен бы поддерживать INotifyPropertyChanged - как в примере - чтобы отображение привязанного сразу из XAML графа изменилось после заполнения. 


Ссылки (cобраны из обоих постов в кучку):

1. Визуализация средствами QuickGraph.Glee http://quickgraph.codeplex.com/wikipage?title=Visualization%20Using%20Glee .
2. Домашняя страница и конкретно мануалы по Graph#  http://graphsharp.codeplex.com/wikipage?title=Tutorials&referringTitle=Home
3. Пример работы QuickGraph и Graphiz
http://dotnet.wonderu.com/2009/05/blog-post.html

Картинка, Проект. Музычка





суббота, 28 февраля 2015 г.

CodeStars. Track 04. Математика

Задача.
Чтобы узнать, сколько комментариев у тебя будет на GitHub, реши следующую задачу:На международном музыкальном фестивале участники говорят на множестве разных языков. Чтобы все могли понимать друг друга, организаторы предложили использовать автоматические переводчики, но переводчики есть не для всех пар языков. В текстовом файле в каждой строке содержится (через пробел) имя переводчика, с какого языка и на какой он может переводить. Какое минимальное количество переводчиков необходимо, чтобы переводить с Исландского на Албанский?

Предисловие.
Ох лол... у меня не будет комментариев на GitHub, потому что у меня нет аккаунта на GitHub. Но задача есть задача. Классическая задача на графы, каждый язык - вершина графа, а переводчик - ребро графа, связывающее языки. И надо найти кратчайший путь в графе. Структура данных проста: есть язык, точки входа и точки выхода. Алгоритм известен... что означает, мы с легкостью можем его загуглить, например здесь (Задача о кратчайшем пути в Википедии)Наученная прошлым опытом и вооруженная ленью, я предполагаю, что библиотеки работы с графами для C# уже есть. Есть три аспекта работы с графами: создание структуры данных, алгоритмы и отображение. Почти все библиотеки поддерживают создание структур и их отображение. Различия в основном лежат в алгоритмах визуализации, различных параметрах визуализации, механизмах выгрузки картинок (например в pdf), способа ввода данных и возможностях автоматической генерации графа. А вот математические алгоритмы реализует только QuickGraph. Может быть и не только он.. но остальных мне найти не удалось. )) 
  1. Microsoft Automatic Graph Layout (MSAGL) она же Glee - собственно была библиотека Glee, теперь она же перекуплена MS, доработана и называется страшной аббревиатурой MSAGL. Также иногда в инетиках именуется Microsoft Glee. Что не совсем корректно, по ссылке есть раздел отличий.
  2. Graphviz но в нем мне не устанавливая разобраться не удалось. Хотя в этом формате вот здесь есть пример: QuickGraph + Graphiz  . Если верить документации на сайте очень развитые варианты  вывода и настроек отображения.
  3. Graph# - по факту WPF контроллер для отображения графов. Содержит несколько алгоритмов визуализации. 
  4. NodeXL - вроде работает только с графами заданными через Excel.
  5. QuickGraph - библиотека обеспечивающая мат. алгоритмы работы с графами. А также позволяет выводить графы через Glee, Graphiz. 
Решение.
Сначала разберемся с математикой. 
  1. Добавляем QuickGraph в ссылки и включаем использование:

    using QuickGraph;

    using QuickGraph.Algorithms.ShortestPath;
  2. Создаем граф. Так как переводчик единица универсальная - в том смысле, что, вообще говоря, переводить может в обоих направлениях, то создаем ненаправленный граф.

     
    UndirectedGraph<string, Edge<string>> languageGraph = null;
  3. Пишем метод, для заполнения этого графа из файла. Предположим FillGraph();
  4. Думаем над тем, какой нам нужен алгоритм. Мне нравится Дейкстра. Это ничем не обосновано, на самом деле.

    var a = 
    new UndirectedDijkstraShortestPathAlgorithm<string, Edge<string>>(languageGraph, edgeCost);
  5. Используем алгоритм:
    -
      Назначаем ключевую вершину:

     
    a.SetRootVertex("Исландский");

    Делаем расчет алгоритма: a.Compute(); Это по факту запускает алгоритм с заданным графом и настройками.
    - Получаем искомое: 


    if (a.TryGetDistance("Албанский", out path_length))
                   MessageBox.Show(path_length.ToString());
    else
         
       MessageBox.Show("Что-то не так.");

  6. Получаем правильный ответ. 
Визуализация  : to be continued.