AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 10.01.2014, 19:09   #1  
kgksoft is offline
kgksoft
Участник
 
37 / 107 (4) +++++
Регистрация: 24.12.2003
Сортировка контейнеров
Всегда не хватало сортировки контейнеров в DAX. Только руки дошли написать.
Навеяно
http://gatesasbait.wordpress.com/200...n-a-container/
можно указывать несколько полей для сортировки (контейнер индексов).

X++:
//class Global

static container quickSort(
    container   _qsc,
    container   _cIndexKey  = [1],
    int         _qsstart    = 1,
    int         _qsend      = conlen(_qsc)
    )
{
    int         qsi = _qsstart;
    int         qsj = _qsend;
    container   qsx;
    container   qsKey;

    boolean less(container _c1, container _c2)
    {
        int qsk;
        int idx;
        ;

        for (qsk=1; qsk<=conlen(_cIndexKey); qsk++)
        {
            idx = conpeek(_cIndexKey, qsk);
            if (conpeek(_c1, idx) < conpeek(_c2, idx)) return true;
            if (conpeek(_c1, idx) > conpeek(_c2, idx)) return false;
        }
        return false;
    }
    ;

    qsx = conpeek(_qsc, (_qsstart +_qsend)/2);

    do
    {
        qsKey = conpeek(_qsc, qsi);
        while (less(qsKey, qsx))
        {
            qsi++;
            if (qsi <= conlen(_qsc))
            {
                qsKey = conpeek(_qsc, qsi);
            }
            else
            {
                break;
            }
        }

        qsKey = conpeek(_qsc, qsj);
        while (less(qsx, qsKey))
        {
            qsj = qsj - 1;
            if (qsi > 0)
            {
                qsKey = conpeek(_qsc, qsj);
            }
            else
            {
                break;
            }
        }

        if (qsi <= qsj)
        {
            qsKey = conpeek(_qsc, qsi);
            _qsc = conpoke(_qsc, qsi, conpeek(_qsc, qsj));
            _qsc = conpoke(_qsc, qsj, qsKey);
            qsi++;
            qsj = qsj - 1;
        }
    } while (qsi <= qsj);

    if (_qsstart < qsj)
    {
        _qsc = Global::quickSort(_qsc, _cIndexKey, _qsstart, qsj);
    }
    if (qsi < _qsend)
    {
        _qsc = Global::quickSort(_qsc, _cIndexKey, qsi, _qsend);
    }

    return _qsc;
}
Примеры использования:
X++:
    container c = [ [2, 3], [5, 2], [1, 2], [20, 2], [10, 2] ];
    c = quickSort(c, [2,1]);
    //[ [1, 2], [5, 2], [10, 2], [20, 2], [2, 3] ]
    c = quickSort(c);
    //[ [1, 2], [2, 3], [5, 2], [10, 2], [20, 2] ]
За это сообщение автора поблагодарили: S.Kuskov (3).
Старый 10.01.2014, 22:33   #2  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
А, если не секрет, зачем это?.. . В Аксапте есть классы-коллекции, поддерживающие сортировку, - Set и Map, если нужно сортировать данные, то лучше, мне кажется, использовать их. Скажем, если есть контейнер значений одного базового типа, то получить их в отсортированном по возрастанию виде можно так:
X++:
container conValues = ...;
Types baseType = typeof(conpeek(conValues, 1));
Set setOfValues = Set::create([1, any2int(baseType), conlen(conValues)] + conValues);
И все, ядро само отсортирует значения при распаковке Set'а, останется только перебрать их. А если возникает задача отсортировать контейнер контейнеров по нескольким элементам внутри контейнеров, то, возможно, стоило выбрать более подходящую структуру данных, скажем, временную таблицу.

Последний раз редактировалось gl00mie; 10.01.2014 в 22:57. Причина: очепятка
Старый 10.01.2014, 23:45   #3  
Diman is offline
Diman
Участник
Сотрудники Microsoft Dynamics
 
