Seq.map и Seq.mapi оптимизируют побочные эффекты, когда анонимная функция возвращает единицу?

Во время отладки функции запоминания я обнаружил, что пытаюсь выяснить, действительно ли Seq.mapi повторяет последовательность, поэтому я заменил анонимную функцию вызовом printfn следующим образом:

let x = "test" |> Seq.map  (fun c -> c |> printfn "%c")

К моему удивлению, побочный эффект печати на консоль никогда не возникал, в FSI он просто возвращал val d : seq<unit>. Хотя этот результат верен, я ожидал, что побочный эффект сработает, когда функция map перебирает последовательность. То есть, когда я заменяю его на Seq.map id, он работает как положено и возвращается сам.

Когда я заменяю Seq.map или Seq.mapi на Seq.iter или Seq.iteri, побочный эффект действительно печатается.

Теперь я думаю, что это связано с ленивым вычислением последовательности и тем, что F# внутри либо создает последовательность замыканий, либо вообще ничего не делает, пока не будет вызван, потому что, если я сделаю Seq.last d, это выводит все элементы последовательности.


person Abel    schedule 03.05.2015    source источник
comment
вы думаете правильно ;)   -  person Random Dev    schedule 03.05.2015
comment
Вы также можете убедиться в его ленивой оценке, добавив строку for item in x do (), которая заставит его оценивать элементы (и выводить вывод на консоль).   -  person Adam Kewley    schedule 04.05.2015
comment
@AdamKewley еще один способ убедить себя, что такое поведение не является оптимизацией для функций, возвращающих единицу, — попробовать функцию, которая не возвращает единицу, например: let x = "test" |> Seq.map (fun c -> c |> printfn "%c"; (char (int c + 1)).ToString()); тогда это let y = String.concat "" x   -  person phoog    schedule 04.05.2015


Ответы (1)


seq в F# — это псевдоним для IEnumerable, поэтому функции модуля Seq в основном эквивалентны Linq. И да, они оцениваются лениво.

person Petr    schedule 03.05.2015
comment
Спасибо, видимо, мое предположение было верным, и, учитывая все обстоятельства, я думаю, что это был момент типичной слепоты кодирования. Если бы не мое неправильное предположение об оптимизации результатов unit, как упомянул phoog для проверки простым изменением функции, я бы увидел это раньше. - person Abel; 05.05.2015
comment
PS: я не думаю, что правильно считать Seq псевдонимом IEnumerable. Да, он реализует IEnumerable через построитель вычислений, но это не делает его псевдонимом. - person Abel; 05.05.2015
comment
@Abel Seq буквально и явно является псевдонимом интерфейса IEnumerable<T>. Вот 100% исходного кода для него в пространстве имен Microsoft.FSharp.Collections: type seq<'T> = IEnumerable<'T>. Построитель вычислений — это просто удобный способ создания объекта, реализующего этот интерфейс. - person Joel Mueller; 05.05.2015
comment
@JoelMueller, кажется, я запутался/запутался в своем комментарии. seq — это псевдоним IEnumerable<T> (а не IEnumerable). Seq определил модуль и Seq.map функцию в этом модуле. Возможно все слишком придирчиво, и я написал не подумав, так как мое высказывание не верное, в моем комментарии упоминается Seq, но текст про seq и тогда да, это просто псевдоним типа. Я так понимаю, ответчик хотел сказать seq и IEnumerable<T>. Спасибо, что держишь меня в курсе. - person Abel; 06.05.2015
comment
Да, в ответе должно быть seq<T>. Я исправлю это. Спасибо! - person Petr; 06.05.2015