2011-01-17 5 views
13

लंबे समय तक जावा प्रोग्रामर धीरे-धीरे स्केल सीख रहा है (जिस तरह से इसे प्यार करता है), और मुझे लगता है कि मेरा दिमाग अभी भी चीजों को काम करने की अवधारणा के आसपास लपेट रहा है। अभी मैं कुछ चलती 2 डी बनावट के लिए एक साधारण विज़ुअलाइज़र लिखने का प्रयास कर रहा हूं। जरूरी दृष्टिकोण काफी सरल है, और मुझे यकीन है कि आप में से अधिकांश कोड के इस अपेक्षाकृत सर्वव्यापक ब्लॉक पहचान लेंगे कर रहा हूँ (सामान मासूम की रक्षा में बदल गया था):स्कैला गेम प्रोग्रामिंग: एक कार्यात्मक शैली में ऑब्जेक्ट स्थिति को आगे बढ़ाना

class MovingTexture(var position, var velocity) extends Renders with Advances { 
    def render : Unit = {...} 
    def advance(milliseconds : Float) : Unit = { 
     position = position + velocity * milliseconds 
    } 
} 

इस कोड को ठीक से काम करेंगे, लेकिन यह है उत्परिवर्तनीय राज्य के टन और इसके कार्य दुष्प्रभावों से भरे हुए हैं। मैं खुद को इससे दूर जाने नहीं दे सकता, एक बेहतर तरीका होना चाहिए!

क्या किसी के पास इस साधारण समस्या का एक अद्भुत, सुरुचिपूर्ण, कार्यात्मक समाधान है? क्या किसी को ऐसे स्रोत के बारे में पता है जहां मैं इस तरह की समस्याओं को हल करने के बारे में और जान सकता हूं?

उत्तर

11

खेल अक्सर उच्च प्रदर्शन वाले मामले होते हैं, इस मामले में आप पाते हैं कि उत्परिवर्तनीय स्थिति केवल वही चीज है जो आपको चाहिए।

