2

皆さん、私は Scala の学習を強化するためだけに、本からいくつかの Java コード サンプルを慣用的な 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 "; ")
  }

しかし、ここからが難しい部分です。間違ったホスト名を入力したときに発生する例外を簡単に処理したいのです。新しいTry構成を使用できますが、問題を回避するだけです。

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

上記のスニペットでは、ホスト名が不適切な場合、エントリはスキップされ、問題はありません。しかし、私がやりたいのは、悪いホストの場合、 のようなタプルを取得することです(www.hostname.com, Bad host name)。などをいじってみましOptionたが、まだ解読する資格がないコンパイル時エラーが発生します。誰かが簡潔で、Scala が提供するすべての機能を使用する慣用的なソリューションを提案できますか? ありがとう。

4

3 に答える 3

3

私が最初に意図したことは、次のように終わることでした:

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 ";")
}

これはほとんどエレガントなコードではありません。

の使用法を維持する「機能的な」再設計を次に示しますTry

def printHostInfo1(args: Seq[String]) = {
  def hostToTuple(inet: InetAddress) = (inet.getHostName, inet.getHostAddress)

  val hosts = args.flatMap(arg =>
                Try(InetAddress.getAllByName(arg.trim).map(hostToTuple(_)))
                                getOrElse Array((arg.trim,"Bad host name")))

  println(hosts 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 ";")
}
于 2013-07-27T16:16:25.427 に答える
1

次を返すメソッドを定義できますEither

def getAllInetAddressesByName(h: String): Either[Exception, List[InetAddress]] ={
  try {
    Right(InetAddress.getAllByName(h).toList)
  } catch {
    case e: UnknownHostException => Left(e)
  }
}

例外またはアドレスのいずれかを返します。Arrayこのメソッドは、可変から不変にも変換しListます。これは、Java API (例外/配列を使用) から機能データ型へのブリッジです。

このパターン ( を使用) では、マッピング中または理解のために後でEither必要ありません。Try


したがって、この一般的な方法に加えて、次のmapようなさまざまな結果タイプを作成できます。

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")
  }

または使用してfor

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

別の例:collect良いものだけが必要な場合:

val result = hosts.map(h => (h, getAllInetAddressesByName(h))).collect {
  case (h, Right(addresses)) => 
    (h, addresses.map(a => a.getHostAddress).mkString("/"))
}

スクロールを避けるために、コード スニペットを少し再フォーマットする必要がありました。

于 2013-07-27T19:58:32.393 に答える