166 / 35 (2) +++
Регистрация: 27.06.2003
Адрес: Москва
Позвольте позанудствовать, но для реального использования не самая лучшая реализация, а для разминки или задачки на собеседовании вполне себе.
__________________
Sapere aude

Последний раз редактировалось Diman; 10.01.2014 в 23:46. Причина: typo
Старый 11.01.2014, 02:32   #4  
kgksoft is offline
kgksoft
Участник
 
37 / 107 (4) +++++
Регистрация: 24.12.2003
Иногда возникает необходимость отсортировать мизерный набор данных (до 10 строк), но не простые типы, а кортежи. Задача "не заслуживает" отдельной временной таблицы. Вот для этих целей и создавался метод. В моём случае это были контейнер из 2 элементов (лот и некое несопоставленное кол-во по лоту). Сортировал по кол-ву и подбирал нужный лот. Свою задачу я решил. Производительность устраивает. Ничего подобного в базовом фунционале не нашёл.
За это сообщение автора поблагодарили: mazzy (2).
Старый 11.01.2014, 12:07   #5  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от kgksoft Посмотреть сообщение
Всегда не хватало сортировки контейнеров в DAX.
0.
в алгоритме есть принципиальная ошибка - конейнер может содержать другие контейнеры. а также другие объекты, для которых отсутствует операция сравнения.

поэтому ваш алгоритм имеет очень ограниченное применение.
надо отдать должное функция less - прикольная.
но потенциально опасна runtime-ошибками.

1.
насколько я помню (пусть меня поправят знающие люди), контейнер - это атомарный объект в Аксапте.

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

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

2.
контейнер - чисто аксаптовская структура, для которой нет аналога ни на .net., ни в SQL, ни в OLAP...
в связи с планами перевода всего и вся на .net, контейнеры будут все более более неприемлемым инструментом.

поэтому:
  • По возможности, не используйте контейнеры для хранения данных.
  • Если уж используете контейнер, постарайтесь минимизировать число модификаций контейнера

=================================
в связи с вышесказанным лучше:
* преобразовать контейнер в объект-коллекцию, например в set, (при преобразовании выполните проверку)
* (опционально для несортирующих коллекций) выполнить сортировку
* преобразовать объект-коллекцию обратно в контейнер.

причем в классе Global вроде уже есть функции преобразователи.
и если содержимое контейнера вам заранее известно и не требует проверки и не содержит одинаковых элементов, то лучше вместо своего алгоритма вызвать пару функций, что-то вроде
X++:
myContainer = set2con(con2set(myContainer)); // сортируем контейнер, содержащий индексы
(сейчас аксапты нет под рукой, чтобы проверить наличие именно этих функций)

Последний раз редактировалось mazzy; 11.01.2014 в 12:27.
Старый 11.01.2014, 12:24   #6  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
теперь по существу алгоритма.

1.
использовать во вложенной функции less переменную _cIndexKey, глобальную по отношению к функции less - безусловный моветон.

2.
мне кажется, что будут проблемы с пустыми контейнерами (поскольку _cIndexKey и _qsstart инициализируются единицей)

3.
вместо вычисления conlen в цикле по неизменному контейнеру, лучше один раз вычислить и хранить в переменной. поскольку conlen каждый раз просматривает контейнер и вычисляет.

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

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

[ [1, 1], [10,10], [1,1], [5,5], [1,1] ]


В общем:
* за попытку - зачет
* но по реализации - Дональд Кнут в помощь

Последний раз редактировалось mazzy; 11.01.2014 в 12:29.
Старый 11.01.2014, 13:15   #7  
kgksoft is offline
kgksoft
Участник
 
37 / 107 (4) +++++
Регистрация: 24.12.2003
Цитата:
Сообщение от mazzy Посмотреть сообщение
1.
использовать во вложенной функции less переменную _cIndexKey, глобальную по отношению к функции less - безусловный моветон.
это вложенная функция, так что считаю такой подход приемлемым

Цитата:
Сообщение от mazzy Посмотреть сообщение
2.
мне кажется, что будут проблемы с пустыми контейнерами (поскольку _cIndexKey и _qsstart инициализируются единицей)
проблем не было, но это только из-за особенностей поведения интерпретатора.
Сделал дополнительную проверку на пустой контейнер

