2011-10-12 9 views
72

मैंने स्कालाज़ राज्य मोनड के कई उदाहरण नहीं देखे हैं। this example है लेकिन यह समझना मुश्किल है और ऐसा लगता है कि स्टैक ओवरफ़्लो पर केवल एक other question है।स्कालाज़ राज्य मोनड उदाहरण

मैं कुछ उदाहरण पोस्ट करने जा रहा हूं जिनके साथ मैंने खेला है लेकिन मैं अतिरिक्त लोगों का स्वागत करता हूं। इसके अलावा अगर कोई उदाहरण दे सकता है कि क्यों init, modify, put और gets का उपयोग किया जाता है तो यह बहुत अच्छा होगा।

संपादित करें: here राज्य मोनड पर एक शानदार 2 घंटे की प्रस्तुति है।

उत्तर

80

मुझे लगता है, scalaz 7.0.x और निम्नलिखित आयात (scalaz 6.x के लिए जवाब इतिहास पर नज़र डालें):

जहां S प्रकार है
import scalaz._ 
import Scalaz._ 

राज्य प्रकार 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]]] परिभाषित करें।

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) 

यहाँ एक और उदाहरण है: State समारोह ...

import java.util.Random 
def dice() = State[Random, Int](r => (r, r.nextInt(6) + 1)) 

फिर for/yield वाक्य रचना कार्यों की रचना करने के लिए किया जा सकता का उपयोग करें। 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 लागू करने के लिए। traversemap(freqSum).sequence के बराबर है।

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]] 

या अधिक संक्षेप traverseU का उपयोग कर प्रकार का अनुमान लगाने के द्वारा:

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 के रूप में आपके द्वारा लिखा गया जा रहा है। मैं copoint का उपयोग इसे List प्रकार में बदलने के लिए करता हूं।

संक्षेप में, ऐसा लगता है कि राज्य का उपयोग करने की कुंजी राज्य को संशोधित करने वाले फ़ंक्शन को वापस करने और वास्तविक परिणाम मूल्य वांछित कार्य करने के लिए है ... अस्वीकरण: मैंने कभी भी उत्पादन कोड में state का उपयोग नहीं किया है, बस एक इसके लिए महसूस करो।

@ziggystar टिप्पणी पर अतिरिक्त जानकारी

मैं stateT का उपयोग कर किया जा सकता है किसी और दिखाई देते हैं, StateFreq या StateRandom संयुक्त गणना प्रदर्शन करने के लिए संवर्धित किया जा सकता कोशिश कर रहा पर छोड़ दिया। क्या मैं बजाय पाया गया कि दो राज्य ट्रांसफार्मर की संरचना इस तरह जोड़ा जा सकता है है:

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 
} 

यह g पहला राज्य ट्रांसफार्मर का परिणाम लेने और एक राज्य ट्रांसफार्मर लौटने एक एक पैरामीटर समारोह किया जा रहा है पर प्रतिपादित है। उसके बाद निम्न काम करेगा:

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]())) 
+0

'राज्य' मोनड नहीं है, जो "राज्य ट्रांसफार्मर" नहीं है? और एक दूसरे प्रश्न के रूप में: क्या पासा रोलिंग और एक एकल राज्य मोनड में संक्षेप को गठबंधन करने के लिए कुछ अच्छा तरीका है? आप दो monads दिए गए कैसे करेंगे? – ziggystar

+0

@ ज़िगगीस्टार, तकनीकी रूप से 'स्टेटफ्रैक' और 'स्टेटरैंडम' मोनैड हैं। मुझे नहीं लगता कि 'राज्य [एस, एक्स]' एक मोनड ट्रांसफॉर्मर है क्योंकि 'एस' को मोनाड होने की आवश्यकता नहीं है। गठबंधन करने के लिए एक अच्छे तरीके के लिए, मैं भी सोच रहा हूँ। मुझे कुछ भी स्पष्ट रूप से आसानी से उपलब्ध नहीं दिख रहा है। हो सकता है कि 'स्टेट टी' मदद कर सके लेकिन मुझे अभी तक यह पता नहीं लगा है। – huynhjl

+0

