7

私はScalaに非常に慣れておらず、構文とスタイルに慣れようとしているので、これはおそらく非常に単純な質問です。

次のようなオプションが設定されたケースクラスがたくさんあるコードベースで作業しています。

case class Person(
  pants: Option[Pants]
)
case class Pants(
  pocket: Option[Pocket]
)
case class Pocket(
  cash: Option[Cash]
)
case class Cash(
  value: String = "zilch"
)

上記の例では、彼らが実際にズボンを着用している場合、ポケット付きで、お金がある場合、どのようにお金を返すPersonのですか?Pants Pocket

4

4 に答える 4

12

Scalaz 7が少し変更されたので、別の例を示します。

  object PartialLensExample extends App {

  import scalaz._
  import Lens._
  import PLens._


  case class Bar(blub: Option[String])
  case class Foo(bar: Option[Bar])

  // normal lenses for getting and setting values
  val fooBarL: Foo @> Option[Bar] = lensg(foo ⇒ bar ⇒ foo.copy(bar = bar), _.bar)
  val barBlubL: Bar @> Option[String] = lensg(bar ⇒ blub ⇒ bar.copy(blub = blub), _.blub)

  // compose the above as 'Partial Lenses', >=> is just an alias for 'andThen'
  val fooBarBlubL: Foo @?> String = fooBarL.partial >=> somePLens >=> barBlubL.partial >=> somePLens

  // try it
  val foo = Foo(Some(Bar(Some("Hi"))))

  println(fooBarBlubL.get(foo)) // Some(Hi)

  println(fooBarBlubL.set(foo, "Bye")) //Foo(Some(Bar(Some(Bye))))

  // setting values
  val foo2 = Foo(None)
  println(fooBarL.set(foo2, Some(Bar(None)))) // Foo(Some(Bar(None)))

}
于 2013-06-05T20:31:19.060 に答える
8

理解のための素晴らしい時間:

val someCash: Option[Cash] =
   for( pants  <- somePerson.pants;
        pocket <- pants.pocket;
        cash   <- pocket.cash ) yield cash

同様に、次のように書くことができます。最初のコードは構文糖衣です(いくつかの微妙な点を無視します)。

val someCash: Option[Cash] = 
   somePerson.pants.flatMap(_.pocket.flatMap(_.cash))

_(私が行ったように、ワイルドカードを使用して最後の式を記述できるかどうかは完全にはわかりません)。

于 2012-01-20T16:05:59.803 に答える
6

質問にはデータの変更については触れられていませんでしたが、これを行う必要がある場合、Scalaライブラリにはこれを簡単にするツールがないことがすぐにわかります(データが不変の場合)。これをまだ経験していない場合は、質問で定義されたタイプを使用して、によって保持されているvalueのを置き換える、または変更する関数を作成してみてください。CashPerson

Tony MorrisのScalaの非対称レンズで説明されているように、レンズはこの問題の適切な解決策です。

これは、Scalazのscalaz-sevenブランチからのおよび(部分レンズ)実装を使用してvalue、人のにアクセスして更新する方法の例です。CashLensPLens

まず、いくつかの定型文:ケースクラスの各フィールドのLensインスタンスを定義します。A @-@ Bと同じ意味Lens[A, B]です。

val pants: Person @-@ Option[Pants] =
  lensG(_.pants, p => ps => p.copy(pants = ps))

val pocket: Pants @-@ Option[Pocket] =
  lensG(_.pocket, ps => p => ps.copy(pocket = p))

val cash: Pocket @-@ Option[Cash] =
  lensG(_.cash, p => c => p.copy(cash = c))

val value: Cash @-@ String =
  lensG(_.value, c => v => c.copy(value = v))

ただし、ほとんどのフィールドがOptionタイプでラップされているため、これらのレンズのすべてを構成することはできません。

救助のための部分レンズ:これらにより、の値やの値など、存在しない可能性のある構造の部分にアクセスして更新することができます。SomeOptionheadList

somePLensScalaz 7の機能を使用して、各オプションフィールドを表示する部分レンズを作成できます。ただし、通常のレンズの1つで部分レンズを構成するには、partialすべてに存在する方法を使用して、通常のレンズと同等の部分レンズインスタンスにアクセスする必要がありますLens

// @-? is an infix type alias for PLens
val someCash: Pocket @-? Cash = cash.partial andThen somePLens

scala> someCash.get(Pocket(Some(Cash("zilch"))))
res1: Option[Cash] = Some(Cash(zilch))

Person同様に、すべてのレンズのpartialインスタンスを構成し、のインスタンスをサンドイッチすることで、が保有する現金を表示する部分レンズを作成できますsomePLens。ここでは<=<、のエイリアスである演算子を使用しました(これは、オペランドを切り替えたandThen場合と同等です)。compose

val someCashValue: Person @-? String =
  pants.partial <=< somePLens <=<
  pocket.partial <=< somePLens <=<
  cash.partial <=< somePLens <=<
  value.partial

Person遊ぶインスタンスの作成:

val ben = Person(Some(Pants(Some(Pocket(Some(Cash("zilch")))))))

部分レンズを使用して、私が持っている現金の価値にアクセスします。

scala> someCashValue.get(ben)
res2: Option[String] = Some(zilch)

部分レンズを使用して値を変更する:

scala> someCashValue.mod(_ + ", zero, nada", ben)
res3: Person = Person(Some(Pants(Some(Pocket(Some(Cash(zilch, zero, nada)))))))

これで、ズボンを着用していない場合(!)、現金の価値を変更しようとしても効果がないことがわかります。

scala> val ben = Person(None)
ben: Person = Person(None)

scala> someCashValue.mod(_ + ", zero, nada", ben)
res4: Person = Person(None)
于 2012-04-02T14:44:34.827 に答える
2

ziggystarの答えは私が使用するものですが、完全を期すために、パターンマッチングも使用できます。

val someCash: Option[Cash] = person match {
  case Person(Some(Pants(Some(Pocket(Some(cash)))))) => Some(cash)
  case _ => None
}
于 2012-01-23T07:22:51.320 に答える