[解決済み] flatMap/Map変換のfor-comprehensionで迷う。
質問
私は本当にMapとFlatMapを理解していないようです。私が理解していないのは、for-comprehensionがmapとflatMapへのネストされた呼び出しのシーケンスであることです。次の例は Scalaで学ぶ関数型プログラミング
def bothMatch(pat:String,pat2:String,s:String):Option[Boolean] = for {
f <- mkMatcher(pat)
g <- mkMatcher(pat2)
} yield f(s) && g(s)
は以下のように変換されます。
def bothMatch(pat:String,pat2:String,s:String):Option[Boolean] =
mkMatcher(pat) flatMap (f =>
mkMatcher(pat2) map (g => f(s) && g(s)))
mkMatcherメソッドは以下のように定義されています。
def mkMatcher(pat:String):Option[String => Boolean] =
pattern(pat) map (p => (s:String) => p.matcher(s).matches)
そして、パターンメソッドは次のようになります。
import java.util.regex._
def pattern(s:String):Option[Pattern] =
try {
Some(Pattern.compile(s))
}catch{
case e: PatternSyntaxException => None
}
ここでmapとflatMapを使う根拠について、誰かが光を当ててくれると嬉しいのですが。
どのように解決するのですか?
TL;DR 最終例へ直接進む
再録してみる
定義
この
for
の内包は、構文のショートカットで
flatMap
と
map
を読みやすく、推論しやすくしたものです。
物事を少し単純化して、すべての
class
を呼び出すことができると仮定します。
monad
という記号を使用します。
M[A]
を意味するために
monad
を意味し、内部型は
A
.
例
よく見かけるモナドには以下のようなものがあります。
-
List[String]
ここで-
M[X] = List[X]
-
A = String
-
-
Option[Int]
ここで-
M[X] = Option[X]
-
A = Int
-
-
Future[String => Boolean]
ここで-
M[X] = Future[X]
-
A = (String => Boolean)
-
map と flatMap
汎用モナドで定義される
M[A]
/* applies a transformation of the monad "content" mantaining the
* monad "external shape"
* i.e. a List remains a List and an Option remains an Option
* but the inner type changes
*/
def map(f: A => B): M[B]
/* applies a transformation of the monad "content" by composing
* this monad with an operation resulting in another monad instance
* of the same type
*/
def flatMap(f: A => M[B]): M[B]
など
val list = List("neo", "smith", "trinity")
//converts each character of the string to its corresponding code
val f: String => List[Int] = s => s.map(_.toInt).toList
list map f
>> List(List(110, 101, 111), List(115, 109, 105, 116, 104), List(116, 114, 105, 110, 105, 116, 121))
list flatMap f
>> List(110, 101, 111, 115, 109, 105, 116, 104, 116, 114, 105, 110, 105, 116, 121)
式に対して
-
を使った式の各行は
<-
記号に変換されます。flatMap
の呼び出しに変換されますが、最後の行は最後のmap
呼び出しに変換され、左側の "bound symbol" が引数関数のパラメータとして渡されます (以前はf: A => M[B]
):// The following ... for { bound <- list out <- f(bound) } yield out // ... is translated by the Scala compiler as ... list.flatMap { bound => f(bound).map { out => out } } // ... which can be simplified as ... list.flatMap { bound => f(bound) } // ... which is just another way of writing: list flatMap f
-
を1つだけ持つfor式。
<-
に変換されます。map
の呼び出しに変換されます。// The following ... for { bound <- list } yield f(bound) // ... is translated by the Scala compiler as ... list.map { bound => f(bound) } // ... which is just another way of writing: list map f
さて、本題に入ります。
ご覧のように
map
の操作によって、元の
monad
であるため、同じことが
yield
の式でも同様です。
List
は
List
の中の操作によって変換された内容で
yield
.
一方
for
は単に連続した
monads
であり、単一の外形を維持するために平坦化されなければならない。
仮に、それぞれの内部バインディングが
map
の呼び出しで、右側は同じ
A => M[B]
関数であった場合、最終的に
M[M[B]]
になってしまいます。
全体の意図
for
構文は、連続するモナド操作(つまり、"モナド形状" の値を持ち上げる操作)の連結を簡単に"フラット化" するためのものです。
A => M[B]
を加えたもの)を連結し、最後に
map
という操作を追加しました。
おそらく
は、結論となる変換を実行します。
という機械的な方法で適用される翻訳を選択する論理を説明することができたと思います。
n
flatMap
ネストされた呼び出しは、1つの
map
の呼び出しで終わります。
巧妙な説明の例
の表現力を示すことを意図しています。
for
構文
case class Customer(value: Int)
case class Consultant(portfolio: List[Customer])
case class Branch(consultants: List[Consultant])
case class Company(branches: List[Branch])
def getCompanyValue(company: Company): Int = {
val valuesList = for {
branch <- company.branches
consultant <- branch.consultants
customer <- consultant.portfolio
} yield (customer.value)
valuesList reduce (_ + _)
}
のタイプはわかりますか?
valuesList
?
すでに述べたように、形状は
monad
の形状は理解を通じて維持されるので、まず最初に
List
で
company.branches
で終わる必要があり
List
.
代わりに内側の型が変わり、その型は
yield
表現:どのような
customer.value: Int
valueList
であるべきです。
List[Int]
関連
-
[解決済み] IntelliJ IDEAで依存関係が変更された後、build.sbtから強制的に再ロードするには?
-
[解決済み] Scalaです。リスト[Future]からFuture[List]への変換は、失敗したFutureを無視する。
-
[解決済み] 単純な case class の順序を定義する簡単な慣用的方法
-
[解決済み] Scala の Case Classes のオーバーロード・コンストラクタ?
-
[解決済み] Scalaのアクター:受信と反応
-
[解決済み] Scalaでマップを反転させるエレガントな方法
-
[解決済み] Scalaのtraitでvalとdefの使い分けは?
-
[解決済み] ScalaにおけるNull/Nothing/Unitの使用法
-
[解決済み] scala.concurrent.Promiseのユースケースは何ですか?
-
[解決済み] に似た三項演算子。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Scalaです。リスト[Future]からFuture[List]への変換は、失敗したFutureを無視する。
-
[解決済み] sbtとGradleの比較 [終了しました]。
-
[解決済み] Scala の Case Classes のオーバーロード・コンストラクタ?
-
[解決済み] Scalaにおけるval-mutableとvar-immutableの比較
-
[解決済み] Scalaのcaseクラスを宣言することのデメリットは何ですか?
-
[解決済み] Scala型プログラミングリソース
-
[解決済み] SBTのrunアクションでアプリケーションを実行するために、JVMの最大ヒープサイズを"-Xmx "で指定する方法は?
-
[解決済み] Scala の "new" キーワード
-
[解決済み] Scalaで2つ以上のリストをまとめてzipで圧縮することはできますか?
-
[解決済み] フォールドの早期中止