2010-02-19 11 views
52

गुना बाएं कुछ अच्छे ट्यूटोरियल क्या हैं?कार्यात्मक प्रोग्रामिंग, स्कैला मानचित्र और गुना बाएं

मूल प्रश्न, हटाए जाने से बहाल अन्य उत्तर को संदर्भ प्रदान करने:

मैं आयत, वृत्त, स्थान की boudning बॉक्स और समूह है जो सभी का विस्तार आकार को खोजने के लिए एक विधि को लागू करने की कोशिश कर रहा हूँ। समूह मूल रूप से आकारों की एक श्रृंखला है

abstract class Shape 
case class Rectangle(width: Int, height: Int) extends Shape 
case class Location(x: Int, y: Int, shape: Shape) extends Shape 
case class Circle(radius: Int) extends Shape 
case class Group(shape: Shape*) extends Shape 

मुझे ग्रुप वन को छोड़कर सभी तीनों के लिए बाध्यकारी बॉक्स मिला है। तो अब बाध्यकारी बॉक्स विधि के लिए मुझे पता है कि मुझे समूह का उपयोग करना चाहिए और समूह के लिए बाएं गुना होना चाहिए, लेकिन मुझे इसे बनाने का सटीक वाक्यविन्यास नहीं मिल रहा है।

object BoundingBox { 
    def boundingBox(s: Shape): Location = s match { 
    case Circle(c)=> 
     new Location(-c,-c,s) 
    case Rectangle(_, _) => 
     new Location(0, 0, s) 
    case Location(x, y, shape) => { 
     val b = boundingBox(shape) 
     Location(x + b.x, y + b.y, b.shape) 
    } 
    case Group(shapes @ _*) => (/: shapes) { } // i dont know how to proceed here. 
    } 
} 

समूह बाध्यकारी बॉक्स मूल रूप से सभी आकारों के साथ सबसे छोटा बाउंडिंग बॉक्स है।

+9

आप एस में हैं टॉम के रूप में एमई कक्षा? Http://stackoverflow.com/questions/2274852/scala-how-to-perform-pattern-matching-with-vararg-case-classes देखें। – huynhjl

+3

यह स्कैला और 'फ़ोल्ड लेफ्ट' के बारे में कोई सवाल नहीं है। यह एल्गोरिदम के बारे में एक सवाल है। आप पूछने से बेहतर होंगे _ "मैं अपरिवर्तनीय डेटा संरचनाओं का उपयोग करके आकृतियों की सूची से सबसे छोटे बाध्यकारी बॉक्स की गणना कैसे करूं?" _। प्रश्न को भाषा-अज्ञेयवादी और एल्गोरिदम के रूप में टैग करें। और शायद कार्यात्मक-प्रोग्रामिंग। यदि आपको स्कैला में सुझाए गए एल्गोरिदम को लागू करने में कोई समस्या है, तो आप इसके बारे में एक स्कैला प्रश्न खोलें। वर्तमान प्रश्न गलत समूह में लक्षित किया जा रहा है। –

उत्तर

2

एक बाध्यकारी बॉक्स आम तौर पर एक आयताकार होता है। मुझे नहीं लगता कि (-r, -r) पर स्थित एक सर्कल त्रिज्या आर के एक चक्र का बाध्यकारी बॉक्स है ....

वैसे भी, मान लें कि आपके पास बाउंडिंग बॉक्स बी 1 और दूसरा बी 2 और एक फ़ंक्शन combineBoxes है जो बी 1 और बी 2 के बाध्यकारी बॉक्स की गणना करता है।

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

मान लीजिए कि blist की एक सूची है प्रत्येक आकार के बाध्यकारी बक्से।

val bigBox = blist reduceLeft((box1,box2) => combineBoxes(box1,box2)) 

आप खाली समूह मामला अलग से पकड़ने के लिए हालांकि, की आवश्यकता होगी: (संकेत इस जहां map में आता है।) इसके बाद। (चूंकि इसमें कोई अच्छी तरह से परिभाषित बाध्यकारी बॉक्स नहीं है, इसलिए आप फ़ोल्डरों का उपयोग नहीं करना चाहते हैं; जब डिफ़ॉल्ट रिक्त केस होता है तो फ़ोल्डर्स अच्छे होते हैं। या आपको Option के साथ फोल्ड करना होगा, लेकिन फिर आपके संयोजन फ़ंक्शन में समझने के लिए Some(box) साथ None गठबंधन कैसे, जो संभवत: इस मामले में इसके लायक नहीं है -। लेकिन बहुत अच्छी तरह से हो सकता है अगर आप उत्पादन कोड सुंदर ढंग से खाली सूची स्थितियों के विभिन्न प्रकार संभाल करने की जरूरत है कि लिख रहे थे)

