Страницы

Поиск по вопросам

суббота, 4 января 2020 г.

LINQ сортировка по динамическим параметрам

#c_sharp #net #linq


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

var newItemsReportInfo = new List();
switch (itemsReportInfo[0].SortingTH.Count)
{
    case 0:
        newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos)
            .ThenBy(x => x.Description)
            .ThenBy(x => x.Flieztext)
            .ToList();
        break;
    case 1:
        newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos)
            .ThenBy(x => x.SortingTH[0], new TechDataComparer())
            .ThenBy(x => x.Description)
            .ThenBy(x => x.Flieztext)
            .ToList();
        break;
    case 2:
        newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos)
            .ThenBy(x => x.SortingTH[0], new TechDataComparer())
            .ThenBy(c => c.SortingTH[1], new TechDataComparer())
            .ThenBy(x => x.Description)
            .ThenBy(x => x.Flieztext)
            .ToList();
        break;
    case 3:
        newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos)
            .ThenBy(x => x.SortingTH[0], new TechDataComparer())
            .ThenBy(c => c.SortingTH[1], new TechDataComparer())
            .ThenBy(c => c.SortingTH[2], new TechDataComparer())
            .ThenBy(x => x.Description)
            .ThenBy(x => x.Flieztext)
            .ToList();
        break;
    case 4:
        newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos)
            .ThenBy(x => x.SortingTH[0], new TechDataComparer())
            .ThenBy(c => c.SortingTH[1], new TechDataComparer())
            .ThenBy(c => c.SortingTH[2], new TechDataComparer())
            .ThenBy(c => c.SortingTH[3], new TechDataComparer())
            .ThenBy(x => x.Description)
            .ThenBy(x => x.Flieztext)
            .ToList();
        break;
    case 5:
        newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos)
            .ThenBy(x => x.SortingTH[0], new TechDataComparer())
            .ThenBy(c => c.SortingTH[1], new TechDataComparer())
            .ThenBy(c => c.SortingTH[2], new TechDataComparer())
            .ThenBy(c => c.SortingTH[3], new TechDataComparer())
            .ThenBy(c => c.SortingTH[4], new TechDataComparer())
            .ThenBy(x => x.Description)
            .ThenBy(x => x.Flieztext)
            .ToList();
        break;
    case 6:
        newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos)
            .ThenBy(x => x.SortingTH[0], new TechDataComparer())
            .ThenBy(c => c.SortingTH[1], new TechDataComparer())
            .ThenBy(c => c.SortingTH[2], new TechDataComparer())
            .ThenBy(c => c.SortingTH[3], new TechDataComparer())
            .ThenBy(c => c.SortingTH[4], new TechDataComparer())
            .ThenBy(c => c.SortingTH[5], new TechDataComparer())
            .ThenBy(x => x.Description)
            .ThenBy(x => x.Flieztext)
            .ToList();
        break;
    default:
        newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos)
            .ThenBy(x => x.SortingTH[0], new TechDataComparer())
            .ThenBy(c => c.SortingTH[1], new TechDataComparer())
            .ThenBy(c => c.SortingTH[2], new TechDataComparer())
            .ThenBy(c => c.SortingTH[3], new TechDataComparer())
            .ThenBy(c => c.SortingTH[4], new TechDataComparer())
            .ThenBy(c => c.SortingTH[5], new TechDataComparer())
            .ThenBy(c => c.SortingTH[6], new TechDataComparer())
            .ThenBy(x => x.Description)
            .ThenBy(x => x.Flieztext)
            .ToList();
        break;
}


Как видно из кода, мы всегда смотрим на размер itemsReportInfo[0].SortingTH и далее
уже применяем один из методов сортировки. Хотелось бы заменить этот код, на код вида.

 newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos)
     .ThenBy(x => x.SortingTH, new TechDataComparer())
     .ThenBy(x => x.Description)
     .ThenBy(x => x.Flieztext)
     .ToList();

    


Ответы

Ответ 1



