Операция LINQ Concat()
ни в коей мере не эквивалентна функциональной операции cons
.Они очень разные.Для каждой операции «против» создается «новый» список.Это на самом деле быстро, так как структуры данных в игре разработаны специально для этого использования.Для каждой операции Concat создается новый итератор, который не предназначен для такого использования.Чтобы проиллюстрировать, что делается каждый раз, рассмотрим следующий более короткий пример:
1:2:3:[]
Функциональная операция состоит из нескольких шагов для оценки.3:[]
приводит к временному списку [3]
, затем 2:[3]
приводит к спискам [2,3]
и 1:[2,3]
.Это 3 простых шагов только для создания списка.Чтобы «перебрать» его, потребуется некоторая рекурсия и сопоставление (или любой другой эквивалент Хаскелла).Это займет где-то около 3 или 4 дополнительных сложных шагов (по одному для каждого сегмента списка).
new[] { 1 }.Concat(new[] { 2 }).Concat(new[] { 3 });
Операция LINQ также состоит из нескольких шагов для оценки.new[] { 1 }.Concat(new[] { 2 })
выдает новый итератор, повторяющий последовательность [[1]+[2]]
, а последний выдает новый итератор, повторяющий последовательность [[[1]+[2]]+[3]]
.Для создания итератора потребовалось два простых шага, но вы должны заметить, насколько сложна последовательность на самом деле.Здесь используются 5 итераторов, по одному для каждого массива (3) и по одному для каждой объединенной пары (2).Я не буду проходить все этапы итерации, но при этом выполняется гораздо больше вызовов функций и доступа к свойствам (как того требует каждый итератор).
new[] { 1 }.Concat(new[] { 2 }.Concat(new[] { 3 }))
Операция LINQ, которая выглядит более структурно эквивалентной, снова принимаеттакое же количество шагов для создания, получая [[1]+[[2]+[3]]]
с тем же количеством итераторов и столь же сложными итерациями, хотя это и так.Ну, это не потому, что так делается на функциональных языках, и они оптимизированы для этого использования.У них есть преимущество использования неизменяемых последовательностей, которые могут быть повторно использованы в других составных списках.Генерация сложных последовательностей с использованием LINQ таким образом не совсем то, для чего она была разработана.По языку нет оптимизаций (некоторые по JIT, но это все), и это явно не то, как вы хотите перебирать последовательность.ИМХО прямо в голову, потому что это сложно.
Я думаю, что лучший подход в попытке повысить производительность - создать связанные списки для представления объединенной последовательности.Вы можете использовать класс LinkedList<T>
, создать свои собственные структуры данных, напоминающие функциональный список, или, что еще лучше, использовать F # FSharpList<T>
.Затем добавьте методы расширения и другие вспомогательные классы.