मैंने "मोनैड ट्रांसफार्मर" नहीं लिखा लेकिन "राज्य ट्रांसफार्मर"। 'राज्य [एस, एक्स]' ऑब्जेक्ट्स में एक राज्य नहीं है बल्कि बाद में परिवर्तन होता है। यह सिर्फ इतना है कि मुझे लगता है कि नाम कम भ्रमित चुना जा सकता है। यह आपके उत्तर के बारे में कुछ नहीं है लेकिन स्कालज़ के बारे में है। – ziggystar

15

मैं sigfp से एक दिलचस्प ब्लॉग पोस्ट Grok Haskell Monad Transformers एक इकाई ट्रांसफार्मर के माध्यम से दो राज्य monads लागू करने का एक उदाहरण है कि पर ठोकर खाई। यहां एक स्कालाज़ अनुवाद है।

val test1 = for { 
    a <- init[Int] 
    _ <- modify[Int](_ + 1) 
    b <- init[Int] 
} yield (a, b) 

val go1 = test1 ! 0 
// (Int, Int) = (0,1) 

तो मैं यहाँ init और modify उपयोग का एक उदाहरण है:

पहला उदाहरण एक State[Int, _] इकाई को दर्शाता है। इसके साथ खेलने के बाद, init[S]State[S,S] मान उत्पन्न करने के लिए वास्तव में सुविधाजनक हो गया है, लेकिन दूसरी चीज यह समझने के लिए राज्य को एक्सेस करना है। modify[S] समझ के अंदर राज्य को बदलने का एक सुविधाजनक तरीका है।

  • a <- init[Int]:: तो उपरोक्त उदाहरण के रूप में पढ़ा जा सकता है एक Int राज्य के साथ शुरू, State[Int, _] इकाई द्वारा लिपटे मूल्य के रूप में सेट और a
  • _ <- modify[Int](_ + 1) को यह बाँध: बढ़ाने के Int राज्य
  • b <- init[Int]: ले Int राज्य और b को यह बाँध (a के लिए के रूप में ही है, लेकिन अब राज्य वृद्धि की जाती है)
  • एक State[Int, (Int, Int)] मूल्य उसी उपज एनजी a और b

समझ वाक्य रचना के लिए पहले से ही यह State[S, A] में A पक्ष पर काम करने के लिए तुच्छ बना देता है।init, modify, put और getsState[S, A] में S पक्ष पर काम करने के लिए कुछ टूल प्रदान करते हैं।

val test2 = for { 
    a <- init[String] 
    _ <- modify[String](_ + "1") 
    b <- init[String] 
} yield (a, b) 

val go2 = test2 ! "0" 
// (String, String) = ("0","01") 
बहुत ज्यादा test1 रूप में एक ही स्पष्टीकरण

:

दूसरे उदाहरण ब्लॉग पोस्ट में करने के लिए अनुवाद।

तीसरा उदाहरण अधिक मुश्किल है और मुझे उम्मीद है कि मुझे कुछ आसान खोजना है।

type StateString[x] = State[String, x] 

val test3 = { 
    val stTrans = stateT[StateString, Int, String]{ i => 
    for { 
     _ <- init[String] 
     _ <- modify[String](_ + "1") 
     s <- init[String] 
    } yield (i+1, s) 
    } 
    val initT = stateT[StateString, Int, Int]{ s => (s,s).pure[StateString] } 
    for { 
    b <- stTrans 
    a <- initT 
    } yield (a, b) 
} 

val go3 = test3 ! 0 ! "0" 
// (Int, String) = (1,"01") 

कि कोड में, stTransString राज्य से बाहर खींच के साथ ही दोनों राज्यों (वेतन वृद्धि और "1" साथ प्रत्यय) के परिवर्तन का ख्याल रखता है। stateT हमें मनमाने ढंग से मोनड M पर राज्य परिवर्तन जोड़ने की अनुमति देता है। इस मामले में राज्य Int है जो बढ़ी है। अगर हम stTrans ! 0 कहते हैं तो हम M[String] के साथ समाप्त हो जाएंगे। हमारे उदाहरण में, MStateString है, इसलिए हम StateString[String] के साथ समाप्त हो जाएंगे जो State[String, String] है।

