我如何将这个丑陋的衬垫转换为干净的 scala 以供理解?

How do I convert this ugly one liner into a clean scala for comprehension?

嘿伙计们,我得到了这个丑陋的东西:

val test = Some(Map("TesT",123))
val keys = test.getOrElse(Map()).keys.map(_.toLowerCase).asInstanceOf[Set[String]]
require(keys.contains("test")

我可以将它(第 2 行)变成 clean/readable 以便理解吗?

这是我的尝试:

scala> val keys = for {
     |       map <- test
     |       keys <- map.keys
     |       k <- keys
     | } yield k.toLowerCase
<console>:18: error: value toLowerCase is not a member of Char
       } yield k.toLowerCase
                 ^
<console>:16: error: type mismatch;
 found   : Iterable[Char]
 required: Option[?]
             keys <- map.keys
                  ^

理解只是map/flatMap的语法糖。 Option returns 另一个OptionflatMap 方法。要获得多个值,您应该从序列开始,所以第一行应该是 map <- test.toSeq。这解释了第二条错误消息。

理解中的第二行尝试使用 flatMap 访问 keys。它可以通过两种方式修复。要么用 val keys = map.keys 替换它,这样 mapflatMap 都不涉及,或者完全删除此行以调用第三个:k <- map.keys。这将修复第一个错误。

因此您可以在两种解决方案之间进行选择:

val keys = for {
       map <- test.toSeq
       val keys = map.keys
       k <- keys
 } yield k.toLowerCase

 val keys = for {
       map <- test.toSeq
       k <- map.keys
 } yield k.toLowerCase

你原来的:

val keys = for {
             map <- test
             keys <- map.keys
             k <- keys
       } yield k.toLowerCase

for comprehension 优于 flatmapmap 的一个限制是它始终需要 same kind of enumerator(真正的 monad),首先建立哪种。在您的示例中,第一个 enumerator 是一个 Option,因此它期望 map.keyskeys 也是 Option 类型.

您的示例的另一个问题是 keys 实际上是从集合 map.keys 中提取的单个 key... 因此 k 将引用一个enumuratorkeys: String.

中的字符

通过将 for 理解的初始类型转换为 Seq 来解决 for-comprehension 类型问题,允许以下术语属于那个类型(他们是),然后在提取 key:

后停止
val keys = for {
        map <- test.toSeq
        key <- map.keys
    } yield key.toLowerCase

你不需要理解这个。 清楚地对操作进行排序,并明确地拼出步骤通常更清晰易读。只是不要试图将内容塞进一行:

 test
   .iterator
   .flatMap(_.keys)
   .map(_.toLowerCase)
   .contains("test")