в SortedSet<T> баг?
От: g-host  
Дата: 25.04.11 17:54
Оценка:
Или баг в моих мозгах...Ничего не понимаю.
Есть SortedSet<Tuple<string, int>>
Я благополучно передаю в конструктор свой компаратор. Алгоритм сравнения очень прост,
код компаратора даже нету смысла приводить:
если строки равны возвращаем 0.
иначе сравниваем числа, если и числа равны то просто string.CompareTo(x.Item1, y.Item2)
Но, как же я удивился как например после того как в списке есть ("10", 8)
то ("10", 0) все равно добавляется, несмотря на обещанное
"Класс SortedSet<T>не принимает повторяющихся элементов. Если item уже присутствует в наборе, то этот метод возвращает значение false и не вызывает исключение"
Такой код:

 SortedSet<Tuple<string, int>> l = new SortedSet<Tuple<string, int>>(new MyComparer());
 for (int i = 0; i < 100; i++)
    l.Add(new Tuple<string, int>(i.ToString(), i));
 for (int i = 0; i < 10; i++)
    l.Add(new Tuple<string, int>(i.ToString(), default(int)));
 Console.WriteLine(l.Count);


выводит 106, хотя я ожидаю 100

В то же время


 SortedSet<Tuple<string, int>> l = new SortedSet<Tuple<string, int>>(new MyComparer());
 for (int i = 0; i < 100; i++)
    l.Add(new Tuple<string, int>(i.ToString(), i));
 for (int i = 0; i < 10; i++)
    l.Remove(new Tuple<string, int>(i.ToString(), default(int)));
 Console.WriteLine(l.Count);



успешно удаляет 10 элементов и выводит 90
Т.е. при удалении элементы равны, а при добавлении и Contains — нет
Что за двойные стандарты
Re: в SortedSet<T> баг?
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.04.11 18:14
Оценка: 29 (5) +3
Здравствуйте, g-host, Вы писали:

GH>Или баг в моих мозгах...Ничего не понимаю.

GH>Есть SortedSet<Tuple<string, int>>
GH>Я благополучно передаю в конструктор свой компаратор. Алгоритм сравнения очень прост,
GH>код компаратора даже нету смысла приводить:
GH>если строки равны возвращаем 0.
GH>иначе сравниваем числа, если и числа равны то просто string.CompareTo(x.Item1, y.Item2)
GH>Но, как же я удивился как например после того как в списке есть ("10", 8)
GH>то ("10", 0) все равно добавляется, несмотря на обещанное

Судя по всему, ваша реализация IComparer не обеспечивает транзитивности.
Re[2]: в SortedSet<T> баг?
От: g-host  
Дата: 25.04.11 18:33
Оценка:
Здравствуйте, samius, Вы писали:

S>Здравствуйте, g-host, Вы писали:


S>Судя по всему, ваша реализация IComparer не обеспечивает транзитивности.


Так оно и есть.
Но проблема в том что только такая реализация может обеспечить нужное мне поведение.
Да и смущает "правильная" работа Remove, ведь в нем также используется не линейный поиск.

Есть какие-либо альтернативы? Елементов на данный момент планируется хранить
порядка нескольких десятков тысяч. Я понимаю что это может быть даже не очень много, но все же
хотелось бы ускорить работу с коллекцией при таком алгоритме сравнения.
Re: в SortedSet<T> баг?
От: Lloyd Россия  
Дата: 25.04.11 18:37
Оценка: 6 (2) +1
Здравствуйте, g-host, Вы писали:

GH>Я благополучно передаю в конструктор свой компаратор. Алгоритм сравнения очень прост,

GH>код компаратора даже нету смысла приводить:
GH>если строки равны возвращаем 0.
GH>иначе сравниваем числа, если и числа равны то просто string.CompareTo(x.Item1, y.Item2)

Если я все правильно понял, то ваша реализация компарера некорректна. Компарер задает упорядочение элементов, при этом всегда должно выполняться
(t1 < t2) && (t2 < t3) => t1 < t3

Предложенная вами реализация не соблюдает этот инвариант:
using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        [STAThread]
        static void Main()
        {
            var cmp = new MyComparer();
            var t1 = Tuple.Create("A", 1);
            var t2 = Tuple.Create("B", 2);
            var t3 = Tuple.Create("A", 3);

            Console.WriteLine("t1 < t2: {0}", cmp.Compare(t1, t2) < 0);
            Console.WriteLine("t2 < t3: {0}", cmp.Compare(t2, t3) < 0);
            Console.WriteLine("t1 < t3: {0}", cmp.Compare(t1, t3) < 0);
        }
    }

    internal class MyComparer : IComparer<Tuple<string, int>>
    {
        public int Compare(Tuple<string, int> x, Tuple<string, int> y)
        {
            if (string.Equals(x.Item1, y.Item1))
                return 0;
            if (x.Item2 != y.Item2)
                return Comparer<int>.Default.Compare(x.Item2, y.Item2);
            return string.Compare(x.Item1, y.Item1);
        }
    }
}


