2015-01-24 12 views
5

मुझे उत्सुकता है कि इस कोड को स्कैला में कैसे बनाया जाएगा।रिकर्सिव फ़ंक्शन प्रकारों का मॉडल कैसे करें?

यह golang में है, और यह एक पुनरावर्ती समारोह प्रकार है:

type walkFn func(*int) walkFn 

तो ऊपर सिर्फ प्रकार का एक defintion है, टहलने के समारोह एक समारोह है कि एक पूर्णांक के लिए एक सूचक लेता है, और एक चलने का काम देता है।

एक उदाहरण कार्यान्वयन होगा:

func walkForward(i *int) walkFn { 
    *i += rand.Intn(6) 
    return pickRandom(walkEqual, walkBackward) 
} 

func walkBackward(i *int) walkFn { 
    *i += -rand.Intn(6) 
    return pickRandom(walkEqual, walkForward) 
} 

आप इस यहाँ की तरह कोड चला सकते हैं: http://play.golang.org/p/621lCnySmy

क्या यह संभव है स्काला में इस पैटर्न की तरह कुछ लिखने के लिए?

उत्तर

5

स्कैला के दुखद तथ्यों में से एक यह है कि पुनरावर्ती प्रकार उपनाम समर्थित नहीं हैं। आरईपीएल में निम्न कार्य निम्नलिखित पैदावार:

scala> type WalkFn = Function[Int, WalkFn] 
<console>:7: error: illegal cyclic reference involving type WalkFn 
     type WalkFn = Function[Int, WalkFn] 
           ^

एक और टिप्पणी है कि स्काला आप संदर्भ द्वारा मूल्यों को संशोधित करने की अनुमति नहीं है (आम तौर पर की बात है, अस्वीकार, कार्यात्मक-प्रोग्रामिंग-प्रतिमान में पूरी तरह से घृणा सिकोड़ी)।

हालांकि, निराश मत हो! अन्य विकल्प हैं। लक्षण स्वयं-रेफरेंसियल हो सकते हैं, और कार्य स्काला में कक्षाएं हैं। तो हम लक्षणों के साथ सामान्य रिकर्सिव WalkFn मॉडल कर सकते हैं। इसके अलावा, हम अपरिवर्तनीय मूल्यों को गले लगा सकते हैं और हमारे कार्य को संदर्भ द्वारा पैरामीटर को म्यूट करने के बजाय अगली प्रगति को वापस कर सकते हैं।

के बाद से निम्नलिखित चक्रीय संदर्भ शामिल हैं (WalkForward -> WalkBackward, WalkBackward -> WalkForward, आदि), तुम इतनी स्काला संकलक सभी संकलन होगा स्केला में :paste टाइप करने के लिए REPL पहले निम्न उदाहरण (चल करने की आवश्यकता होगी एक कदम में 3 Walk{Forward,Backward,Equal} कार्यान्वयन

पहले:।

import scala.util.Random 

object Helpers { 
    def pickRandom[A](items: A*) = 
    items(Random.nextInt(items.length)) 
} 

trait WalkFn extends (Int => (Int, WalkFn)) {} 

object WalkForward extends WalkFn { 
    def apply(i: Int) = 
    (i + Random.nextInt(6), 
     Helpers.pickRandom(WalkEqual, WalkBackward)) 
} 

object WalkEqual extends WalkFn { 
    def apply(i: Int) = 
    (i + (Random.nextInt(7) - 3), 
     Helpers.pickRandom(WalkForward, WalkBackward)) 
} 

object WalkBackward extends WalkFn { 
    def apply(i: Int) = 
    (Random.nextInt(6) - 3, 
     Helpers.pickRandom(WalkEqual, WalkForward)) 
} 

def doWalk(count: Int, walkFn: WalkFn = WalkEqual, progress: Int = 0): Unit = 
    if (count > 0) { 
    val (nextProgress, nextStep) = walkFn(progress) 
    println(nextProgress) 
    doWalk(count - 1, nextStep, nextProgress) 
    } 

doWalk(20) 
0123:

$ scala 
Welcome to Scala version 2.11.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_05). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

अब, कोड पेस्ट

फिर, प्रति निर्देश, ctrl-D मारा।

कार्यात्मक शराबी स्टैगर का आनंद लें!

+0

अच्छा जवाब है, लेकिन (नहीं * अभी तक * स्कैला से परिचित होना): आपको 'हेल्पर्स' ऑब्जेक्ट की क्या आवश्यकता है? क्या यह सिर्फ एक नामस्थान है, या क्या कोई सम्मेलन आपको शीर्ष-स्तर 'def'inition बनाने से रोकता है? – Bergi

+3

@ बर्गि सभी 'def'' स्केल में विधियां हैं (वे स्पष्ट रूप से/स्पष्ट रूप से ईटा-विस्तार द्वारा फ़ंक्शन ऑब्जेक्ट्स में परिवर्तित हो रहे हैं)। इसलिए कुछ ऑब्जेक्ट को कुछ ऑब्जेक्ट के अंदर रखा जाना चाहिए। वैसे, स्कैला आरईपीएल (और अंतर्निर्मित दुभाषिया स्वयं) सिंथेटिक ऑब्जेक्ट बनाता है, ताकि आप आरईपीएल पाश के अंदर विधियों को परिभाषित कर सकें, लेकिन आम तौर पर लोगों को बिना किसी बदलाव के स्केलैक द्वारा संकलित करने के लिए कोड तैयार करने के लिए ऑब्जेक्ट्स का उपयोग किया जाता है। – dk14

