Scala 在for表达式中使用默认值处理异常

Scala 在for表达式中使用默认值处理异常,scala,exception,Scala,Exception,各位,我一直在尝试将书中的一些Java代码示例转换为惯用的Scala,以加强我对Scala的学习。我对小东西很满意,但在使用for表达式时无缝地处理异常让我感到困惑 前提是:给定主机名列表,检索主机名/ip地址元组列表。听起来很简单,适用于好的情况,例如 def printHostInfo(args: Array[String]) { val tuples = for { arg <- args inet <- InetAddress.getAll

各位,我一直在尝试将书中的一些Java代码示例转换为惯用的Scala,以加强我对Scala的学习。我对小东西很满意,但在使用
for
表达式时无缝地处理异常让我感到困惑

前提是:给定主机名列表,检索主机名/ip地址元组列表。听起来很简单,适用于好的情况,例如

  def printHostInfo(args: Array[String]) {
    val tuples = for {
      arg <- args
      inet <- InetAddress.getAllByName(arg.trim)
    } yield (inet.getHostName, inet.getHostAddress)
    println(tuples mkString "; ")
  }

在上面的代码片段中,如果主机名不正确,则跳过该条目,我们都很高兴。但是,我想做的是,如果是坏主机,检索一个元组,如
(www.hostname.com,坏主机名)
。我试着乱搞
选项
和其他东西,但我得到了编译时错误,我还没有资格破译这些错误。有人能推荐一个简洁且使用Scala提供的全部功能的惯用解决方案吗?谢谢。

经过一番思考,我终于找到了一个可行的解决方案,不过我非常感谢您的改进:

  def printHostInfo(args: Array[String]) {
    val tuples = for {
      arg <- args
      inet <- Try(InetAddress.getAllByName(arg.trim) map
                      (Some(_))) getOrElse Array(None)
    } yield (inet.map(_.getHostName) getOrElse arg,
      inet.map(_.getHostAddress) getOrElse "Host Not Found")
    println(tuples mkString "\n")
  }
def printHostInfo(参数:数组[字符串]){ val tuples=for{
arg我最初的意思是这样结束:

def printHostInfo(args: Array[String]) = {
    val tuples = for {
      arg <- args
    } yield Try[Seq[(String,String)]](InetAddress.getAllByName(arg.trim)
        .map(inet => (inet.getHostName, inet.getHostAddress))) getOrElse List((arg.trim,"Bad host name"))
    println(tuples.flatten mkString ";")
}
一般来说,我不知道我现在是否已经清楚地解释了这一点,但我的观点是,您应该尽可能地推迟异常处理。在我看来,您最终遇到的问题是,您处理异常太早,现在您被类型系统所困,阻碍而不是帮助您(注意:在使用动态类型的语言(如Python)中,这种情况不会成为问题)

因此,这里有一个简单的迭代备选方案:

def printHostInfo3(args: Array[String]) {
    val tuples = for(arg <- args)
        yield try {
            for(inet <- InetAddress.getAllByName(arg.trim))
                yield (inet.getHostName, inet.getHostAddress)
        } catch  {
            case e: Exception => Array((arg.trim, "Bad host name"))
        }

    println(tuples.flatten mkString ";")
}
def printHostInfo3(参数:数组[字符串]){
val tuples=for(arg您可以定义一个返回
的方法:

def getAllInetAddressesByName(h: String): Either[Exception, List[InetAddress]] ={
  try {
    Right(InetAddress.getAllByName(h).toList)
  } catch {
    case e: UnknownHostException => Left(e)
  }
}
它返回异常或地址。此方法也从可变的
数组
转换为不可变的
列表
。它是Java API(使用异常/数组)到函数数据类型的桥梁

使用此模式(使用
),您不需要稍后在映射过程中或在理解过程中尝试


因此,在这个通用方法的基础上,您可以
映射到不同的结果类型,例如:

val hosts = List("stackoverflow.com", "sdfsdf.sdf", "google.com")

val result = hosts.
  map(host => (host, getAllInetAddressesByName(host))).
  map {
    case (host, Right(addresses)) =>
      (host, addresses.map(a => a.getHostAddress).mkString("/"))
    case (host, Left(ex)) =>
      (host, s"Host $host not found")
  }
或对

val result = for {
  host <- hosts
} yield {
  (host,
    getAllInetAddressesByName(host).fold(
      ex => s"Host $host not found",
      addresses => addresses.map(a => a.getHostAddress).mkString("/")))
}

我不得不重新格式化代码片段以避免滚动。

为什么不将地址解析移到
yield
表达式?@TheTerribleSwiftTomato:我不太明白,也许你可以发布一个示例?最终能够解决这个问题,作为答案发布。
val hosts = List("stackoverflow.com", "sdfsdf.sdf", "google.com")

val result = hosts.
  map(host => (host, getAllInetAddressesByName(host))).
  map {
    case (host, Right(addresses)) =>
      (host, addresses.map(a => a.getHostAddress).mkString("/"))
    case (host, Left(ex)) =>
      (host, s"Host $host not found")
  }
val result = for {
  host <- hosts
} yield {
  (host,
    getAllInetAddressesByName(host).fold(
      ex => s"Host $host not found",
      addresses => addresses.map(a => a.getHostAddress).mkString("/")))
}
val result = hosts.map(h => (h, getAllInetAddressesByName(h))).collect {
  case (h, Right(addresses)) => 
    (h, addresses.map(a => a.getHostAddress).mkString("/"))
}