Мне лень проверять деуствительно ли это причина, но я практически уверен, что это она и есть.
Re[3]: в SortedSet<T> баг?
От: Lloyd Россия  
Дата: 25.04.11 18:43
Оценка: 3 (1) +1
Здравствуйте, g-host, Вы писали:

S>>Судя по всему, ваша реализация IComparer не обеспечивает транзитивности.


GH>Так оно и есть.

GH>Но проблема в том что только такая реализация может обеспечить нужное мне поведение.

Вы уверены, что вам нужен именно SortedSet? Ведь ему по определению нужен способ упорядочения, а вы его предложить не можете.
Может вам больше подойдет HashSet?
Re[3]: в SortedSet<T> баг?
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.04.11 18:43
Оценка: 3 (1)
Здравствуйте, g-host, Вы писали:

GH>Здравствуйте, samius, Вы писали:


S>>Здравствуйте, g-host, Вы писали:


S>>Судя по всему, ваша реализация IComparer не обеспечивает транзитивности.


GH>Так оно и есть.

GH>Но проблема в том что только такая реализация может обеспечить нужное мне поведение.
Но такая реализация не может обеспечить корректной работы SortedSet
GH>Да и смущает "правильная" работа Remove, ведь в нем также используется не линейный поиск.
Стоит удалить 10 случайных элементов и наверняка будет различное поведение при повторных запусках.

GH>Есть какие-либо альтернативы? Елементов на данный момент планируется хранить

GH>порядка нескольких десятков тысяч. Я понимаю что это может быть даже не очень много, но все же
GH>хотелось бы ускорить работу с коллекцией при таком алгоритме сравнения.
Set<T> не подойдет?
Если нужен порядок, то требуется корректное отношение порядка. Хитрить тут можно только в рамках соблюдения свойств отношения полного порядка.
Re[4]: в SortedSet<T> баг?
От: g-host  
Дата: 25.04.11 20:06
Оценка:
Всем спасибо, свою ошибку насчет компаратора осознал

Для Eduals и GetHashCode в случае HashSet сходу реализацию не придумал,
слишком много требований должно выполняться — кроме того что список надо хранить
в отсортированном порядке для ускорения доступа, происходит периодическая подгрузка данных из внешнего источника,
и при этом с этими данными постоянно работает "клиентский" код, который также должен рапортовать
об актуальности полученных данных, опять же влияя тем самым на порядок элементов.
В любом случае, в контексте этого топика это уже оффтоп и отход от темы.
Re: в SortedSet<T> баг?
От: Аноним  
Дата: 17.05.11 13:24
Оценка:
Здравствуйте, g-host, Вы писали:

GH>Или баг в моих мозгах...Ничего не понимаю.

GH>Есть SortedSet<Tuple<string, int>>
GH>Я благополучно передаю в конструктор свой компаратор. Алгоритм сравнения очень прост,
GH>код компаратора даже нету смысла приводить:
GH>если строки равны возвращаем 0.
GH>иначе сравниваем числа, если и числа равны то просто string.CompareTo(x.Item1, y.Item2)
GH>Но, как же я удивился как например после того как в списке есть ("10", 8)
GH>то ("10", 0) все равно добавляется, несмотря на обещанное
GH>"Класс SortedSet<T>не принимает повторяющихся элементов. Если item уже присутствует в наборе, то этот метод возвращает значение false и не вызывает исключение"

SortedDictionary или SortedList не подойдёт?
Re[2]: в SortedSet<T> баг?
От: g-host  
Дата: 18.05.11 04:34
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, g-host, Вы писали:


А>SortedDictionary или SortedList не подойдёт?


Да, я на них уже смотрел, не помню почему именно но не подошли.
Проблема так-то уже давно решена с помощью обычного List<T>
используется сортировка вставками(с помощью BinarySearch с нужным компаратором и последующего Insert),
и иногда быстрая сортировка(при подгрузке данных, писал выше). Удалось добиться приемлемой скорости.
Re: в SortedSet<T> баг?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 31.05.11 11:41
Оценка:
Здравствуйте, g-host, Вы писали:

удя по
l.Add(new Tuple<string, int>(i.ToString(), i));
for (int i = 0; i < 10; i++)
l.Add(new Tuple<string, int>(i.ToString(), default(int)));

для компаратора new Tuple<string, int>(i.ToString(), i)<>new Tuple<string, int>(i.ToString(), default(int))

А значит компаратор уще и int? но как то странно, т.к должно получиться 109 (при i==0 должны бытьодинаковы
и солнце б утром не вставало, когда бы не было меня
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.