、scalazのの7.0.xと、以下の輸入(scalaz 6.xののための回答履歴を見て):
import scalaz._
import Scalaz._
状態タイプはS
がタイプですState[S, A]
として定義されています状態のA
は装飾される値のタイプです。
// Create a state computation incrementing the state and returning the "str" value
val s = State[Int, String](i => (i + 1, "str"))
初期値に状態計算を実行するには:状態は関数呼び出しに通すことができる
// start with state of 1, pass it to s
s.eval(1)
// returns result value "str"
// same but only retrieve the state
s.exec(1)
// 2
// get both state and value
s(1) // or s.run(1)
// (2, "str")
状態値を作成するための基本的な構文はState[S, A]
機能を利用します。 Function[A, B]
の代わりにこれを行うには、Function[A, State[S, B]]]
を定義します。
import java.util.Random
def dice() = State[Random, Int](r => (r, r.nextInt(6) + 1))
に
...
State
機能を使用すると
for/yield
構文は、関数を構成するために使用することができます:ここでは
def TwoDice() = for {
r1 <- dice()
r2 <- dice()
} yield (r1, r2)
// start with a known seed
TwoDice().eval(new Random(1L))
// resulting value is (Int, Int) = (4,5)
は別の例です。 TwoDice()
状態計算のリストを記入してください。
val list = List.fill(10)(TwoDice())
// List[scalaz.IndexedStateT[scalaz.Id.Id,Random,Random,(Int, Int)]]
State[Random, List[(Int,Int)]]
を取得するためにシーケンスを使用してください。タイプエイリアスを提供できます。
type StateRandom[x] = State[Random,x]
val list2 = list.sequence[StateRandom, (Int,Int)]
// list2: StateRandom[List[(Int, Int)]] = ...
// run this computation starting with state new Random(1L)
val tenDoubleThrows2 = list2.eval(new Random(1L))
// tenDoubleThrows2 : scalaz.Id.Id[List[(Int, Int)]] =
// List((4,5), (2,4), (3,5), (3,5), (5,5), (2,2), (2,4), (1,5), (3,1), (1,6))
それとも種類を推測なるsequenceU
を使用することができます。
は
val list3 = list.sequenceU
val tenDoubleThrows3 = list3.eval(new Random(1L))
// tenDoubleThrows3 : scalaz.Id.Id[List[(Int, Int)]] =
// List((4,5), (2,4), (3,5), (3,5), (5,5), (2,2), (2,4), (1,5), (3,1), (1,6))
State[Map[Int, Int], Int]
とのもう一つの例は、上記のリストの和の周波数を計算します。 freqSum
はスローとカウントの合計を計算します。
def freqSum(dice: (Int, Int)) = State[Map[Int,Int], Int]{ freq =>
val s = dice._1 + dice._2
val tuple = s -> (freq.getOrElse(s, 0) + 1)
(freq + tuple, s)
}
今tenDoubleThrows
上freqSum
を適用するためにトラバースを使用します。 traverse
はmap(freqSum).sequence
に相当します。
tenDoubleThrows2.copoint.traverseU(freqSum).exec(Map[Int,Int]())
// Map(10 -> 1, 6 -> 3, 9 -> 1, 7 -> 1, 8 -> 2, 4 -> 2) : scalaz.Id.Id[Map[Int,Int]]
注State[S, A]
がStateT[Id, S, A]
の型の別名であるため、tenDoubleThrows2がId
として入力されてしまうこと:タイプを推測するtraverseU
を用いて
type StateFreq[x] = State[Map[Int,Int],x]
// only get the state
tenDoubleThrows2.copoint.traverse[StateFreq, Int](freqSum).exec(Map[Int,Int]())
// Map(10 -> 1, 6 -> 3, 9 -> 1, 7 -> 1, 8 -> 2, 4 -> 2) : scalaz.Id.Id[Map[Int,Int]]
以上簡潔。私はcopoint
を使ってList
タイプに戻します。
簡潔に言えば、状態を使用する鍵は、状態と目的の実際の結果値を変更する関数を返すようになっているようです... 免責事項:私はstate
をプロダクションコードで使用したことがなく、それを感じる。
@ziggystarコメントに関する追加情報
私はstateT
を使用しようとあきらめたStateFreq
かStateRandom
を組み合わせた計算を実行するために増強することができるならば他の誰かを示すことができるかもしれません。私が代わりに見つけた2つの状態の変圧器の構成は次のように組み合わせることができるということである:それはg
が第1の状態変圧器の結果を取り、状態の変圧器を返送つのパラメータの関数であることを前提だ
def stateBicompose[S, T, A, B](
f: State[S, A],
g: (A) => State[T, B]) = State[(S,T), B]{ case (s, t) =>
val (newS, a) = f(s)
val (newT, b) = g(a) apply t
(newS, newT) -> b
}
。そして、次は動作します:
def diceAndFreqSum = stateBicompose(TwoDice, freqSum)
type St2[x] = State[(Random, Map[Int,Int]), x]
List.fill(10)(diceAndFreqSum).sequence[St2, Int].exec((new Random(1L), Map[Int,Int]()))
「状態」モナドは、状態の「状態変換」ではありませんか? 2番目の質問として、サイコロの回転と集計を1つのState Monadに組み合わせる方法がありますか?あなたは2つのモナドを与えられたらどうしますか? – ziggystar
@ziggystar、技術的には 'StateFreq'と' StateRandom'はモナドです。 'S [x]はモナドである必要はないので、状態[S、x] 'はモナド変圧器ではないと私は考えます。結合するより良い方法のために、私はあまりにも不思議です。私は明らかに容易に利用可能なものは見当たりません。 'stateT'が役立つかもしれないが、私はまだそれを理解していない。 – huynhjl
私は "モナド変圧器"ではなく "状態変圧器"と書いていました。 'State [S、x] 'オブジェクトは状態を保持するのではなく、後者を変換します。それは、名前があまり混乱しないように選ぶことができると思います。それはあなたの答えではなく、Scalazについてです。 – ziggystar