2013-10-25 3 views
7

Мне часто кажется, что я хочу использовать методы Head и Tail для IEnumerables, которые не существуют как часть Linq. Хотя я мог легко написать свои собственные, я задаюсь вопросом, были ли они целенаправленно опущены. Например,Почему у Линка нет головы и хвоста?

var finalCondition = new Sql("WHERE @0 = @1", conditions.Head().Key, conditions.Head().Value); 
foreach (var condition in conditions.Tail()) 
{ 
    finalCondition.Append("AND @0 = @1", condition.Key, condition.Value); 
} 

Итак, что является лучшей практикой в ​​этом отношении с Linq? Является ли тот факт, что я продолжаю находить использование для этого признака того, что я не делаю что-то рекомендованное? Если нет, то почему эта общая функциональная парадигма не реализована в Linq?

+4

Первый() и последний() или FirstOrDefault() – Laurijssen

+0

Книга Оливера Штурма [Функциональное программирование на C# _] (http://eu.wiley.com/WileyCDA/WileyTitle/productCd-0470744588.html) имеет много загружаемого исходного кода. Если я правильно помню, это также содержит методы расширения для головы и хвоста. –

+1

@GertArnold У меня есть проблемы с ними, вопрос о том, считается ли это лучшей практикой. –

ответ

7

Учитывая интерфейс IEnumerable<T>, производительность не всегда может быть гарантирована.

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

IEnumerable<T> не имеет таких ограничений, и поэтому нельзя предположить, что это было бы эффективно.

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

Если вы сделали это с Entity Framework, например, вы бы отправить следующий (мета) вызов SQL-сервера, плотный цикл.

Select * from 
(
    Select * from 
    (
     Select * from 
     (...) 
     Skip 1 
    ) 
    Skip 1 
); 

Это было бы очень неэффективно.

EDIT:

Подумайте об этом. Другая причина заключается в том, что C#/VB.Net не поддерживает хвостовую рекурсию, и, таким образом, этот шаблон может легко вызвать StackOverflow.

+0

+1 для справки 'StackOverflow' (и хороший ответ). –

+0

Этот ответ неверен и не имеет значения. Его не следует принимать. Хвост 'IEnumerable' легко доступен [' .Skip (1) '] (https://msdn.microsoft.com/library/bb358985 (v = vs.100) .aspx), и нет никаких неожиданностей производительности, независимо от того, структура находится в памяти или нет. Классы 'IEnumerable' * * предназначены для перемещения по одному элементу за раз, и это все' Skip (1) 'does. Перспективы производительности возникают только тогда, когда вам нужно подсчитать «IEnumerable» или перейти к элементу в произвольном «индексе». – kdbanman

+0

@kdbanman skip и Enumerator.GetNext() - это разные вещи. – Aron

13

Технически, ваша голова будет .First(), а ваш хвост будет .Skip(1). Но, может быть, вы сможете найти лучшее решение? Как использовать .Aggregate() на вашем IEnumerable?

+2

@MatthewWatson Нет, 'tail' не является последним элементом. – sloth

+0

О, ну, хвост - это остаток после головы, правильно? –

+3

@MatthewWatson Да. Дело с головой/хвостом в том, что вы не называете head * before * tail, это скорее концепция разделения списка. nvoigt прав, говоря, что код OP эквивалентен вызову first/skip (1), и это не хороший пример использования head/tail. – sloth

1

Потому что концепция «Head & Tail» используется в функциональном программировании для pattern matching и рекурсивных вызовах. Поскольку C# не поддерживает сопоставление шаблонов, тогда нет необходимости реализовывать методы head() и tail().

let rec sum = function 
    | [] -> 0 
    | h::t -> h + sum t 

Что касается вашего случая - вы должны использовать Aggregate метод.

Смежные вопросы