Попробуйте что-то в таком стиле: var newItemsReportInfo = new List(); var query = itemsReportInfo.OrderBy(x => x.FlyerPos); for (int i = 0; i < itemsReportInfo[0].SortingTH.Count; i++) { query = query.ThenBy(x => x.SortingTH[i], new TechDataComparer()); } query = query.ThenBy(x => x.Description); query = query.ThenBy(x => x.Flieztext); newItemsReportInfo = query.ToList();

Ответ 2



Вариант с TechDataComparer для List public class ListTechDataComparer : IComparer> { private IComparer comparer = new TechDataComparer(); public int Compare(List x, List y) { for (int i = 0; i < x.Count; i++) { var compareResult = comparer.Compare(x[i],y[i]); if (compareResult != 0) return compareResult; } return 0; } } и использовать newItemsReportInfo = itemsReportInfo.OrderBy(x => x.FlyerPos) .ThenBy(x => x.SortingTH, new ListTechDataComparer()) .ThenBy(x => x.Description) .ThenBy(x => x.Flieztext) .ToList();

Битовая математика

#c_sharp #инспекция_кода #byte


Программа читает бинарные данные из файла. У меня был пример чтения, на основе которого,
я реализовал данный пример записи.Общий смысл: есть заголовок файла (это код до слов
"пишем данные"). С заголовком все просто. Пишется без всяких преобразований. Далее
пишется маска и после маски пишутся 3 значения переменных. В маску пишется сколько
байт каждое из чисел занимает.

Код:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ConnectButton_Click(object sender, RoutedEventArgs e)
    {
        // Переменная хранит путь к файлу
        string file_path = "ResultFile.rls";

        // Удаляем файл, если он есть
        if (File.Exists(file_path))
        {
            File.Delete(file_path);
        }

        // Создаем файл
        FileStream fs = File.Open(file_path, FileMode.OpenOrCreate, FileAccess.Write,
FileShare.Write);
        // Поток для записи в файл
        BinaryWriter bw = new BinaryWriter(fs);

        // Пишем размерность. Double
        bw.Write(-1.0);
        // Пишем какую то хрень. int32. 
        bw.Write(2);
        // Количество записей. int32. 
        bw.Write(5);
        // Val1. Double
        bw.Write(6800.0);
        // Val2. Double
        bw.Write(6800.0);
        // Val3. Double
        bw.Write(6800.0);
        // Val4 Double
        bw.Write(6800.0);
        // DateTime. Int64
        bw.Write(new DateTime(2015, 03, 12, 23, 0, 0).Ticks);
        // Val5. Int64
        bw.Write((Int64)2);

        // пишем данные
        AddItem(bw, 5, 10, 0);
        AddItem(bw, 1, 0, 1);
        AddItem(bw, 3, 20, 3);
        AddItem(bw, 5, 60, 1);
        AddItem(bw, 4, 80, 2);

        bw.Close();
        fs.Close();
    }

    // Функция отрезает не значимые значения массива
    byte[] CutZero(byte[] in_array)
    {
        // Список байт
        List result;
        // Инициализируем список
        result = new List();

        // Переменная определяет, что пора начинать копирование
        bool ready_to_copy = false;

        // В цикле перебираем каждый байт переданного массива с конца
        for (int i = in_array.Length - 1; i >= 0; i--)
        {
            // Если это не нулевой байт, то начинаем копирование
            if (in_array[i] != 0x00)
            {
                ready_to_copy = true;
            }

            // Если копирование началось
            if(ready_to_copy)
            {
                result.Add(in_array[i]);
            }
        }

        // Возвращаем полученный массив
        return result.ToArray();
    }

    // Добавить данные в файл
    private void AddItem(BinaryWriter _bw, int _vol, int _dp, int _dt)
    {
        // 1.Считаем переменные
        // 1.1. Val1
        byte[] vol = CutZero(BitConverter.GetBytes(_vol));
        // 1.2. Val2
        byte[] dp = CutZero(BitConverter.GetBytes(ConvertVal2Bytes(_dp)));
        // 1.3. Val3
        byte[] dt = CutZero(BitConverter.GetBytes(_dt));

        // 1. Количество байт. Сразу пишем в двоичном виде
        string str_length = "0";
        // 1.1. Для Val1
        str_length += ConvertFromVal1(vol.Length);
        // 1.2. Для Val2
        str_length += ConvertFromVal2(dp.Length);
        // 1.3. Для Val3
        str_length += ConvertFromVal3(dt.Length);

        // Пишем маску
        _bw.Write(Convert.ToByte(str_length, 2));

        // Val3
        _bw.Write(dt);
        // Val2
        _bw.Write(dp);
        // Val1
        _bw.Write(vol);
    }

    // Конвертируем байты
    private byte ConvertVal2Bytes(int _dp)
    {
        byte result;
        // Получим массив байт, для определения длины
        byte[] dp = CutZero(BitConverter.GetBytes(_dp));

        // В зависимости от количества байт добавляем дельты
        switch (dp.Length)
        {
            case 0:
            {
                // Ничего не добавляем
                result = (byte)_dp;
                break;
            }
            case 1:
                {
                    result = (byte)((byte)_dp + 0x80);
                    break;
                }
            case 2:
                {
                    result = (byte)((byte)_dp + 0x400);
                    break;
                }
            case 4:
                {
                    result = (byte)((byte)_dp + 0x4000000);
                    break;
                }
            default:
                {
                    result = (byte)_dp;
                    break;
                }
        }

        // Возвращаем результат
        return result;
    }

    // Конвертируем длину в двоичное представление
    private string ConvertFromVal3(int length)
    {
        string result = "";

        switch (length)
        {
            case 0:
                result = "00";
                break;
            case 1:
                result = "01";
                break;
            case 2:
                result = "10";
                break;
            case 3:
                result = "11";
                break;
            default:
                break;
        }

        return result;
    }
    // Конвертируем длину в двоичное представление
    private string ConvertFromVal2(int length)
    {
        string result = "";

        switch (length)
        {
            case 0:
                result = "00";
                break;
            case 1:
                result = "01";
                break;
            case 2:
                result = "10";
                break;
            case 4:
                result = "11";
                break;
            default:
                break;
        }

        return result;
    }
    // Конвертируем длину в двоичное представление
    private string ConvertFromVal1(int length)
    {
        string result = "";

        switch (length)
        {
            case 1:
                result = "001";
                break;
            case 2:
                result = "110";
                break;
            case 4:
                result = "111";
                break;
            case 8:
                result = "010";
                break;
            default:
                break;
        }

        return result;
    }
}


