Показаны сообщения с ярлыком C#. Показать все сообщения
Показаны сообщения с ярлыком C#. Показать все сообщения

вторник, 7 июня 2011 г.

Используем Microsoft Excel при помощи C# и .NET

Для того, что бы можно было начать работу с Microsoft Excel необходимо в проект добавить ссылку на COM компонент "Microsoft Excel 12.0 Object Library" после чего в проекте можно будет использовать классы из пространства имен Microsoft.Office.Interop.Excel.

Для быстрого старта и понимания работы с Microsoft Excel посредством в COM в C# достаточно базового примера - считывание из указанного файла .xlsx и запись в указанный файл. Я использовал .NET Framework 4 и Microsft Excel 2007. 

Код программы с комментариями: 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MicrosoftExcel = 
    Microsoft.Office.Interop.Excel;

namespace MicrosoftExcelDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // создание объекта 
            // приложения
            MicrosoftExcel.Application application = 
                new MicrosoftExcel.Application();
            try 
            {
                // открытие диалогового 
                // окна для поиска файла 
                application.FindFile();
                // считывание данных из активного листа 
                ReadData(application.ActiveSheet);
                // запись в активный лист
                WriteData(application.ActiveSheet);                
            }
            catch(Exception e)
            {
                Console.WriteLine(
                    "Exception catched " + 
                    "with message: '{0}'", 
                    e.Message);
            }
            finally
            {
                // обязательное завершение
                // работы приложения 
                application.Quit();                            
            }
            Console.ReadKey();
        }

        static void ReadData(
            MicrosoftExcel.Worksheet workSheet)
        {
            // индексация в Microsoft Excel идет с 1
            // далее следует обход только 
            // используемой области активного листа 
            for (int row = 1; 
                row <= 
                  workSheet.UsedRange.Cells.Rows.Count; 
                row++)
            {
                for (int col = 1; 
                    col <= 
                      workSheet.UsedRange.Cells.Columns
                               .Count; 
                    col++)
                {
                    Console.Write(
                        "{0, 5} ", 
                        workSheet.UsedRange
                                 .Cells[row, col]
                                 .Value);
                }
                Console.WriteLine();
            }
        }

        static void WriteData(
            MicrosoftExcel.Worksheet workSheet)
        {
            for (int row = 1;
                row <= 
                  workSheet.UsedRange.Cells.Rows.Count; 
                row++)
            {
                for (int col = 1; 
                    col <= 
                      workSheet.UsedRange.Cells.Columns
                               .Count; 
                    col++)
                {
                    workSheet.UsedRange
                             .Cells[row, col]
                             .Value *= 10;
                }                
            }
        }
    }
}

Или вы можете скачать исходный код и файл с которым я работал здесь

Если возникнут вопросы или что-то не понятно, то обязательно спрашивайте, я всегда рад помочь! 

вторник, 8 марта 2011 г.

Список форумов по .NET/C#

Я решил составить список наиболее популярных форумов по .NET/C#. В список вошли, как англоязычные так и русскоязычные форумы. Я отсортировал их по "оценке", которая вычисляется по простой формуле:  "Оценка = Google PageRank * 0,05 + Yandex Rank * 0,05 + Yandex тИЦ * 0,001 + Количество тем * 0,00001 + Количество сообщений * 0,000001 + Количество пользователей * 0,0000001 + Количество сообщений в теме (в среднем) * Количество сообщений на пользователя (в среднем)".