Цитата:
Сообщение от mazzy Посмотреть сообщение
3.
вместо вычисления conlen в цикле по неизменному контейнеру, лучше один раз вычислить и хранить в переменной. поскольку conlen каждый раз просматривает контейнер и вычисляет.
Исправил

Цитата:
Сообщение от mazzy Посмотреть сообщение
4.
и мне кажется... что будут серьезные проблемы, если контейнер будет содержать одинаковые элементы.

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

[ [1, 1], [10,10], [1,1], [5,5], [1,1] ]
Нормально отсортировало.

Попутно доделал возможность указывать порядок сортировки для каждого элемента. Исправленная версия:
X++:
static container quickSort(
    container   _qsc,
    container   _cIndexKey  = [[1, SortOrder::Ascending]],
    int         _qsstart    = 1,
    int         _qsend      = conlen(_qsc)
    )
{
    int         qsi = _qsstart;
    int         qsj = _qsend;
    container   qsx;
    container   qsKey;

    boolean less(container _c1, container _c2)
    {
        int qsk;
        int idx;
        SortOrder so;
        int len = conlen(_cIndexKey);
        ;

        for (qsk=1; qsk<=len; qsk++)
        {
            [idx, so] = conpeek(_cIndexKey, qsk);
            if (conpeek(_c1, idx) < conpeek(_c2, idx)) return (so==SortOrder::Ascending)?true:false;
            if (conpeek(_c1, idx) > conpeek(_c2, idx)) return (so==SortOrder::Ascending)?false:true;
        }
        return false;
    }
    ;

    if (! conlen(_qsc)) return _qsc;
    qsx = conpeek(_qsc, (_qsstart +_qsend)/2);

    do
    {
        qsKey = conpeek(_qsc, qsi);
        while (less(qsKey, qsx))
        {
            qsi++;
            if (qsi <= conlen(_qsc))
            {
                qsKey = conpeek(_qsc, qsi);
            }
            else
            {
                break;
            }
        }

        qsKey = conpeek(_qsc, qsj);
        while (less(qsx, qsKey))
        {
            qsj = qsj - 1;
            if (qsi > 0)
            {
                qsKey = conpeek(_qsc, qsj);
            }
            else
            {
                break;
            }
        }

        if (qsi <= qsj)
        {
            qsKey = conpeek(_qsc, qsi);
            _qsc = conpoke(_qsc, qsi, conpeek(_qsc, qsj));
            _qsc = conpoke(_qsc, qsj, qsKey);
            qsi++;
            qsj = qsj - 1;
        }
    } while (qsi <= qsj);

    if (_qsstart < qsj)
    {
        _qsc = Global::quickSort(_qsc, _cIndexKey, _qsstart, qsj);
    }
    if (qsi < _qsend)
    {
        _qsc = Global::quickSort(_qsc, _cIndexKey, qsi, _qsend);
    }

    return _qsc;
}
Тестирование:
X++:
    container c = [ [1, 2], [10,10], [1,5], [5,5], [1,10] ];
    ;
    
    c = global::quickSort(c, [[1, SortOrder::Descending],[2, SortOrder::Ascending]]);
    // [ [10,10], [5,5], [1, 2], [1,5], [1,10]  ]
За это сообщение автора поблагодарили: mazzy (5), alex55 (3).
Старый 11.01.2014, 14:52   #8  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Спасибо за конструктивный подход.
Цитата:
Сообщение от kgksoft Посмотреть сообщение
Нормально отсортировало.

Тестирование:
X++:
    container c = [ [1, 2], [10,10], [1,5], [5,5], [1,10] ];
    ;
    
    c = global::quickSort(c, [[1, SortOrder::Descending],[2, SortOrder::Ascending]]);
    // [ [10,10], [5,5], [1, 2], [1,5], [1,10]  ]
нет-нет-нет.
Согласно вашей функции less, кортежи [1,2], [1,5], [1,10] - это разные элементы.
я же говорил об одинаковых элементах [1,1], [1,1], [1,1].