+0

आपकी समस्या न केवल ऐसा लगता है कि आप स्कैला वाक्यविन्यास को नहीं जानते हैं। सबसे पहले, पता लगाएं कि गणितीय रूप से क्या होना चाहिए। फिर चिंता करें कि इसे भाषा कैसे लिखना है। गलत काम करने के लिए सही वाक्यविन्यास का उपयोग करना सहायक नहीं है! आपको एक फ़ंक्शन या विधि की आवश्यकता है जो दो बाउंडिंग बॉक्स और इनपुट ले सकता है और आउटपुट के रूप में दोनों के बाउंडिंग बॉक्स का उत्पादन कर सकता है। 'एएक्स + आरएक्स' उस कोने को नहीं रखेगा जहां आप इसे चाहते हैं। यदि आप एक तस्वीर खींचते हैं और गणित को समझते हैं, तो आप वहां से अधिकतर तरीके से हैं। –

4

बुनियादी एल्गोरिथ्म इस तरह जाना होगा:

shapes.tail.foldLeft(boundingBox(shapes.head)) { 
    case (box, shape) if box contains shape => box 
    case (box, shape) if shape contains box => shape 
    case (box, shape) => boxBounding(box, shape) 
} 

अब आप contains और boxBounding लिखने के लिए है, जो एक शुद्ध एल्गोरिदम समस्या है एक भाषा समस्या से अधिक lem।

यदि आकार सभी का एक ही केंद्र था, contains लागू करना आसान होगा। यह इस तरह जाना होगा:

abstract class Shape { def contains(s: Shape): Boolean } 
case class Rectangle(width: Int, height: Int) extends Shape { 
    def contains(s: Shape): Boolean = s match { 
    case Rectangle(w2, h2) => width >= w2 && height >= h2 
    case Location(x, y, s) => // not the same center 
    case Circle(radius) => width >= radius && height >= radius 
    case Group(shapes @ _*) => shapes.forall(this.contains(_)) 
    } 
} 
case class Location(x: Int, y: Int, shape: Shape) extends Shape { 
    def contains(s: Shape): Boolean = // not the same center 
} 
case class Circle(radius: Int) extends Shape { 
    def contains(s: Shape): Boolean = s match { 
    case Rectangle(width, height) => radius >= width && radius >= height 
    case Location(x, y) => // not the same center 
    case Circle(r2) => radius >= r2 
    case Group(shapes @ _*) => shapes.forall(this.contains(_)) 
    } 
} 
case class Group(shapes: Shape*) extends Shape { 
    def contains(s: Shape): Boolean = shapes.exists(_ contains s) 
} 

boxBounding है, जो दो आकार लेता है और उन्हें गठबंधन का सवाल है, यह आम तौर पर एक आयत होगा, लेकिन निश्चित circunstances के तहत एक चक्र हो सकता है। वैसे भी, यह एल्गोरिदम का पता लगाने के बाद, यह बहुत सीधी आगे है।

+0

आपके द्वारा प्राप्त समूह वर्ग की 'शामिल' विधि एक बाउंडिंग बॉक्स की गणना करने में सहायक नहीं है (चाहे आप जोर देते हैं कि यह एक बॉक्स है या नहीं)। एक बिंदु एक्स ए 1 यू ए 2 यू में निहित है ... यू एएन iff वहाँ एक आईआई मौजूद है जैसे एक्स एक आई में है। 'forall' के लिए x उनमें से प्रत्येक में होना आवश्यक है (और निश्चित रूप से, आप इसे पूरे ऑब्जेक्ट के लिए आवश्यक हैं, हर बिंदु पर नहीं)। वास्तव में संघ की गणना करने के बजाय आप कम से कम 'खोज' का उपयोग कर सकते हैं। लेकिन, एक तरफ, मुझे लगता है कि यह स्कैला का उपयोग करने का एक निर्देशक उदाहरण है। –

+0