Я учитывал Google PageRank, Yandex Rank, Ynadex тИЦ, количество зарегистрированных пользователей, количество сообщений, количество тем, но не факт, что эти параметры являются объективными. Далее приведу список форумов в порядке убывания оценки. Чем выше оценка, тем лучше, чем меньше, тем хуже:
  1. Forums at asp.net
  2. Microsoft Developer Network > Forums
  3. Microsoft.NET на sql.ru
  4. .NET форум на forum.vingrad.ru
  5. С/С++, C#, Delphi, .NET, Asm  на forum.antichat.ru
  6. .NET на forum.shelek.ru
  7. .NET на Исходники.Ru
  8. .NET на forum.sysman.ru
  9. PHP форум на ПЫХА.РУ => Языки программирования => C/C++ и C#
  10. .NET Programming Forum at CodeGuru.Com
  11. C# .NET на КиберФоруме
  12. C# Corner Forums
  13. C# на forum.chertenok.ru
  14. Программирование для .NET в клубе программистов
  15. C# Online.NET Forums
  16. .NET на programmingforum.ru
  17. Forum at csharphelp.com
  18. .NET на forum.codeby.net
  19. Microsoft .NET framework на codenet.ru
  20. .NET на realcoding.net
  21. MSDN Форум
  22. .NET на progz.ru
  23. Основы программирования на C# на форуме forum.intuit.ru
Вот как примерно, выглядит сама таблица:



Если пожелаете вы можете просмотреть список форумов с подробностями здесь или скачать файл в формате Microsoft Excel.

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

четверг, 20 января 2011 г.

C#/.NET маленькие чудеса: ограничение обобщений при помощи условия where

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

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

Однако, обобщения C# иногда введут себя совсем иначе, чем ихние двоюродные братья C++ шаблоны. Существует одно удобное положение, которое поможет обойти эти воды и сделает ваши  обобщения более мощными.

Проблема - C# допускает наименьший общий знаменатель

В C++, вы можете создать шаблон и делать практически все с параметром шаблона, конечно, если это допускается синтаксически, и C++ не будет проверять корректен ли вызванный метод/поле/операция до тех пор, пока вы не объявите реализацию типа. Давайте-ка я вам это продемонстрирую:

// компилируется нормально, C++ не делает 
// никаких предположений о типе T
template <typename T>
class ReverseComparer
{
public:
     int Compare(const T& lhs, const T& rhs)
     {
         return rhs.CompareTo(lhs);
     }
};

Заметьте, что мы спокойно вызываем метод CompareTo() для шаблонного типа T. Потому, как мы на данный момент не знаем, что из себя представляет тип T, и C++ не делает ни каких предположений, следовательно и не возникает ошибок.

C++ стремится не проверять используемый тип шаблона до тех пор, пока метод действительно не будет вызван для конкретного типа, что очень отличается от поведения C#:

// это НЕ скомпилируется! 
// C# допускает наименьший общий знаменатель
public class ReverseComparer<T>
{
     public int Compare(T lhs, T rhs)
     {
         return lhs.CompareTo(rhs);
     }
}

Так почему же C# выдает ошибку компиляции когда, мы еще не знаем, что за тип имеет T? Это происходит потому, что C# идет по-другому пути создания обобщений, в отличии от C++. Пока вы не укажите обратное, T трактуется как нечто похожее на object (заметьте я не сказал как object).

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

Сейчас, чем шире объект является, тем более абстрактным (более общим) он должен быть. Так как же мы позволим нашему обобщенному типу заменителю, делать нечто большее, чем может делать object?

Решение: ограничить тип используя условие where

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

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

Ограничение обобщенного типа интерфейсом или суперклассом

Одно из удобных возможностей where ограничений - указывать какой интерфейс обобщенный тип должен реализовать или какой класс обобщенный тип должен наследовать. Например, вы не могли вызвать метод CompareTo() в нашем первом C# обобщении, но если ограничить тип T интерфейсом IComparable<T>, то вы сможете:

public class ReverseComparer<T> 
    where T : IComparable<T>
{
    public int Compare(T lhs, T rhs)
    {
        return lhs.CompareTo(rhs);
    }
}

Теперь, когда мы ограничили T реализацией IComparable<T>, это означает, что наши переменные обобщенного типа могут вызывать любые члены IComparable<T>. Теперь можно легально вызвать CompareTo().

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

