for 理解中的模式匹配分配如何转化为一元操作?
Posted
技术标签:
【中文标题】for 理解中的模式匹配分配如何转化为一元操作?【英文标题】:How do pattern match assignments in for comprehensions translate into monadic operations? 【发布时间】:2021-12-02 22:18:05 【问题描述】:我熟悉 Scala 的 for
推导式只是单元操作(map
、withFilter
、foreach
和 flatMap
)的语法糖这一概念,并且在 this popular answer 中描述了脱糖。
按照这种逻辑,我惊讶地发现,当使用模式匹配作为 for
理解的赋值部分的一部分时,当模式与元素不匹配时,不会抛出 MatchError
。而是过滤掉不匹配的元素:
case class Account(id: String, role: String)
val accounts = Set(Account("a", "ADMIN"), Account("b", "USER"), Account("c", "ADMIN"), Account("d", "USER"), Account("e", "USER"))
val adminIds = for (Account(id, "ADMIN") <- accounts) yield id
// Set("a", "c") (no MatchError on Account("b", "USER")!
我本来希望这种理解可以翻译成这样的:
val adminIds = accounts.map case Account(id, "ADMIN") => id
// or maybe
val adminIds = accounts.map account =>
val Account(id, "ADMIN") = account
id
当然,那些会抛出MatchError
。相反,它似乎更类似于:
val adminIds = accounts.collect case Account(id, "ADMIN") => id
但是,我从未见过任何提及 for
理解将糖分转化为 collect
调用。
那么这是如何在幕后完成的呢?
【问题讨论】:
不是collect
是对withFilter
的调用
在幕后,您的理解翻译为:val adminIds = accounts.withFilter case Account(id: String, "ADMIN") => true; case _ => false .map( case Account(id: String, "ADMIN") => id )
换句话说,由于集合的严格性(如链接答案中所述),它使用withFilter
。这会导致按需过滤。
@m_vemuri 您不妨发布此评论作为答案!
@CarloPrato 看起来像是有人从我这里偷走了这个特权。但我会继续发布它。我希望我的最初努力能得到回报。
【参考方案1】:
添加我的答案,最初是作为评论发布的。
在幕后,你的理解被翻译成:
val adminIds = accounts.withFilter
case Account(id: String, "ADMIN") => true;
case _ => false
.map(
case Account(id: String, "ADMIN") => id
)
换句话说,它使用的是withFilter
。
总结 linked answer,withFilter
是在 scala 2.8 中引入的,适用于严格集合(如 List
,而不是像 Stream
这样的非严格集合。)而不是返回一个新的过滤集合,它会按需过滤。
这就是为什么您在运行代码时不会注意到MatchError
。
【讨论】:
啊,我明白了。很高兴知道有这种默认情况。【参考方案2】:如果您将该代码放入 Intellij 和 desugar 之类的 IDE 中,您将得到类似的结果
val adminIds: Set[String] = accounts.withFilter case Account(id: String, "ADMIN") => true; case _ => false .map( case Account(id: String, "ADMIN") => id )
这不是收集电话
【讨论】:
以上是关于for 理解中的模式匹配分配如何转化为一元操作?的主要内容,如果未能解决你的问题,请参考以下文章