Haskell использует стратегию реализации, известную как «стирание типов»: типы не имеют вычислительного значения, поэтому код, который вы генерируете, не должен их отслеживать. Это значительное преимущество для производительности.
Цена, которую вы платите за это преимущество в производительности, заключается в том, что типы не имеют вычислительной значимости: функция не может изменить свое поведение в зависимости от типа аргумента, который ей передан. Если бы вы допустили что-то вроде
f () = "foo"
f [] = "bar"
, то это свойство не было бы истинным: поведение f
действительно зависело бы от типа его первого аргумента.
Конечно, есть языки, которые допускают подобные вещи, особенно в языках с типизированной зависимостью, где типы вообще не могут быть стерты.
Я бы начал с
var res = seq1.Union(seq2, Comparer);
, но документация для Enumerable.Union
не указывает, из какой последовательности взят соответствующий элемент, и требует Comparer
должен быть реализацией IEqualityComparer
, которого у вас не будет, если TSource
является анонимным типом (но, возможно, стоит конкретизировать, чтобы разрешить это).
В противном случае вам нужно будет реализовать собственный помощник, например:
IEnumerable<KeyValuePair<TKey, TValue>> Union(
this IEnumerable<KeyValuePair<TKey, TValue>> first,
IEnumerable<KeyValuePair<TKey, TValue>> second) {
var allFirst = first.ToDictionary(v => v.Key, v => v.Value);
foreach (var kv in allFirst) { yield return kv; }
foreach (var s in second) {
if (!allFirst.ContainsKey(s.Key)) {
yield return s;
}
}
(Если вы хотите придерживаться своего собственного типа (свойства x и y), вы можете сделать этот универсальный параметр для одного параметра типа, но вам нужно будет добавить ограничение типа для предоставления доступа к отдельным свойствам.)
Возможно, вам будет проще сделайте это без Linq, но я думаю примерно так:
var distinctWithPrecedence = seq1
.Concat(seq2)
.GroupBy(i => i.x)
.Select(g => g.First());
Если Linq не так уж много делает из-за своих укрытий. Основная идея - группировка по ключу. Затем пройдите по группам, вытягивая по первому элементу из каждой. Если Linq ведет себя так, как я думаю, первым элементом будет первый элемент из объединенного списка, что означает, что первая последовательность имеет прецедент.
Возможно, есть другие способы сделать это, но это первое, что пришло в голову.