Все работает. Но у меня есть такое ощущение, что можно как то это упростить. Я не
работал ни разу с байтами / битами. Возможно часть кода можно заменить какими то стандартными
функциями. Очень было бы поучительно.
    


Ответы

Ответ 1



Например, функцию CutZero можно представить на LINQ так: IEnumerable CutZero(IEnumerable in_array) { return in_array.Reverse().SkipWhile(b => b == 0x00); } Затем, код (byte)((byte)_dp + 0x400) выглядит странно: прибавление 0x400 не затрагивает младший байт, так что после преобразования к (byte) он всё равно отбросится. Затем, ConvertFromVal3 — это перевод в двоичное представление? Его можно легко заменить на встроенную функцию: Convert.ToString(length, 2).PadLeft(2, '0') Затем, совершенно неясно, что делают функции ConvertFromVal2 и ConvertFromVal1. Они не конвертируют в двоичное представление (т. к., например, двоичное представление для 4 будет никак не "111"), но возвращают явно двоичные строки. Затем, конвертация чисел в строку лишь для того, чтобы потом склеить и сконвертировать назад, выглядит как-то совсем странно. Если вы хотите склеить биты, применяйте битовые операции: private byte ConvertFromVal3(int length) { if (length < 0 || length > 3) throw new ArgumentException(...); return (byte)length; } private byte ConvertFromVal2(int length) { switch (length) { case 0: return (byte)0; case 1: return (byte)1; case 2: return (byte)2; case 4: return (byte)3; default: throw new ArgumentException(...); } } private byte ConvertFromVal1(int length) { switch (length) { case 1: return (byte)1; case 2: return (byte)6; case 4: return (byte)7; case 8: return (byte)2; default: throw new ArgumentException(...); } } // ... // 1. Количество байт. Сразу пишем в двоичном виде byte lengths = 0; // 1.1. Для Val1 lengths |= (ConvertFromVal1(vol.Length) << 4); // 1.2. Для Val2 lengths |= (ConvertFromVal2(dp.Length) << 2); // 1.3. Для Val3 lengths |= ConvertFromVal3(dt.Length); // Пишем маску _bw.Write(lengths);