case class MovingTexture(position: VecXY, velocity: VecXY) extends Renders with Advances { 
    def advance(ms: Float) = copy(position = position + ms*velocity 
    def accelerate(acc: Float, ms: Float) = copy(velocity = velocity + ms*acc) 
    ... 
} 

है, बजाय अपनी कक्षाओं अपने आप अपडेट होने की, उन्हें खुद के नए प्रतियाँ वापस है:

हालांकि, इस मामले में, वहाँ सरल समाधान के एक जोड़े हैं। (आप देख सकते हैं कि यह कैसे महंगा हो सकता है। टेट्रिस के लिए, कोई बड़ा सौदा नहीं। Crysis के लिए? शायद इतना स्मार्ट नहीं।) ऐसा लगता है जैसे यह समस्या को एक स्तर पर वापस धक्का देता है: अब आपको MovingTexture के लिए एक var की आवश्यकता है, है ना? बिलकुल नहीं:

Iterator.iterate(MovingTexture(home, defaultSpeed))(_.advance(defaultStep)) 

यह उसी दिशा में स्थिति अपडेट की एक अंतहीन धारा उत्पन्न करेगा। आप उपयोगकर्ता इनपुट या व्हाट्नॉट में मिश्रण करने के लिए और अधिक जटिल चीजें कर सकते हैं।

वैकल्पिक रूप से, आप

class Origin extends Renders { 
    // All sorts of expensive stuff goes here 
} 
class Transposed(val ori: Origin, val position: VecXY) extends Renders with Advances { 
    // Wrap TextureAtOrigin with inexpensive methods to make it act like it's moved 
    def moving(vel: VecXY, ms: Float) = { 
    Iterator.iterate(this).(tt => new Transposed(tt.ori, position+ms*vel)) 
    } 
} 

है, हैवीवेट बातें कभी नहीं अद्यतन किया जा सकता है और उनमें से हल्का वजन देखा गया है कि उन्हें देखने के लिए बनाने के रूप में यद्यपि वे जिस तरह से आप उन्हें बदल चाहते हैं कि में बदल दिया है है ।

+1

सामान्य सर्वसम्मति (सभी उत्तरों के बीच) केवल मेरी वस्तुओं की प्रतियां बनाने के लिए प्रतीत होता है जो वर्तमान स्थिति का प्रतिनिधित्व करते हैं। यह वही नहीं है जो मैं खोज रहा था, लेकिन _this_ उत्तर विचारों के लिए कुछ वाकई दिलचस्प भोजन प्रस्तुत करता है जैसे कि इटरेटर्स को परिभाषित करना जो व्यक्त करता है कि मूल्य कैसे बदलते हैं (मूल्यों को व्यक्त करने के बजाए) ... मैं एक ऐसे उत्तर की उम्मीद कर रहा था जो मैं चाहता था कुछ प्रकार के फ़ंक्शन (या पुनरावृत्ति) के रूप में स्थिति को व्यक्त करने का कोई तरीका ढूंढें जो व्यक्त करेगा कि वेरिएबल परिवर्तन कैसे होते हैं (कैलकुस में एक अभिन्न अंग की तरह)। हालांकि, मुझे कुछ अच्छी चीजों के बारे में जानकारी देने के लिए धन्यवाद! – jtb

14

एक स्टैक ओवरफ्लो प्रतिक्रिया की जगह में फिट होने के मुकाबले इस उत्तर के लिए और भी कुछ तरीका है, लेकिन इस तरह के सवालों के सर्वोत्तम और सबसे पूर्ण उत्तर को कार्यात्मक प्रतिक्रियाशील प्रोग्रामिंग नामक कुछ का उपयोग करना है। मूल विचार प्रत्येक समय-भिन्न या संवादात्मक मात्रा को एक परिवर्तनीय चर के रूप में नहीं दर्शाता है, बल्कि मूल्यों की एक अपरिवर्तनीय धारा के रूप में, प्रत्येक बार क्वांटा के लिए एक है। चाल तब यह है कि प्रत्येक मान को मूल्यों की संभावित अनंत धारा द्वारा दर्शाया जाता है, जबकि धाराओं की आलसी गणना की जाती है (ताकि स्मृति को आवश्यक होने तक नहीं लिया जा सके), और धारा मूल्यों को समय क्वांटा में नहीं देखा जाता है अतीत (ताकि पिछली गणना कचरा एकत्र हो)। गणना अच्छी तरह से कार्यात्मक और अपरिवर्तनीय है, लेकिन गणना के उस हिस्से का आप समय के साथ "देख रहे हैं" परिवर्तन कर रहे हैं।

यह सब जटिल है, और इस तरह की धाराओं को जोड़ना मुश्किल है, खासकर यदि आप मेमोरी लीक से बचना चाहते हैं और सब कुछ थ्रेड-सुरक्षित और कुशल तरीके से काम करना चाहते हैं। कुछ स्कैला पुस्तकालय हैं जो कार्यात्मक प्रतिक्रियाशील प्रोग्रामिंग को कार्यान्वित करते हैं, लेकिन परिपक्वता अभी तक बहुत अधिक नहीं है। सबसे दिलचस्प शायद scala.react है, वर्णित here

+0

मुझे लगता है कि आपने इस समस्या के बारे में सोचने के लिए सिर पर नाखून मारा है। आपको लगता है कि आप अपनी सामग्री जानते हैं, मेरी इच्छा है कि आप कुछ सरल कोड उदाहरण प्रदान कर चुके हों ... लेकिन शायद आप सही हैं, यह समस्या व्यक्त करने के लिए एक संक्षिप्त समाधान के लिए बहुत व्यापक है। – jtb

+0

ठीक है, मैं वास्तव में इंटरैक्टिव पक्ष पर ज्यादा काम नहीं करता हूं, और कभी भी क्रोध में एफआरपी का उपयोग नहीं किया है। रीडिंग पूर्ण हो गया, सिद्धांत को जानें, लेकिन वास्तव में कभी भी कोड काट नहीं लें। –

+0

कूल, ठीक है, मैंने निश्चित रूप से एफआरपी के बारे में कभी भी यह नहीं सुना है कि आपने इसका उल्लेख किया है, इसलिए कम से कम मुझे अब और अधिक जानकारी मिली है और मेरे पास पढ़ने के लिए एक दिलचस्प पेपर है। धन्यवाद! – jtb

1

यहां कुछ कोड का नमूना है जो मैं काम कर रहा हूं जो सीधे राज्य को म्यूट करने के बजाए प्रतिलिपि वापस करने के दृष्टिकोण का उपयोग करता है।कम से कम सर्वर पक्ष पर इस तरह के दृष्टिकोण के बारे में अच्छी बात यह है कि यह मुझे लेनदेन-प्रकार अर्थशास्त्र को आसानी से कार्यान्वित करने में सक्षम बनाता है। यदि अद्यतन करते समय कुछ गलत हो जाता है, तो मेरे लिए अभी भी एक स्थिर स्थिति में अद्यतन की गई सभी चीज़ों के लिए यह छोटा है।

नीचे दिया गया कोड एक ऐसे गेम सर्वर से है जिस पर मैं काम कर रहा हूं, जो आप जो कर रहे हैं उसके समान कुछ करता है, यह उन वस्तुओं को ट्रैक करने के लिए है जो समय स्लाइस में घूम रहे हैं। यह दृष्टिकोण उतना शानदार नहीं है जितना डेव ग्रिफिथ सुझाव देता है, लेकिन यह आपके लिए चिंतन के लिए कुछ उपयोग हो सकता है। स्काला में मामला कक्षाओं के बारे में

case class PosController(
    pos: Vector3 = Vector3.zero, 
    maxSpeed: Int = 90, 
    velocity: Vector3 = Vector3.zero, 
    target: Vector3 = Vector3.zero 
) { 
    def moving = !velocity.isZero 

    def update(elapsed: Double) = { 
     if (!moving) 
      this 
     else { 
      val proposedMove = velocity * elapsed 
      // If we're about to overshoot, then stop at the exact position. 
      if (proposedMove.mag2 > pos.dist2(target)) 
       copy(velocity = Vector3.zero, pos = target) 
      else 
       copy(pos = pos + proposedMove) 
     } 
    } 

    def setTarget(p: Vector3) = { 
     if (p == pos) 
      this 
     else { 
      // For now, go immediately to max velocity in the correct direction. 
      val direction = (p - pos).norm 
      val newVel = direction * maxSpeed 
      copy(velocity = direction * maxSpeed, target = p) 
     } 
    } 

    def setTargetRange(p: Vector3, range: Double) = { 
     val delta = p - pos 
     // Already in range? 
     if (delta.mag2 < range * range) 
      this 
     else { 
      // We're not in range. Select a spot on a line between them and us, at max range. 
      val d = delta.norm * range 
      setTarget(p - d) 
     } 
    } 

    def eta = if (!moving) 0.0 else pos.dist(target)/maxSpeed 
} 

एक अच्छी बात यह है कि वे you-- तुम सिर्फ पारित जिसमें मानकों को बदल दिया है के लिए विधि प्रति() बनाने के लिए, और दूसरों को एक ही मूल्य को बनाए रखने की है। यदि आप केस कक्षाओं का उपयोग नहीं कर रहे हैं, तो आप इसे हाथ से कोड कर सकते हैं, लेकिन जब भी आप कक्षा में कौन से मूल्य मौजूद हैं, तो आपको कॉपी विधि को अपडेट करना याद रखना होगा।

संसाधनों के संबंध में, वास्तव में मेरे लिए क्या अंतर आया, एरलांग में कुछ समय करने में व्यतीत कर रहा था, जहां मूल रूप से अपरिवर्तनीय स्थिति का उपयोग करने के अलावा कोई विकल्प नहीं है। मेरे पास दो एरलांग किताबें हैं जिनके माध्यम से मैंने काम किया और प्रत्येक उदाहरण को ध्यान से पढ़ा। इसके अलावा, एर्लांग में कुछ चीजें करने के लिए खुद को मजबूर करने के लिए मुझे अपरिवर्तनीय डेटा के साथ काम करने के साथ बहुत अधिक आरामदायक बना दिया।

+0

अजीब रूप से पर्याप्त, एरलांग अगली भाषा है जिसे मैं लेने (या शायद हास्केल) लेने का प्रयास करने जा रहा था, हालांकि मेरे कमजोर मस्तिष्क को अभी भी स्कैला के साथ बहुत काम करना पड़ा है। मुझे आपके उदाहरण पसंद हैं! मुझे अभी भी लगता है कि प्रत्येक पुनरावृत्ति के ऑब्जेक्ट्स की प्रतियां बनाने के बजाय चर बदलने के तरीके का प्रतिनिधित्व करने का एक और सामान्य तरीका होना चाहिए। क्या आपने कभी एक var के बजाय समय के साथ स्थिति के एक समारोह के रूप में प्रतिनिधित्व स्थिति के साथ प्रयोग किया है? – jtb

+0

मैंने नहीं किया है। मैंने कई राज्यों को एक वैर के भीतर एक अभिनेता के लिए रखकर शुरू किया, जो कई वैल से बना है। उदाहरण के लिए, स्थिति नियंत्रक एक वैल है, और मस्तिष्क राज्य एक और मूल्य है। उन सभी को एक राज्य परिवर्तनीय में घुमाया जाता है।अगर मेरे पास एक तरीका है जो एक को बदलता है, दूसरे को बदलता है, और संभवतः मध्य में अपवाद फेंकता है, तो मुझे गारंटी है कि या तो सभी 3 ऑप्स काम करते हैं या सभी 3 असफल होते हैं, और राज्य संरक्षित होता है। कारण मैंने इसे समय के कार्य के रूप में व्यक्त नहीं किया है कि लक्ष्य की स्थिति कभी-कभी मस्तिष्क वर्ग द्वारा नियंत्रित होती है, और कभी-कभी किसी खिलाड़ी द्वारा। – Unoti

+0

आपने बताया कि पहले अपने स्कैला में अपने मस्तिष्क को लपेटने के लिए बहुत कुछ है। मेरे लिए, मैंने पहली बार एरलांग से स्केल को और अधिक कठिन पाया - पहले। अन्य लोग अलग-अलग महसूस करते हैं, लेकिन मैंने यहां सीखने की प्रक्रिया के दौरान एर्लांग बनाम स्काला के बारे में विचार लिखे हैं http://tango11.com/news/scala-complexity-vs-erlang-complexity/ जो आपको बहुत रूचि दे सकता है। – Unoti

2

प्रोग्रामिंग इंटरैक्टिव अनुप्रयोगों के लिए एक पूरी तरह से कार्यात्मक दृष्टिकोण के बारे में कुछ लंबाई में चला जाता है, "दुनिया को कैसे डिजाइन करें" नामक एक ब्रोशर ("प्रोग्राम कैसे डिजाइन करें" के लेखकों द्वारा)।

असल में, वे एक "दुनिया" (एक डेटाटाइप जिसमें सभी गेम स्टेटस शामिल हैं) पेश करते हैं, और कुछ फ़ंक्शन, जैसे कि "टिक" (प्रकार की दुनिया -> दुनिया) और "ऑनकीप्रेस" (प्रकार की कुंजी * दुनिया -> दुनिया)। एक "प्रस्तुत करना" फ़ंक्शन तब एक दुनिया लेता है और एक दृश्य देता है, जिसे तब "वास्तविक" प्रस्तुतकर्ता को पास किया जाता है।

0

लघु लेखों की इस श्रृंखला ने मुझे प्रोग्रामिंग समस्याओं को हल करने में कार्यात्मक रूप से सोचने में शुरुआत करने में मदद की। खेल रेट्रो (पीएसी मैन) है, लेकिन प्रोग्रामर नहीं है। http://prog21.dadgum.com/23.html

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