посмотрю чуть позже, когда доберусь до аксапты.
Старый 11.01.2014, 16:06   #9  
kgksoft is offline
kgksoft
Участник
 
37 / 107 (4) +++++
Регистрация: 24.12.2003
Одинаковые кортежи тоже нормально отсортировались. Новый пример привел для демонстрации управления порядком сортировки
Старый 12.01.2014, 19:25   #10  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от kgksoft Посмотреть сообщение
Одинаковые кортежи тоже нормально отсортировались. Новый пример привел для демонстрации управления порядком сортировки
да, добралсямдо аксапты. работает корректно.
спасибо.

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

===================
маленькое дополнение на будущее:
= в качестве значения по умолчанию используйте литералы
= в качестве значения по умолчанию никогда НЕ используйте потенциально-долго-выполняющиеся функции, вместо них используйте prmisdefault

Дело в том, что Аксапта ВСЕГДА вычисляет значение по умолчанию. Даже если значение было передано в вызывающем классе.

X++:
static container quickSort(
    container   _qsc,
    container   _cIndexKey  = [[1, SortOrder::Ascending]],
    int         _qsstart    = 1,
    int         _qsend      = 0
    )
{
    int         _qsend      = prmisdefault(_qsend) ? conlen(_qsc) : _qsend;
...
===================
но в целом - контейнеры лучше не использовать.
Старый 13.01.2014, 08:49   #11  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,440 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от mazzy Посмотреть сообщение
Аксапта ВСЕГДА вычисляет значение по умолчанию. Даже если значение было передано в вызывающем классе.
Первый раз об этом слышу.

AX2009:
X++:
    boolean defaultValue()
    {
        ;
        info("!");
        return true;
    }
    
    void test(boolean _prm = defaultValue())
    {
        ;
        info(strfmt("%1", _prm));
    }
    ;
    
    test();
    test(true);
    test(false);
Функция расчета значения по-умолчанию вызвалась только когда параметр был опущен.
Цитата:
!
true
true
false
За это сообщение автора поблагодарили: mazzy (5).
Старый 13.01.2014, 09:57   #12  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Первый раз об этом слышу.
Перепроверю.
столкнулся с этим, когда занимался производительностью в ax2012.
conlen,, map.elements и т.п. приходилось убирать из дефолта.
Старый 13.01.2014, 17:21   #13  
russ is offline
russ
Участник
Сотрудники Microsoft Dynamics
 
51 / 33 (2) +++
Регистрация: 02.04.2002
Адрес: Moscow
есть еще системный класс KeySum
Старый 14.01.2014, 17:51   #14  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Первый раз об этом слышу.
проверил. действительно не вычисляется.
спасибо.

извините, что ввел в заблуждение.
Старый 15.01.2014, 15:41   #15  
dech is offline
dech
Участник
Аватар для dech
Самостоятельные клиенты AX
 
647 / 350 (13) ++++++
Регистрация: 25.06.2009
Адрес: Омск
Записей в блоге: 3
Цитата:
Сообщение от russ Посмотреть сообщение
есть еще системный класс KeySum
Об этом уже говорили:
Цитата:
Сообщение от mazzy Посмотреть сообщение
насколько я помню, именно это и послужило причиной создания объектов-коллекций и причиной неразвития таких полезных структур как KeySum.
Кстати, не припомню, чтобы там можно было сортировать контейнеры.
Всё же придерживаюсь мнения использовать классы-коллекции для сортировки.
__________________
// no comments
Теги
контейнер, сортировка

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Сортировка в гриде с двумя DataSource ViV DAX: Программирование 15 05.06.2014 15:55
Сортировка в Query в отчете Zlojbarsuk DAX: Программирование 7 27.03.2010 00:18
Сортировка в оборотно-сальдовой ведомости по поставщикам Silphidae DAX: Программирование 6 16.02.2010 15:02
FormListControl сортировка maximka DAX: Программирование 2 26.12.2008 13:44
Сортировка в гриде Pegiy DAX: Функционал 15 06.12.2006 08:21

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 21:39.