Ответ 2



Если под упростить подразумевается уменьшение количества строк кода, то можно в ConnectButton_Click вместо многократных bw.Write(...) значения поместить в массив и вывести их в цикле. А ConvertFromVal3, например, определить так: private string ConvertFromVal3(int length) { var arr = new[] { "00", "01", "10", "11" }; return length < arr.Length ? arr[length] : ""; }

Как логировать работу программы с помощью ETW?

#c_sharp #net #clr #логирование


Информация о работе .NET выводится в ETW (Event Tracing for Windows) и ее можно получить
с помощью программы PerfView.
Как из приложения выводить свою информацию в ETW?
И возможно ли выводить 100 тыс. сообщений в секунду? 
    


Ответы

Ответ 1



ETW позволяет выводить до 500 тыс. сообщений в секунду с минимальными затратами. Например, надо в ETW выводить уведомления о ходе выполнения, а также информацию о начале и завершении какой-то активности. Для этого надо определить класс, производный от EventSource. using System.Diagnostics.Tracing; [EventSource(Name = "MyApp")] class MyAppEvent : EventSource { public static MyAppEvent Log = new MyAppEvent(); [Event(1)] // уведомление о ходе выполнения public void Progress(int v, string msg) { WriteEvent(1, v, msg); } [Event(2, Opcode = EventOpcode.Start)] // начало активности public void StartAction(int id) { WriteEvent(2, id); } [Event(3, Opcode = EventOpcode.Stop)] // завершение активности public void StopAction(int id) { WriteEvent(3, id); } } для вывода в ETW пишем partial class MainWindow : Window { public MainWindow() { var log = MyAppEvent.Log; ... log.Start(1); log.Progress(1, "ok"); log.Stop(1); Компилируем и из командной строки запускаем PerView > PerfView /OnlyProviders=*MyApp run WpfApplication.exe В результате создается PerfViewData.etl.zip, в котором находится файл PerfViewData.etl - его можно открыть в PerfView и посмотреть информацию, например, по вызовам Start.

спецификаторы inline __inline __forceinline

#cpp


Есть ли отличия в скорости работы кода, если например использовать спецификатор в
методе класса?
    


Ответы

Ответ 1



Есть, иногда. inline не имеет никакого отношения к встраиванию функций, все компиляторы ее игнорируют. По этому единственное практическое применение inline - это использовать ее для функций, определенных в заголовочных файлах: // header.hpp inline void f() { // несколько экранов кода } Такая функция f возможно никогда не будет заинлайнена, но без inline ее нельзя разместить в заголовочном файле. __inline, __forceinline, и другие расширения компилятора уже могут как-то влиять на то, будет функция заинлайнена или нет. Однако в общем случае встраивание функции не приводит к увеличению производительности. После встраивания функции, производительность может как увеличиться (убрали вызов функции), так и уменьшиться - цикл может стать большим, и перестанут работать предсказания в процессоре (prefetch и т.п.). По этому __inline и __forceinline следует применять совместно с бенчмарками, использованием профилировщика, и просмотром сгенерированного ассемблерного кода. Использование этих ключевых слов вслепую не дает никакого эффекта.

Считается ли нормальным такой стиль написания кода?

#java #инспекция_кода


public class MainTest {
    PracticeWork1 grades;

    MainTest() {
        grades = new PracticeWork1();
        init();
    }

    public static void main(String[] args) {
        new MainTest();
    }

    public void init() {
        testOut();
        grades.adder();
        testOut();
        grades.adder(98);
        testOut();
    }
}


пример 2

public class MyWin extends JFrame {

    private static final long serialVersionUID = 1L;

    public MyWin() {
        Container c = getContentPane(); 
        c.setLayout(new BorderLayout()); 

        Panel child= new Panel();    
        c.add(child);
        setTitle("Example window"); 

        setPreferredSize(new Dimension(640, 480));

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setVisible(true); // отображаем окно
    }

    public static void main(String args[]) {
        new MyWin();
    }
}


Правильно ли в методе main создавать обьект того класса в котором этот мейн находиться
, а не прописывать большинство логики в нём ?
    


Ответы

Ответ 1



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

Ответ 2



Логика в конструкторе - не очень хорошая идея. Такой код тяжело тестировать. Так же в случае возникновения исключения в конструкторе могут быть проблемы. Я бы рекомендовал инициализацию проводить не в конструкторе: public class MainTest { PracticeWork1 grades; MainTest() { } public static void main(String[] args) { MainTest test = new MainTest(); test.init(); } public void init() { grades = new PracticeWork1(); testOut(); grades.adder(); testOut(); grades.adder(98); testOut(); } } Для этого хорошо подошёл бы шаблон Builder.

Как вывести в консоль следующий месяц?

#python #python_3x


import datetime
today = datetime.date.today()
month = today.strftime("%b")# Показать текущий месяц
print(month)


Данный код выводит текущий месяц, в данном случае "Jan"
Как вывести следующий месяц в таком же формате?
    


Ответы

Ответ 1



import calendar import datetime today = datetime.date.today() calendar.month_abbr[today.month%12 + 1]

Ответ 2



import datetime t = datetime.date.today() try: n = t.replace(t.year, t.month + 1, 1) except ValueError: n = t.replace(t.year + 1, 1, 1) print n.strftime("%b") В n будет лежать первый день следующего месяца.

TypeError при изменении списка оператором += внутри кортежа

#python


Известно, что оператор += для списков примерно эквивалентен методу extend в том смысле,
что при его использовании не создаётся копия списка, а расширяется текущий.

В связи с этим возникает вопрос: почему следующая конструкция бросает исключение
TypeError:

t = ([1], )  # кортеж из одного элемента, являющегося списком
try:
    t[0] += [2]
except TypeError as e:
    print(e)             # 'tuple' object does not support item assignment
print(t)                 # ([1, 2],) -- и сам список изменился!


Ведь мы не изменяем ссылки, сохранённые в кортеже и следующий код работает без проблем:

t = ([1], )
t[0].extend([2])
print(t)           # ([1, 2],)


Почему такое поведение?
    


Ответы

Ответ 1



Дело в том, что операция += всё же содержит в себе присваивание, хоть и выполняет изменение "на месте". Оператор += реализуется через метод __iadd__, и код t[0] += [2] эквивалентен следующей записи: res = t[0].__iadd__([1]) t[0] = res # TypeError: 'tuple' object does not support item assignment Хоть ссылка на самом деле и не меняется: print(res is t[0]) # True но присваивание есть, и это мешает выполниться подобному оператору без ошибок. Способ с использованием метода, очевидно, такой проблемы иметь не может. Можно удостовериться, что присваивание действительно есть. Для этого рассмотрим более простой случай: a = [1] a += [2] Посмотрим, какой код генерируется в Python 3 для выполнения второй строки (операции могут отличаться, но суть будет примерно та же): import dis dis.dis('a += [2]') Расшифровка действия со стеком 1 0 LOAD_NAME 0 (a) push [1] (переменная "a") 3 LOAD_CONST 0 (2) push 2 6 BUILD_LIST 1 pop (2), push [2] 9 INPLACE_ADD pop ([2]), pop ([1]), push [1, 2] 10 STORE_NAME 0 (a) pop ([1, 2] -> a) ... Именно попытка выполнить операцию STORE_NAME (более точно, STORE_SUBSCR, для кортежа) вызывает ошибку в случае, описанном в вопросе. Важно понимать, что операция INPLACE_ADD выполняет добавление элементов "на месте", т.е. на стеке в итоге окажется тот же объект.