繁体   English   中英

如何在Scala中以单数Some特质递归地构造列表?

[英]How can you recursively construct a list inside of a singular Some trait in Scala?

我现在正在阅读红皮书《 Scala中的功能编程》,因此同时我正在学习Scala。 如果我理解正确,特质并不意味着对象。 如果我错了,请纠正我。

我的问题是我不知道如何将A类型的列表包装在Some特质内。 我会向正确的方向暗示。

在我正在做的练习中,要求我定义一个函数,该函数用于转换列表中的每个元素,然后将整个列表包含在Some特质内。

这是我的代码:

def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a match {
    case Nil => Nil: B
    case h :: t => f(h) flatMap ( hh => hh :: traverse(t)(f))
  }

我觉得我在这里的路正确,但是scala解释器抱怨::Option[List[B]]不可用。 我认为这是因为函数的类型签名不返回列表,而是返回包装在Some中的List。

但是我对flatMap的直觉也可能是错误的吗? f(h)返回一个Option[B] 调用flatmap实际上看起来在Option内部,所以B类型正确吗? 我的逻辑是,通过这种方式,我可以使用函数hh :: traverse(t)(f)构造类型B的列表。 但是我不确定是否会在这里。

如果有什么不同,我将scala解释器与:paste命令一起使用。 我不确定这是否会使事情变得不合时宜。

如果我理解正确,特质并不意味着对象。

特性是特质,对象是对象。 特性有点像Java中的“接口”,而object是单例对象。 这些单例对象可以从特征扩展。

Some特质

Some不是特征,它是扩展Option

它返回包装在Some中的列表。

它返回包装在Option内的列表(即,可以为None ,根本没有任何列表)。 为了在可选列表值上调用:: ,您需要另一个map

def traverse[A, B](a: List[A])(f: A => Option[B])
: Option[List[B]] = a match {
  case Nil => Some(Nil)
  case h :: t => f(h) flatMap {
    hValue => traverse(t)(f) map {
      tValue => hValue :: tValue
    }
  }
}

您可以将其缩写为:

def traverse[A, B](a: List[A])(f: A => Option[B])
: Option[List[B]] = a match {
  case Nil => Some(Nil)
  case h :: t => f(h) flatMap {
    hValue => traverse(t)(f) map (hValue :: _)
  }
}

或立即使用for -comprehension:

def traverse[A, B](a: List[A])(f: A => Option[B])
: Option[List[B]] = a match {
  case Nil => Some(Nil)
  case h :: t => for {
    hValue <- f(h)
    tValue <- traverse(t)(f)
  } yield (hValue :: tValue)
}

有关单子运算的更多信息。 Option是monad,但将其视为具有零或一个值的容器可能会更容易。 像没有元素的List()或只具有一个元素的List(x) 要修改隐藏在容器内的元素,您需要map

List(1).map(_ + 1) => List(2)
Some(1).map(_ + 1) => Some(2)

但是map不能更改列表中元素的数量。 因此,您需要的是另一个操作,称为flatMap ,该操作带有一个函数,该函数又带一个元素并返回List

List(1).flatMap { x => List() } => List()
Some(1).flatMap { x => None } => None

List(1).flatMap { x => List(x + 1) } => List(2)
Some(1).flatMap { x => Some(x + 1) } => Some(2)

这将传播“失败”,这是一个空列表,因为List().flatMap { ...whatever...}将始终返回一个空列表。

traverse就是这样做的,如果任何元素的函数f返回None (即我的解释中的List() ,则传播“失败”,并将非失败结果合并到容器内的新列表中。 由于从traverse获得的是内部包含列表的容器,因此需要应用map 并且由于应该传播“故障”(如果发生),因此需要flatMap

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM