【问题标题】:Seq.map and Seq.mapi optimize away side effects when anonymous function returns unit?当匿名函数返回单位时,Seq.map 和 Seq.mapi 优化了副作用?
【发布时间】:2015-07-12 22:37:18
【问题描述】:

在调试一个记忆函数时,我发现自己试图找出 Seq.mapi 是否真的在迭代一个序列,所以我用 printfn 调用替换了一个匿名函数,如下所示:

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

令我惊讶的是,打印到控制台的副作用从未发生过,在 FSI 中它只是返回 val d : seq<unit>。虽然这个结果是正确的,但我预计当map 函数迭代序列时会出现副作用。即,当我将其替换为 Seq.map id 时,它会按预期工作并返回自身。

当我将Seq.mapSeq.mapi 替换为Seq.iterSeq.iteri 时,它实际上会打印出副作用。

现在我认为这是由于序列被延迟评估,并且 F# 在内部创建了一个闭包序列,或者实际上在它被调用之前什么都不做,因为如果我这样做 @987654331 @,它确实打印序列中的所有项目。

【问题讨论】:

  • 认为正确;)
  • 您还可以通过添加行 for item in x do () 来说服自己相信它的惰性评估,这将强制它评估项目(并将输出吐到控制台)
  • @AdamKewley 另一种说服自己这种行为不是对返回单位的函数的优化的方法是尝试使用不返回单位的函数,如下所示:let x = "test" |> Seq.map (fun c -> c |> printfn "%c"; (char (int c + 1)).ToString());然后这个let y = String.concat "" x

标签: dictionary f# seq side-effects


【解决方案1】:

F# 中的seqIEnumerable 的别名,因此Seq 模块函数大多等同于Linq。是的,它们被懒惰地评估。

【讨论】:

  • 谢谢,显然我的假设是正确的,考虑到所有的事情,我认为这是典型的编码失明时刻。如果不是因为我对 unit 结果优化的错误猜测,正如 phoog 提到的通过对函数的简单更改进行检查,我会更快看到它。
  • PS:我认为将Seq 视为IEnumerable 的别名是不正确的。是的,它通过计算构建器实现了IEnumerable,但这并不能使其成为别名。
  • @Abel Seq 从字面上看是IEnumerable<T> 接口的别名。这是 Microsoft.FSharp.Collections 命名空间中 100% 的源代码:type seq<'T> = IEnumerable<'T>。计算构建器只是生成实现此接口的对象的一种便捷方式。
  • @JoelMueller,我想我在评论中感到困惑/困惑。 seqIEnumerable<T> 的别名(而不是 IEnumerable)。 Seq 定义了 moduleSeq.map 该模块中的函数。也许太挑剔了,我在考虑之前写了因为我的陈述不正确,我的评论提到Seq,但文字是关于seq,然后是的,它只是一个类型别名。我认为回答者的意思是说seqIEnumerable<T>。感谢您让我保持警惕。
  • 是的,答案中应该是seq<T>。我会纠正这个。谢谢!
猜你喜欢
  • 2013-11-16
  • 2013-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-02
  • 1970-01-01
相关资源
最近更新 更多