हां, मैंने उस भाग का पालन किया।मैं केवल 'ढूंढने' के लिए ऑब्जेक्ट कर रहा था जिसे आपने तय किया था - जैसा कि मैंने सुझाव दिया था 'खोजने' के साथ कम-उपयोगी रूप से 'अस्तित्व' के साथ सही ढंग से। –

+0

@Rex आह, ठीक है। अब जब मैं आपकी टिप्पणी दोबारा पढ़ता हूं, मुझे एहसास है कि आप 'ग्रुप' के 'शामिल' के बारे में बात कर रहे थे, 'ग्रुप' के बजाय विभिन्न 'युक्त' पर। :-) –

258

अब जब आपने लगभग पूरी तरह से अलग प्रश्न पूछने के लिए संपादित किया है, तो मैं एक अलग उत्तर दूंगा। नक्शे और फ़ोल्डरों पर एक ट्यूटोरियल को इंगित करने के बजाय, मैं बस एक दे दूंगा।

स्कैला में, आपको सबसे पहले अज्ञात फ़ंक्शन बनाने का तरीका जानने की आवश्यकता है। यह अधिक विशिष्ट करने के लिए तो जैसे चला जाता है, सबसे सामान्य से:

(var1: Type1, var2: Type2, ..., varN: TypeN) => /* output */ 
(var1, var2, ..., varN) => /* output, if types can be inferred */ 
var1 => /* output, if type can be inferred and N=1 */ 

यहाँ कुछ उदाहरण हैं:

(x: Double, y: Double, z: Double) => Math.sqrt(x*x + y*y + z*z) 
val f:(Double,Double)=>Double = (x,y) => x*y + Math.exp(-x*y) 
val neg:Double=>Double = x => -x 

अब, सूचियों के map विधि और इस तरह के एक समारोह (गुमनाम या अन्यथा) लागू होगी मानचित्र के हर तत्व। यही कारण है कि, अगर आप

List(a1,a2,...,aN) 
f:A => B 

है तो

List(a1,a2,...,aN) map (f) 

पैदा करता

List(f(a1) , f(a2) , ..., f(aN)) 

कारण यह उपयोगी हो सकता है के सभी प्रकार के होते हैं। हो सकता है कि आपके पास तारों का एक गुच्छा हो और आप जानना चाहते हैं कि प्रत्येक कितना समय है, या आप उन्हें सभी ऊपरी मामले बनाना चाहते हैं, या आप उन्हें पीछे की तरफ चाहते हैं।

scala> List("How","long","are","we?") map (s => s.length) 
res0: List[Int] = List(3, 4, 3, 3) 

scala> List("How","capitalized","are","we?") map (s => s.toUpperCase) 
res1: List[java.lang.String] = List(HOW, CAPITALIZED, ARE, WE?) 

scala> List("How","backwards","are","we?") map (s => s.reverse) 
res2: List[scala.runtime.RichString] = List(woH, sdrawkcab, era, ?ew) 

तो, कि सामान्य रूप में नक्शा है, और स्काला में: यदि आप एक समारोह है, तो क्या आप एक तत्व को, नक्शे सभी तत्वों के लिए यह करना होगा चाहते हैं करता है।

लेकिन अगर हम अपने परिणाम एकत्र करना चाहते हैं तो क्या होगा? यही वह जगह है जहां गुना आता है (foldLeft जो संस्करण बाईं ओर से शुरू होता है और सही काम करता है)।

मान लीजिए कि हमारे पास एक कार्य f:(B,A) => B है, यानी, यह बी और ए लेता है, और उन्हें बी बनाने के लिए जोड़ता है, ठीक है, हम बी के साथ शुरू कर सकते हैं, और उसके बाद ए की हमारी सूची को खिला सकते हैं एक समय, और इसके अंत में, हमारे पास कुछ बी होगा। यह वही है जो ठीक है। foldLeft यह सूची के बाईं ओर से शुरू होता है; foldRight दाएं से शुरू होता है। यही कारण है,

List(a1,a2,...,aN) foldLeft(b0)(f) 

f(f(... f(f(b0,a1) , a2) ...), aN) 

जहां b0 ज़ाहिर है, है, अपने प्रारंभिक मूल्य पैदा करता है।

तो, शायद हमारे पास एक ऐसा फ़ंक्शन है जो एक int और स्ट्रिंग लेता है, और स्ट्रिंग की int या लंबाई देता है, जो भी अधिक हो - यदि हम इसका उपयोग करके हमारी सूची को जोड़ते हैं, तो यह हमें सबसे लंबी स्ट्रिंग बताएगा (मानते हुए कि हम 0 से शुरू करते हैं)। या हम int के रूप में लंबाई जोड़ सकते हैं, मूल्यों को जमा करते समय हम जाते हैं।

