Причина как раз в том, что Seq
"ленив", как вы говорите.
"Лени" в том смысле, что он оценивается каждый раз, когда вы его просите.Все это.До последней не ленивой вещи.
В частности, звонок на Seq.map
ленив.Это не создает новую структуру в памяти, которая полна словарей.Вместо этого он создает то, что вы могли бы назвать «конвейером».Этот конвейер начинается с вашего списка ["a"; "b"; "c"]
, а затем есть инструкция: каждый раз, когда кто-то пытается перебрать эту последовательность, создайте новый словарь для каждого элемента.Здесь важен бит «каждый раз» - поскольку вы повторяете последовательность дважды (один раз для печати «здесь» и другой раз для печати значений), словари также создаются дважды.Словарь, в который вы помещаете «что-то», и словарь, из которого вы получаете «ключ», - это не один и тот же словарь.
Чтобы проиллюстрировать это, попробуйте следующее:
let s = ["a";"b";"c"] |> Seq.map( fun x -> printfn "got %s" x; x )
s |> Seq.iter(printfn "here's %s")
s |> Seq.iter(printfn "again %s")
Это напечатаетследующее:
got a
here's a
got b
here's b
got c
here's c
got a
again a
got b
again b
got c
again c
Видите, как вывод "got" происходит дважды для каждого элемента?Это потому, что Seq.map
работает при каждой итерации, а не только один раз.
Не так со списками.Каждый раз, когда вы List.map
, вы создаете новый список в памяти.Он просто сидит там навсегда (где «навсегда» определено «до тех пор, пока сборщик мусора не доберется до него») и ждет, когда вы что-то с ним сделаете.Если вы делаете несколько вещей с ним, это все тот же список, он не создается заново.Вот почему ваши словари всегда одни и те же словари, они не создаются заново, как в Seq
.Вот почему вы можете изменить их и увидеть изменения в следующий раз, когда вы посмотрите.
Вы можете добиться аналогичного, но не совсем идентичного эффекта с последовательностями с помощью Seq.cache
.Эта функция принимает обычную последовательность оценки по требованию и возвращает идентичную последовательность, за исключением того, что каждый элемент оценивается только один раз.
В отличие от списка, Seq.cache
не будет оценивать последовательность целом в тот момент, когда она вызывается.Вместо этого он создаст изменяемый кеш, который обновляется каждый раз, когда вы оцениваете.
Это полезно в случаях, когда последовательность очень большая или даже бесконечная, но вам нужно работать только с небольшим конечным числомэлементов в начале.
Иллюстрация:
let s = ["a";"b";"c"]
|> Seq.map( fun x -> printfn "got %s" x; x )
|> Seq.cache
s |> Seq.iter(printfn "here's %s")
s |> Seq.iter(printfn "again %s")
Вывод:
got a
here's a
got b
here's b
got c
here's c
again a
again b
again c