Re[18]: Веб и динамика? Веб и статика+метапрограммирование.
От: VladD2 Российская Империя www.nemerle.org
Дата: 24.12.10 19:19
Оценка:
Здравствуйте, DarkGray, Вы писали:

DG>вот только он неправильный, и сортировка будет всегда по последнему кортежу: потому что orderby и thenby это не одно и тоже.


DG>сложные вещи ты обошел:

DG>преобразованием значения из строки в тип поля — не привел.

DG>код с восстановлением операции по строке — тоже не привел.


Убил еще час времени чтобы создать полный рабочий вариант воспроизводящий твое т.з.
Интересно как ты теперь будешь выкручиваться? Просто сделаешь вид, что не заметил ответ или найдешь фатальный недостаток?
using Nemerle.Collections;

using System;
using System.Collections.Generic;
using System.Console; // В Nemerle можно открывать не только пространства имен, но и классы!
using System.Linq;

using PersonQuery; // В Nemerle можно открывать не только пространства имен, но и классы!

[Record] // Этот макрос генерирует коструктор инициализирующий все поля класса
class Person
{
  public Name     : string;
  public Birthday : DateTime;
  public Company  : string;
  
  public override ToString() : string
  { // $-строка - это песня! :)
    $"Person: $Name, $(string(' ', 4 - Name.Length)) $(Birthday.ToShortDateString()), $Company"
  }
}

module Program
{
  data : list[Person] = 
    [
      Person("Вася", DateTime(1976, 5, 15),  "RSDN"),
      Person("Петя", DateTime(1967, 3, 1),   "Мега"),
      Person("Петя", DateTime(1981, 1, 5),   "Мега"),
      Person("Петя", DateTime(1953, 1, 12),  "Мега"),
      Person("Аня",  DateTime(1981, 12, 21), "Мега"),
      Person("Дуня", DateTime(1982, 12, 21), "Мега"),
    ];

  Main() : void
  {
    def print(query, msg)
    {
      WriteLine($"$msg:");
      WriteLine($<#..$(query; "\n")#>);
      WriteLine();
    }
    
    def myQuery = data.Where(p => p.Birthday < DateTime(1982, 1, 1)); // эмулируем основной запрос.
    print(myQuery, "Исходный запрос");
    
    def orderingInfos = [("name", true), ("birthday", false)];
    def filterInfos = [("name", "starts", "Пе"), ("birthday", ">=", "10.01.1955")];
    
    def filteredQuery = FilterInfoToQuery(myQuery, filterInfos);
    print(filteredQuery, "После добавления фильтра");

    def orderedMyQuery = OrderingInfosToQuery(filteredQuery, orderingInfos);
    print(orderedMyQuery, "После добавления сортировки");

    def orderedMyQuery2 = OrderingInfosToQuery(myQuery, orderingInfos);
    print(orderedMyQuery2, "После добавления сортировки к исходному запросу (демонстрирвет, что сортировка производится по двум полям)");
   _ = ReadLine();
  }
}

/// Модуль предоставляющий функции конвертации опписаний заданных в виде
/// в соответствующие кобмбинатроные функции.
module PersonQuery
{
  // Таблицы отображения.
  _personFieldOperMap : Hashtable[string * string, Person * string -> bool];
  _orderFirstMap  : Hashtable[string * bool, IEnumerable[Person]        -> IOrderedEnumerable[Person]];
  _orderFollowMap : Hashtable[string * bool, IOrderedEnumerable[Person] -> IOrderedEnumerable[Person]];
  
