6

内でコレクションを構築する場合、コレクションOptionの次のメンバーを作成しようとするたびに失敗し、コレクション全体も失敗する可能性があります。Noneメンバーを作るのに最初に失敗したら、すぐにあきらめて、コレクション全体に戻りたいと思います。Scalaでこれを行う慣用的な方法は何ですか?

これが私が思いついた1つのアプローチです:

def findPartByName(name: String): Option[Part] = . . .

def allParts(names: Seq[String]): Option[Seq[Part]] =
  names.foldLeft(Some(Seq.empty): Option[Seq[Part]]) {
    (result, name) => result match {
      case Some(parts) =>
        findPartByName(name) flatMap { part => Some(parts :+ part) }
      case None => None
    }
  }

つまり、 return を呼び出すと、 return がfindPartByName返されます。それ以外の場合は、すべてが有効であることが保証されているのコレクションを含む を返します。空のコレクションは問題ありません。NoneallPartsNoneallPartsSomeParts

上記にはfindPartByName、最初の失敗の後に呼び出しを停止するという利点があります。ただしfoldLeft、関係なく、名前ごとに 1 回繰り返します。

findPartByNameを返すとすぐに救済するバージョンは次のNoneとおりです。

def allParts2(names: Seq[String]): Option[Seq[Part]] = Some(
  for (name <- names) yield findPartByName(name) match {
    case Some(part) => part
    case None => return None
  }
)

私は現在、2 番目のバージョンの方が読みやすいと感じていますが、(a) 最も読みやすいと思われるものは、Scala の経験を積むにつれて変わる可能性が高く、(b) Scala では早期returnは嫌われているという印象を受けます。(c) どちらもありません。何が起こっているのかを特に明白にしているようです。

「オール オア ナッシング」と「最初の失敗であきらめる」の組み合わせは、基本的なプログラミング概念のように思えます。それを表現するには、一般的な Scala または関数型イディオムが必要だと思います。

4

5 に答える 5

5

returnコード内の は、実際には無名関数の数レベルの深さです。そのため、外部関数でキャッチされる例外をスローして実装する必要があります。これは効率的でもきれいでもないため、眉をひそめています。

ループwhileIterator.

def allParts3(names: Seq[String]): Option[Seq[Part]] = {
  val iterator = names.iterator
  var accum = List.empty[Part]
  while (iterator.hasNext) {
    findPartByName(iterator.next) match {
      case Some(part) => accum +:= part
      case None => return None
    }
  }
  Some(accum.reverse)
}

の種類がわからないため、 or インデックスSeq namesを使用するのではなく、効率的にループするイテレータを作成する必要があります。tailwhile ループは末尾再帰内部関数に置き換えることができますが、反復子を使用するとwhileループがより明確になります。

于 2014-05-28T15:25:25.690 に答える