5

मैं कहूंगा कि यह पुनरावृत्ति भाग को कारगर करने के लिए स्कैला में अधिक मूर्खतापूर्ण है। उदाहरण के लिए हम एक राज्य मशीन को परिभाषित कर सकते हैं:

import scala.util.Random 

sealed trait Walker { 
    def i: Int 
    def advance: Walker 
} 

case class WalkEqual(i: Int) extends Walker { 
    def advance = { 
    val next = i + Random.nextInt(7) - 3 
    if (Random.nextBoolean) WalkForward(next) else WalkBackward(next) 
    } 
} 

case class WalkForward(i: Int) extends Walker { 
    def advance = { 
    val next = i + Random.nextInt(6) 
    if (Random.nextBoolean) WalkEqual(next) else WalkBackward(next) 
    } 
} 

case class WalkBackward(i: Int) extends Walker { 
    def advance = { 
    val next = i - Random.nextInt(6) 
    if (Random.nextBoolean) WalkEqual(next) else WalkForward(next) 
    } 
} 

और फिर हम निम्नलिखित लिख सकते हैं:

val locations = Stream.iterate[Walker](WalkEqual(0))(_.advance).map(_.i) 

यह उन स्थानों है कि हमारे वॉकर यात्राओं की एक अनंत धारा है। हम इसे इस तरह उपयोग कर सकते हैं:

scala> locations.take(10).foreach(println) 
0 
0 
-1 
2 
1 
0 
-5 
-5 
-10 
-6 

हम भी locations.take(100).toList लिख कर इन की एक सीमित संख्या लेते हैं और उन्हें एक ठोस रूप की प्राप्ति नहीं संग्रह (जैसे एक सूची के रूप में) में एकत्र कर पाया है।

+0

क्या कक्षाएं होने के बावजूद WalkEqual, WalkForward जैसे विधियों को कक्षा में रखना संभव है? – Blankman

+0

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

8

यह संभव है।आप अस्तित्व प्रकार का उपयोग कर सकते हैं "धोखा" के लिए स्केला का चक्रीय संदर्भ प्रतिबंध:

type A[T <: A[_]] = Int => (Int, T) 

lazy val walkEqual: A[A[_]] = (i: Int) => 
    (i + Random.nextInt(7) - 3, if (Random.nextBoolean) walkForward else walkBackward) 

lazy val walkForward: A[A[_]] = (i: Int) => 
    (i + Random.nextInt(6), if (Random.nextBoolean) walkEqual else walkBackward) 

lazy val walkBackward: A[A[_]] = (i: Int) => 
    (i - Random.nextInt(6), if (Random.nextBoolean) walkEqual else walkForward) 

def doWalk(count: Int, walkFn: A[_] = walkEqual, progress: Int = 0): Unit = 
    if (count > 0) { 
    val (nextProgress, nextStep: A[_] @unchecked) = walkFn(progress) 
    println(nextProgress) 
    doWalk(count - 1, nextStep, nextProgress) 
    } 

परिणाम:

scala> doWalk(10) 
2 
5 
2 
0 
-3 
-5 
-4 
-8 
-8 
-11 

या की तरह @Travis ब्राउन अलावा:

val locations = Stream.iterate[(Int,A[_] @unchecked)](walkEqual(0)) { 
    case (x: Int, f: A[_]) => f(x) 
}.map(_._1) 

scala> locations.take(20).toList 
res151: List[Int] = List(-1, 1, 1, 4, 1, -2, 0, 1, 0, 1, 4, -1, -2, -4, -2, -1, 2, 1, -1, -2) 
+0

क्या आप विस्तार से पहली पंक्ति विस्तार से समझा सकते हैं? टी ए का उप-वर्ग है, लेकिन ए [_} का क्या अर्थ है? क्या आप कृपया स्पष्ट हो सकते हैं कि ए के इनपुट और आउटपुट क्या हैं? मैं आसानी से भ्रमित हो जाता हूं :) – Blankman

+3

'ए [_]' एक अस्तित्वहीन प्रकार है जिसका मतलब यहां अनिर्धारित है। तो, 'ए [टी: <ए [_]]' का अर्थ वास्तव में है कि 'टी'' ए 'होना चाहिए या कुछ छोटा होना चाहिए और यह केवल वास्तव में हो सकता है - तो यह चाल है। मुझे 'ए [ए [_]]' का उपयोग करना है (जो इस संदर्भ में है ए जो ए लौटाता है जो रिटर्न नहीं जानता) क्योंकि स्कैला प्रकार के अनंत रिकर्सन की अनुमति नहीं देता है, लेकिन असल में एक तरीका है ऐसी चीजें करें] (https://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/)। – dk14

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