चलिए इसे आज़माएं।

scala> List("How","long","is","longest?").foldLeft(0)((i,s) => i max s.length) 
res3: Int = 8 

scala> List("How","long","is","everyone?").foldLeft(0)((i,s) => i + s.length) 
res4: Int = 18 

ठीक है, ठीक है, लेकिन क्या हम जानना चाहते हैं जो सबसे लंबे समय तक है चाहते हैं? एक तरीका (शायद सबसे अच्छा नहीं, लेकिन यह एक उपयोगी पैटर्न को अच्छी तरह से दिखाता है) लंबाई (एक पूर्णांक) और दोनों प्रमुख दावेदार (एक स्ट्रिंग) के साथ ले जाने के लिए है।के कि एक जाना देता हूँ:

scala> List("Who","is","longest?").foldLeft((0,""))((i,s) => 
    | if (i._1 < s.length) (s.length,s) 
    | else i 
    |) 
res5: (Int, java.lang.String) = (8,longest?) 

यहाँ, i अब प्रकार (Int,String) की एक टपल है, और i._1 कि टपल (एक इंट) का पहला हिस्सा है।

लेकिन कुछ मामलों में, एक गुना का उपयोग करना वास्तव में हम नहीं चाहते हैं। यदि हम दो तारों के लंबे समय तक चाहते हैं, तो सबसे प्राकृतिक कार्य max:(String,String)=>String जैसा होगा। हम इसे कैसे लागू करते हैं?

ठीक है, इस मामले में, एक डिफ़ॉल्ट "सबसे छोटा" मामला है, इसलिए हम स्ट्रिंग-अधिकतम फ़ंक्शन को "" से शुरू कर सकते हैं। लेकिन को कम करने का एक बेहतर तरीका है। गुना के साथ, दो संस्करण हैं, जो बाएं से काम करता है, दूसरा जो दाईं ओर से काम करता है। इसमें कोई प्रारंभिक मूल्य नहीं होता है, और एक फ़ंक्शन f:(A,A)=>A की आवश्यकता होती है। यही है, इसमें दो चीजें होती हैं और एक ही प्रकार में से एक लौटाती है। स्ट्रिंग-मैक्स फ़ंक्शन के साथ एक उदाहरण यहां दिया गया है:

scala> List("Who","is","longest?").reduceLeft((s1,s2) =>    
    | if (s2.length > s1.length) s2 
    | else s1 
    |) 
res6: java.lang.String = longest? 

अब, केवल दो और चाल हैं।

list.foldLeft(b0)(f) 
(b0 /: list)(f) 

सूचना कैसे दूसरे में कम है, और यह एक तरह से आप धारणा है कि आप b0 ले जा रहे हैं और इसके साथ सूची में कुछ कर रही है (जो आप कर रहे हैं देता है: सबसे पहले, निम्न दो एक ही बात मतलब)। (:\foldRight के रूप में ही है, लेकिन आप तो की तरह उपयोग: (list :\ b0) (f)

दूसरा, अगर आप केवल एक बार एक चर का उल्लेख, आप चर नाम के बजाय _ का उपयोग करें और अज्ञात फ़ंक्शन घोषणा के x => भाग को छोड़ सकते हैं ।। यहां दो उदाहरण हैं:, आप कार्यों और नक्शा बनाने के लिए, गुना, और उन्हें स्काला का उपयोग कर को कम इस प्रकार, यदि आप जानते हैं कि कैसे अपने एल्गोरिथ्म काम करना चाहिए सक्षम होना चाहिए

scala> List("How","long","are","we?") map (_.length) 
res7: List[Int] = List(3, 4, 3, 3) 

scala> (0 /: List("How","long","are","we","all?"))(_ + _.length) 
res8: Int = 16 

इस बिंदु पर, यह काफी होना चाहिए इसे कार्यान्वित करने के लिए सीधा।

+22

+1 मेरी इच्छा है कि मैं दो बार वोट दे सकता हूं .. –

+1

बिल्कुल सही जवाब, इससे मुझे – qui

+1

ओह की मदद मिली है। मेरे। परमेश्वर। +200। – slezica

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