2012-03-15 10 views
7

मैं scalaz-seven में traverseImpl कार्यान्वयन को समझने के लिए कोशिश कर रहा हूँ में Traverse [सूची] कार्यान्वयन:के बारे में बताएं scalaz-सात

def traverseImpl[F[_], A, B](l: List[A])(f: A => F[B])(implicit F: Applicative[F]) = { 
    DList.fromList(l).foldr(F.point(List[B]())) { 
    (a, fbs) => F.map2(f(a), fbs)(_ :: _) 
    } 
} 

कोई व्याख्या कर सकते हैं कि कैसे ListApplicative साथ सूचना का आदान? आखिरकार, मैं Traverse के लिए अन्य उदाहरणों को लागू करने में सक्षम होना चाहता हूं।

उत्तर

9

एक आवेदक आपको संदर्भ में किसी संदर्भ में किसी संदर्भ में फ़ंक्शन लागू करने देता है। तो उदाहरण के लिए, आप some((i: Int) => i + 1) को some(3) पर लागू कर सकते हैं और some(4) प्राप्त कर सकते हैं। चलो अब के लिए भूल जाओ। मैं बाद में वापस आऊंगा।

सूची में दो प्रतिनिधित्व हैं, यह या तो Nil या head :: tail है। आप foldLeft का उपयोग कर इस पर गुना करने के लिए इस्तेमाल किया जा सकता है लेकिन वहाँ एक और तरीका से अधिक गुना करने के लिए है यह:

def foldr[A, B](l: List[A], acc0: B, f: (A, B) => B): B = l match { 
    case Nil => acc0 
    case x :: xs => f(x, foldr(xs, acc0, f)) 
} 

List(1, 2) को देखते हुए हम समारोह दाईं ओर से शुरू लागू करने सूची पर गुना - भले ही हम वास्तव में सूची deconstruct बाईं तरफ से!

f(1, f(2, Nil)) 

इसका उपयोग किसी सूची की लंबाई की गणना करने के लिए किया जा सकता है। List(1, 2) को देखते हुए:

foldr(List(1, 2), 0, (i: Int, acc: Int) => 1 + acc) 
// returns 2 

यह भी एक और सूची बनाने के के लिए इस्तेमाल किया जा सकता है:

foldr[Int, List[Int]](List(1, 2), List[Int](), _ :: _) 
//List[Int] = List(1, 2) 

तो एक खाली सूची और :: समारोह हम एक और सूची बनाने के लिए सक्षम थे दिया।क्या होगा यदि हमारे तत्व कुछ संदर्भ में हैं? यदि हमारा संदर्भ एक आवेदक है तो हम उस संदर्भ में अभी भी हमारे तत्व और :: लागू कर सकते हैं। हमारे आवेदक के रूप में List(1, 2) और Option के साथ जारी है। हम some(List[Int]())) से शुरू करते हैं हम फ़ंक्शन को Option संदर्भ में लागू करना चाहते हैं। यह F.map2 करता है। Option संदर्भ में दो मान लेते हैं, Option संदर्भ में दो तर्कों के प्रदान किए गए फ़ंक्शन को रखें और उन्हें एक साथ लागू करें।

तो संदर्भ से बाहर हम संदर्भ में (2, Nil) => 2 :: Nil

है हमने: (Some(2), Some(Nil)) => Some(2 :: Nil)

वापस मूल प्रश्न के लिए जा रहे:

// do a foldr 
DList.fromList(l).foldr(F.point(List[B]())) { 
    // starting with an empty list in its applicative context F.point(List[B]()) 
    (a, fbs) => F.map2(f(a), fbs)(_ :: _) 
    // Apply the `::` function to the two values in the context 
} 

मैं क्यों अंतर DList प्रयोग किया जाता है यकीन नहीं है। मैं जो देखता हूं वह यह है कि यह ट्रैम्पोलिन का उपयोग करता है, इसलिए उम्मीद है कि यह कार्यान्वयन स्टैक को उड़ाने के बिना काम करता है, लेकिन मैंने कोशिश नहीं की है इसलिए मुझे नहीं पता।

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

उदाहरण के लिए दिए गए:

trait Tree[+A] 
object Leaf extends Tree[Nothing] 
case class Node[A](a: A, left: Tree[A], right: Tree[A]) extends Tree[A] 

फोल्ड इस तरह परिभाषित किया जा सकता है (जो वास्तव में List के लिए के रूप में ही दृष्टिकोण पीछा कर रहा है):

def fold[A, B](tree: Tree[A], valueForLeaf: B, functionForNode: (A, B, B) => B): B = { 
    tree match { 
    case Leaf => valueForLeaf 
    case Node(a, left, right) => functionForNode(a, 
     fold(left, valueForLeaf, functionForNode), 
     fold(right, valueForLeaf, functionForNode) 
    ) 
    } 
} 

और पार का प्रयोग करेंगे कि foldF.point(Leaf) साथ और इसे Node.apply पर लागू करें। हालांकि F.map3 नहीं है, इसलिए यह थोड़ा बोझिल हो सकता है।

+0

भयानक उत्तर जिसके लिए किसी भी बाहरी ज्ञान की आवश्यकता नहीं है – betehess

6

यह समझने में इतना आसान नहीं है। मैं my blog post on the subject की शुरुआत में जुड़े आलेख को पढ़ने की अनुशंसा करता हूं।

मैंने सिडनी में अंतिम कार्यात्मक प्रोग्रामिंग मीटिंग के दौरान इस विषय पर एक प्रस्तुति भी की और आप स्लाइड here पा सकते हैं।

मैं कुछ शब्दों में समझाने की कोशिश कर सकते हैं, traverse सूची के प्रत्येक तत्व पार करने के लिए जा रहा है एक के बाद एक, अंत में फिर से निर्माण कर सूची (_ :: _) लेकिन जमा/"प्रभाव" किसी तरह का क्रियान्वित द्वारा दिए गए के रूप में F Applicative। यदि FState है तो यह कुछ राज्यों का ट्रैक रखता है। यदि FMonoid से संबंधित आवेदक है तो यह सूची के प्रत्येक तत्व के लिए किसी प्रकार का उपाय एकत्र करता है।

सूची और अनुप्रयोगी के मुख्य बातचीत map2 आवेदन जहां यह एक F[B] तत्व प्राप्त करता है और F की परिभाषा के द्वारा अन्य F[List[B]] तत्वों के लिए यह एक Applicative और विशिष्ट के रूप में List निर्माता :: के उपयोग के रूप देते हैं के साथ है आवेदन करने के लिए समारोह।

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

+0

वास्तव में, आपके ब्लॉग लेख और आपकी स्लाइड दोनों उपयोगी हैं! – betehess

+0

ग्रेट स्लाइड्स! क्या आप इसे पीडीएफ बना सकते हैं? मुझे फोंट और संरेखण के साथ कुछ परेशान करने वाले मुद्दे मिल गए। – paradigmatic

+0

मैंने यहां पीडीएफ स्लाइड्स अपलोड की हैं: http://www.slideshare.net/etorreborre/the-essence-of-the-iterator-pattern-pdf – Eric

2

List#foldRight बड़ी सूचियों के लिए ढेर को उड़ाता है। एक आरईपीएल में इस प्रयास करें:

List.range(0, 10000).foldRight(())((a, b) =>()) 

आमतौर पर, आप सूची पलट सकता है, foldLeft उपयोग करते हैं, तो परिणाम रिवर्स इस समस्या से बचने के लिए। लेकिन traverse के साथ हमें यह सुनिश्चित करने के लिए वास्तव में सही क्रम में तत्वों को संसाधित करना होगा कि प्रभाव का सही ढंग से इलाज किया जाए। DList ट्रैम्पोलिनिंग के आधार पर ऐसा करने का एक सुविधाजनक तरीका है।

अंत में, इन परीक्षणों से गुजरना होगा:

https://github.com/scalaz/scalaz/blob/scalaz-seven/tests/src/test/scala/scalaz/TraverseTest.scala#L13 https://github.com/scalaz/scalaz/blob/scalaz-seven/tests/src/test/scala/scalaz/std/ListTest .scala # एल 11 https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/Traverse.scala#L76

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