यहां मुश्किल हिस्सा यह है कि हम राज्य मूल्य stTrans से बाहर निकालना चाहते हैं। यह initT है। यह सिर्फ एक ऑब्जेक्ट बनाता है जो राज्य को इस तरह से पहुंच प्रदान करता है कि हम stTrans के साथ flatMap कर सकते हैं।

संपादित करें:

// same as test3: 
val test31 = stateT[StateString, Int, (Int, String)]{ i => 
    val (_, a) = test1 ! i 
    for (t <- test2) yield (a, (a, t._2)) 
} 
10

यहाँ एक बहुत ही छोटा सा उदाहरण है: अगर हम सही मायने में test1 और test2 जो आसानी से उनके लौटे tuples के _2 तत्व में वांछित राज्यों की दुकान पुन: उपयोग किया है कि भद्दापन के सभी बचा जा सकता है बाहर कर देता है कैसे State का उपयोग किया जा सकता है:

चलिए एक छोटा "गेम" परिभाषित करते हैं जहां कुछ गेम इकाइयां बॉस से लड़ रही हैं (जो एक गेम यूनिट भी है)।

case class GameUnit(health: Int) 
case class Game(score: Int, boss: GameUnit, party: List[GameUnit]) 


object Game { 
    val init = Game(0, GameUnit(100), List(GameUnit(20), GameUnit(10))) 
} 

जब खेलने हम खेल राज्य का ट्रैक रखने, तो चलो एक राज्य इकाई के मामले में हमारे "कार्रवाई" को परिभाषित करना चाहते है:

के मालिक जोर से मारा तो वह से 10 खो देता है चलो उसका health:

def strike : State[Game, Unit] = modify[Game] { s => 
    s.copy(
    boss = s.boss.copy(health = s.boss.health - 10) 
) 
} 

और बॉस वापस आ सकता है! जब वह पार्टी में हर कोई करता है तो 5 health खो देता है।वास्तविक जीवन खेलने और अधिक गतिशील हो जाएगा में

def play = for { 
    _ <- strike 
    _ <- fireBreath 
    _ <- fireBreath 
    _ <- strike 
} yield() 
बेशक

, लेकिन यह मेरी छोटा सा उदाहरण :)

के लिए पर्याप्त भोजन है:

def fireBreath : State[Game, Unit] = modify[Game] { s => 
    val us = s.party 
    .map(u => u.copy(health = u.health - 5)) 
    .filter(_.health > 0) 

    s.copy(party = us) 
} 

अब हम इन कार्यों play में रचना कर सकते हैं

val res = play.exec(Game.init) 
println(res) 

>> Game(0,GameUnit(80),List(GameUnit(10))) 
:

हम खेल के अंतिम राज्य को देखने के लिए अब चला सकते हैं

इसलिए हमने मुश्किल से मालिक को मारा और इकाइयों में से एक की मृत्यु हो गई, आरआईपी।

यहां बिंदु संरचना है। State (जो कि केवल एक फ़ंक्शन S => (A, S) है) आपको उन परिणामों को परिभाषित करने की अनुमति देता है जो परिणाम उत्पन्न करते हैं और साथ ही कुछ राज्यों को बिना किसी जानकारी के कुशलतापूर्वक बताते हैं जहां राज्य आ रहा है।

A => State[S, B] 
B => State[S, C] 
------------------ 
A => State[S, C] 

और इतने पर: Monad हिस्सा आप रचना इसलिए अपने कार्यों की रचना की जा सकती है देता है।

पीएसget, put और modify के बीच मतभेद का सवाल है:

modifyget के रूप में देखा जा सकता है और put एक साथ:

def modify[S](f: S => S) : State[S, Unit] = for { 
    s <- get 
    _ <- put(f(s)) 
} yield() 

या बस

def modify[S](f: S => S) : State[S, Unit] = get[S].flatMap(s => put(f(s))) 

तो जब आप modify का उपयोग आप धारणात्मक get का उपयोग और put, या आप केवल उन्हें अकेले उपयोग कर सकते हैं।

संबंधित मुद्दे