  this()
  {
    // Заполняем таблицы отображения...
    // Вот эту хрень любо-дорого заполнить на макросах!
    
    _personFieldOperMap = Hashtable();
    _personFieldOperMap["birthday", ">="]     = (p, value) => p.Birthday >= DateTime.Parse(value);
    _personFieldOperMap["birthday", "=="]     = (p, value) => p.Birthday == DateTime.Parse(value);
    _personFieldOperMap["birthday", "<="]     = (p, value) => p.Birthday <= DateTime.Parse(value);
    _personFieldOperMap["company",  "=="]     = (p, value) => p.Company == value;
    _personFieldOperMap["company",  "starts"] = (p, value) => p.Company.StartsWith(value);
    _personFieldOperMap["name",     "=="]     = (p, value) => p.Name == value;
    _personFieldOperMap["name",     "starts"] = (p, value) => p.Name.StartsWith(value);

    _orderFirstMap = Hashtable();
    _orderFirstMap["name",     true]  = query => query.OrderBy(_.Name); // "_.Name" - это синтаксис частичного применения. Это аналогично коду "p => p.Name"
    _orderFirstMap["name",     false] = query => query.OrderByDescending(_.Name);
    _orderFirstMap["birthday", true]  = query => query.OrderBy(_.Birthday);
    _orderFirstMap["birthday", false] = query => query.OrderByDescending(_.Birthday);
    _orderFirstMap["company",  true]  = query => query.OrderBy(_.Company);
    _orderFirstMap["company",  false] = query => query.OrderByDescending(_.Company);

    _orderFollowMap = Hashtable();
    _orderFollowMap["name",     true]  = query => query.ThenBy(_.Name);
    _orderFollowMap["name",     false] = query => query.ThenByDescending(_.Name);
    _orderFollowMap["birthday", true]  = query => query.ThenBy(_.Birthday);
    _orderFollowMap["birthday", false] = query => query.ThenByDescending(_.Birthday);
    _orderFollowMap["company",  true]  = query => query.ThenBy(_.Company);
    _orderFollowMap["company",  false] = query => query.ThenByDescending(_.Company);
  }

  /// Формирует LINQ-запрос фильтрующий данные в исходном запросе.
  /// query - исходный запрос.
  /// filteringInfos - список котежей описывающих фильрацию. 
  ///   Первое поле - имя поля в Person. 
  ///   Второе поле - бинарный оператор используемый для создания фильтрующего предиката.
  ///   Третье поле - значение используемое для сравнения (в предикате).
  public FilterInfoToQuery(query : IEnumerable[Person], filteringInfos : list[string * string * string]) : IEnumerable[Person]
  {
    def addFilter((field, op, value), query)
    {
      def predicate = _personFieldOperMap[field, op];
      query.Where(predicate(_, value))
    }

    filteringInfos.FoldLeft(query, addFilter) // производим свертку filteringInfos и формирование запроса
  }

  /// Формирует LINQ-запрос сортирующий данные в исходном запросе.
  /// query - исходный запрос.
  /// orderingInfos - список котежей описывающих сортировку. 
  ///   Первое поле - имя поля в Person. 
  ///   Второе поле - сортировка по возрастанию (true) или убыванию (false).
  public OrderingInfosToQuery(query : IEnumerable[Person], orderingInfos : list[string * bool]) : IEnumerable[Person]
  {
    def addOrdering(orderingInfo, query)
    {
      def addOrder = _orderFollowMap[orderingInfo]; // получаем функцию добавляющую к запросу сортировку
      def newQuery = addOrder(query); // трансформируем запрос
      newQuery
      // Разбиение на стадии и использование промежуточных переменных сделаны
      // чтобы более наглядно продемонстрировать, что делает этот метод.
      // Код этого метода вполне можно было бы записать в одну строку:
      // _orderFollowMap[orderingInfo](query)
    }
    
    def orderedQuery = 
      match (orderingInfos)
      {
        | [] => query // в случае пустого списка фильтрации возвращ исходный запрос.
        | [first] => _orderFirstMap[first](query) // если в списке только один элемент, добавляем тольтко OrderBy
        | first :: tail => tail.FoldLeft(_orderFirstMap[first](query), addOrdering) // иначе добавляем OrderBy для первого элемента и ThenBy для последующих.
      };
    
    orderedQuery // Возвращаем сформированный запрос как возвращаемое значение функции.
  }
}


Консольный вывод:
Исходный запрос:
Person: Вася,  15.05.1976, RSDN
Person: Петя,  01.03.1967, Мега
Person: Петя,  05.01.1981, Мега
Person: Петя,  12.01.1953, Мега
Person: Аня,   21.12.1981, Мега

После добавления фильтра:
Person: Петя,  01.03.1967, Мега
Person: Петя,  05.01.1981, Мега

После добавления сортировки:
Person: Петя,  05.01.1981, Мега
Person: Петя,  01.03.1967, Мега

После добавления сортировки к исходному запросу (демонстрирвет, что сортировка производится по двум полям):
Person: Аня,   21.12.1981, Мега
Person: Вася,  15.05.1976, RSDN
Person: Петя,  05.01.1981, Мега
Person: Петя,  01.03.1967, Мега
Person: Петя,  12.01.1953, Мега


К слову, о том где действительно рулит MP в виде макросов. Создание модулей вроде PersonQuery можно легко автоматизировать с помощью макросов. На вход он может получать описание класса, а на выходе выдавать модуль ИмяКлассаQuery содержащий все нужные вспомогательные методы. При этом можно даже генерировать как IEnumerable- и IQueryable-версию модуля.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.