Ограничение обобщенных типов  только ссылочными типами

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

Хорошо, мы можете это исправить ограничением class в where условии. Объявляя то, что обобщенный тип, должен быть class, мы говорим, что он является ссылочным типом и может принимать значение null для экземпляров этого типа:

public static class ObjectExtensions
{
    public static TOut Maybe<TIn, TOut>(
        this TIn value, Func<TIn, TOut> accessor)
        where TOut : class
        where TIn : class
    {
        return (value != null) 
                   ? accessor(value)  
                   : null;
    }
}
>

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

Ограничение обобщенных типов только типами значений

Как обобщенный тип может быть ссылочным типом, также само он может быть и типом значений. Что бы это сделать используйте ограничение struct, которое говорит, что обобщенный тип должен быть типом значений (примитивный, структура, перечисление и т.п.).

Рассмотрим следующий метод, который будет конвертировать, что угодно реализующее IConvertible (int, double, string и т. п.) в тип значение, который вы укажете, или null, если экземпляр является null:

public static T? ConvertToNullable<T>(
    IConvertible value)
    where T : struct
{
    T? result = null;
  
    if (value != null)
    {
        result = (T)Convert.ChangeType(
                     value, typeof(T));
    }
  
    return result;
}

Так как T был ограничен типом значений, мы можем использовать T? (System.Nullable<T>), где мы не могли этого делать, если T был ссылочным типом.

Ограничение обобщенных типов требованием к наличию конструктора по умолчанию

Вы также можете ограничить тип требованием к наличию конструктора по умолчанию. Так как C# по умолчанию не знает какой конструктор имеет или не имеет обобщенный тип заместитель, то он не может позволить конструктор. Это говорит о том, что если обобщенный тип будет ограничен new(), то это будет обозначать, что тип реализующий обобщенный тип должен иметь конструктор по умолчанию (без параметров).

Предположим, что у вас есть обобщенный класс адаптер, который получив некоторые отображения, будет адаптировать некоторый предмет из типа TFrom в тип TTo. Так как он должен создавать новые экземпляры типа TTo в процессе, то мы должны указать, что TTo обязан иметь конструктор по умолчанию:

// Полученный набор Action 
// отображений будет отображен из TFrom в TTo
public class Adapter<TFrom, TTo> : 
    IEnumerable<Action<TFrom, TTo>>
    where TTo : class, new()
{

    public List<Action<TFrom, TTo>> Translations 
    { 
        get; 
        private set; 
    }
   
    public Adapter()
    {
        Translations = new List<
                           Action<TFrom, TTo>>();
    }
  
    public void Add(
        Action<TFrom, TTo> translation)
    {
        Translations.Add(translation);
    }


    void Add(
        Predicate<TFrom> conditional, 
        Action<TFrom, TTo> translation)
    {
        Translations.Add((from, to) =>
            {
                if (conditional(from))
                {
                    translation(from, to);
                }
            });
    }
  
    public TTo Adapt(TFrom sourceObject)
    {
        var resultObject = new TTo();
  
        Translations.ForEach(
            t => t(sourceObject, resultObject));
        return resultObject;
    }

    public IEnumerator<
        Action<TFrom, TTo>> GetEnumerator()
    {
        return Translations.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

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

Выводы

Условие where - это превосходная вещь, которая дает .NET обобщениям больше мощи для выполнения заданий, которые требуют большего поведения, чем базовое (речь идет об object).

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

Следующие вещи, которые вы не можете указать, при помощи ограничений на данный момент:

В дополнение, вы не можете перегружать определение шаблона, разными ограничениями. Например, вы можете определить Adapter where T : struct и Adapter where T : class. 

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

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

понедельник, 15 ноября 2010 г.

Определяем уравнение прямой при помощи искусственных нейронных сетей

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

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



Нам будет дан набор точек и на основе него, необходимо найти коэффициенты с и m - в уравнение прямой y = mx + c. Для обучения сети будем использовать дельта-правило - (правило Видроу-Хоффа). Если вкратце, то суть правила такова, мы на вход сети (в нашем случае перцептрон)  подадим значение координаты X, сеть вычислит значение координаты Y, мы сравним вычисленное сетью значение с ожидаемым и получим отклонение, то есть ошибку E. После чего мы полученную ошибку умножим на норму обучения n (она задается вручную) и на сигнал приходящий к Y, в результате мы и получаем отклонение весов dw1, dw2 и так далее. Остается только скорректировать весы w1 = w1 + dw1, w2 = w2 + dw2, ... 

Архитектура сети, также взято из Калан. Р., не удивляйтесь на то, что я привожу материалы из источников, моя задача запрограммировать сеть с использованием C#: 



До каких пор обучать сеть? Здесь, все просто до приемлемого уровня заданной точности, например, до 0.05 или просто провести обучение определенное количество раз, например, 10000. Теперь приведу, код программы, которая имитирует данный перцептрон:


// задаем начальное значение весов случайным образом 
// в интервале от [-0.3, 0.3] 
double[] w = new double[] 
{ 
    ((rD = r.Next(-100, 100) * 0.003) == 0) ? 
        0.3 : rD,
    ((rD = r.Next(-100, 100) * 0.003) == 0) ? 
        0.3 : rD
};

double[] w1 = new double[2]; 
w.CopyTo(w1, 0);

// входные сигналы
double[] x = new double[]
{
    1.0, 
    0.0
};

// норма обучения или скорость обучения 
double n = 0.1; 

double[,] pairs = new double[,] 
{
    {0.2, 1.3},
    {0.25, 1.3},
    {0.4, 1.4},
    {0.48, 1.52},
    {0.6, 1.6},
    {0.8, 1.6},
    {0.9, 2},
    {1, 2.1}
};
            
for (int i = 0; i < 10000; i++)
{
    for (int j = 0; j < pairs.GetLength(0); j++)
    {
        // для x на входе
        x[1] = pairs[j, 0];     
        // ожидаемый y на выходе
        double d = pairs[j, 1];
        // сумматор
        double y = x[0] * w[0] + x[1] * w[1];
        // вычисляем ошибку
        double e = d - y;
        // отклонение весов 
        double dW0 = e * n * x[0];
        double dW1 = e * n * x[1];
        // кооректируем весы 
        w[0] = w[0] + dW0;
        w[1] = w[1] + dW1;
    }
}

Console.WriteLine("w0 = {0}, w1 = {1}", 
    w1[0], w1[1]);
Console.WriteLine("m = {0}, c = {1}, |m - c| = {2}", 
    w[1], w[0], Math.Abs(w[1] - w[0]));
// m = 0.993801..., c = 1.03101...

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

Проект для Visual Studio 2010 Premium вы можете скачать отсюда. Спасибо, за внимание! Если у вас возникли вопросы, то задавайте. Либо если вы заметили ошибку, то сообщите мне об этом. Я мог здесь ошибиться, ибо я новичок в искусственных нейронных сетях. 

суббота, 9 октября 2010 г.

Простейший компилятор простейшего языка C в Assembler. Часть 2. Лексический блок

Прежде чем читать этот пост, сначала ознакомьтесь с "Простейший компилятор простейшего языка C в Assembler. Часть 1. Транслитератор".

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

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

X = (0.5E1 + 2 + Y);
SCAN(Y);
IF (Y > (X + 2)) {
    PRINT(Y);
} 
Y1 = 10;
M0:
  IF (Y1 != 20 + 30) {
      GOTO M0;
  }    

Разобьем код на лексемы:

(опер. присваивания, 1)  // 1 - номер строки в 
                         // таблице идентификаторов
                         // пример: X = 
(число, 1)               // 1 - номер строки 
                         // в таблице констант 
                         // пример: 0.5
(SCAN, 1)                // 1 - номер строки в 
                         // таблице идентификаторов
                         // пример: SCAN(X)
(IF, IF)                 // пример: IF
((, ()                   // пример: (
(идентификатор, 1)       // 1 - номер строки в 
                         // таблице идентификаторов
                         // пример: x
(), ))                   // пример: )
(ар. операц, +)          // пример: + 
(оп. отношения, >=)      // пример: >=
({, {)                   // пример: {
(}, })                   // пример: }
(;, ;)                   // точка с запятой
(метка, 3)               // 3 - номер строки в 
                         // таблице идентификаторов
                         // пример M0: 
(GOTO, 3)                // 3 - номер строки в 
                         // таблице идентификаторов 
                         // GOTO M0
(PRINT, 2)               // 2 - номер строки в 
                         // таблице идентификаторов
                         // пример: PRINT(Y)

Здесь приведены, только уникальные лексемы для наглядности, так как дважды писать лексему "идентификатор" не нужно.

Теперь вы видите и понимаете, какая задача стоит перед лексическим блоком. Необходимо разбить код на лексемы да и еще в не которых местах проверить синтаксис.

С этой задачей нам позволят разобраться конечные автоматы: детерминированные и не детерминированные, также они решат проблему с производительностью, так как они могут обрабатывать огромные тексты за одни проход. Далее ознакомьтесь с статьями [1], [2], после чего с статьей [3]. Мы будем делать нечто похожее, как описано в третьей статье.

Сначала спроектируем конечный автомат. Я этот делал в файле формата Microsoft Excel. Сам файл можете скачать отсюда, а также всякий случай оставлю "скриншот", для тех кто качать не хочет:



Если вы прочитали, указанные мной, статьи, то для вас не составит труда разобраться с приведенным конечным автоматом. Хотя это может забрать не мало времени. После того, как КА спроектирован осталось перевести его в код на языке программирования C#, аналогично [3] статье.

Код проекта уже с транслитератором и лексическим блоком вы можете скачать отсюда. Проект создан в Visual Studio 2010 Premium.

Если вы заметите ошибки - пишите. Если у вас возникнут вопросы - задавайте их!

пятница, 24 сентября 2010 г.

Простейший компилятор простейшего языка C в Assembler. Часть 1. Транслитератор

Два слова в общем

Для общего развития, никогда не помешает написать компилятор языка. Я не ставлю перед собой задачу написать компилятор c++ или c#, задачу которую я перед собой ставлю - это необходимый минимум для ознакомления с теорией компиляции.

Входной язык будет достаточно простой, урезанный по самое не могу, C. А выходной язык будет - язык Ассемблера.

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

Наш простейший компилятор языка C (далее TheSimpliestCCompiler - tsico), будет разбит на четыре блока или более: transliteration unit, lexical unit, syntax unit, generation unit. Получатся четырех-проходной компилятор. Чем же занимаются эти блоки?

Transliteration Unit


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

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

Lexical Unit

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

Syntax Unit 

Получает набор лексем, проверяет синтаксис. Определяет набор атомов(простейших единичных
конструкций языка).

Generation Unit

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

Транслитератор

Сегодня, мы приступим к транслитератору. Сразу оговорюсь, язык на котором, мы будем писать транслитератор - C#.

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

Определимся с символьными классами для простейшего прототипа языка C:

  • letter - [A-Za-z]
  • digit - [0-9]
  • whitespace - ' '
  • newline - '\n'
  • compare - [><=!]
  • arithmetic - [+-/*]
  • ( - '('
  • ) - ')'
  • ; - ';'
  • { - '{'
  • } - '}'
  • . - '.'
  • : - ':'

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

Я не буду приводить весь код транслитератор. Покажу лишь фрагмент:

class TransliterationUnit
{

    // ... 

    public void Transliterate(
        string input, string output)
    {
        // don't do this at production, 
        // reading string to memory can be 
        // so dangerous on files with larger size
        string code = File.OpenText(input).ReadToEnd().
                           Replace("\r", "").
                           Replace("\t", "").Trim().
                           ClearExtraWhitespaces();
        List<Symbol> symbols = new List<Symbol>();

        foreach (char c in code)
        {
            
            if (GetSymbolClass(c) != "other")
            {
                symbols.Add(new Symbol() { 
                   Value = c, 
                   Class = GetSymbolClass(c)});
            }
            else
            {
                _logger.Log(String.Format(
                    "Unexpected symbol - '{0}'", c));
            }
        }

        SaveSymbols(output, symbols);
    }

    // ... 
}

Если у вас есть желание ознакомиться с полной работающей версии транслитератора компилятора tsico (я упоминал о названии выше), то вы можете скачать проект с исходным кодом для Visual Studio 2010.

Литература

В завершение я бы хотел посоветовать вам книгу "Альфред В. Ахо, Моника С. Лам, Рави Сети, Джеффри Д. Ульман: Компиляторы. Принципы, технологии и инструментарий".

Отражение точки относительно произвольной прямой - Windows приложение

Как следствие тема о "отражении точки относительно произвольной прямой" тема развилась в такое простое приложение:


Для чего я публикую код этого приложения? Возможно, это не лучший пример, но там затронуты такие темы, как:

  • Перетаскивание графических объектов используя события MouseUp, MouseDown, MouseMove формы. 
  • Изменение курсора мыши на любой другой. 
  • Анимация. 
  • Работа с координатами, перевод координат из одной системы в другую. 
Если вам интересны вышеперечисленные темы, то код для вас. Этот код не является лучшим, а всего лишь наброски идей. Проект для Visual Studio 2010, вы можете скачать отсюда

понедельник, 13 сентября 2010 г.

Отражение точки относительно произвольной прямой

Я уточню название поста - зеркальное отражение точки относительно произвольной прямой проходящей через начало системы координат. Вот стоит перед вами такая задача, вам дана точка с координатами x;y и прямая заданная уравнением ax + by = 0. Программа должна на входе получить a; b; x; y, а на выходе новые отраженные x;y.

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

Например, нам нужно повернуть объект на не который угол. В данном случае мы можем использовать так называемую матрицу поворота (Rotation Matrix или просто R). Как ее получить?






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

В нашей задаче при зеркальной симметрии относительно прямой, мы также можем использовать матрицы. Для начала, посмотрим какие преобразования нам необходимо сделать:






Сначала мы повернем объект до нуля, потом отразим его и вернем в конечное положение. Данные преобразования выполнить достаточно легко - необходимо использовать матрицу поворота и отражения. Для удобства все преобразования сведем к одной матрице, назовем ее Result Matrix и Result Matrix = Rotation Matrix * Reflection Matrix * Rotation Matrix. А уже потом результирующую матрицу мы применим к объекту, в данном случае к точке. 

Как же мы узнаем угол поворота до оси абсцисс, легко мы знаем, что y = kx + b; и мы знаем, что k - это угловой коэффициент, который как раз и показываем отклонение относительно оси абсцисс, k = tan(alpha). Угол мы легко получим, alpha = atan(k). Но у нас же уравнение вида ax + by = 0 - не проблема, приведем его к нужному нам виду y = -(a/b)x; Тогда угол поворота alpha = atan(-a/b), обратный угол поворота это просто -alpha. Итого, у нас есть угол поворота, осталось найти матрицу отражения. А это легко, она также заранее известна, матрица отражения относительно оси абсцисс равна:


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



Код проекта на C#, который демонстрирует решение данной задачи я выложил здесь. Внимание! Проект работает в Visual Studio 2010, желательно Premium. Если же проект вам не удастся открыть, то достаточно скопировать код из файла Program.cs и перенести его в другой проект. 

Если заметите ошибки - сообщите, спасибо и успехов вам!