2015-12-05 4 views
10

के साथ पैटर्न मिलान मैं आकारहीन coproducts के साथ पैटर्न मिलान का उपयोग कर सकते हैं?आकारहीन coproduct

import shapeless.{CNil, :+:} 

type ListOrString = List[Int] :+: String :+: CNil 

def f(a: ListOrString): Int = a match { 
    case 0 :: second :: Nil => second 
    case first :: Nil => first 
    case Nil => -1 
    case string: String => string.toInt 
} 

के पाठ्यक्रम काम नहीं करता, a के बाद से एक के रूप में Coproduct बॉक्सिंग है यही कारण है कि।

क्या प्रोडक्ट्स का उपयोग करने और पैटर्न मिलान की क्षमता बनाए रखने का कोई वैकल्पिक तरीका है?

उत्तर

14

आप पैटर्न मैच में Inl और Inr कंस्ट्रक्टर्स उपयोग कर सकते हैं: क्योंकि आप CNil मामले को संभालने के लिए अगर आप संकलक कि बताने के लिए सक्षम होने के लिए चाहते हैं

import shapeless.{ CNil, Inl, Inr, :+: } 

type ListOrString = List[Int] :+: String :+: CNil 

def f(a: ListOrString): Int = a match { 
    case Inl(0 :: second :: Nil) => second 
    case Inl(first :: Nil) => first 
    case Inl(Nil) => -1 
    case Inr(Inl(string)) => string.toInt 
} 

यह दृष्टिकोण आदर्श नहीं है मैच संपूर्ण-हम है कि यह संभव के लिए यह मामला मैच के लिए नहीं है, लेकिन संकलक नहीं है, तो हम कुछ इस तरह करना है:

def f(a: ListOrString): Int = a match { 
    case Inl(0 :: second :: Nil) => second 
    case Inl(first :: Nil) => first 
    case Inl(Nil) => -1 
    case Inl(other) => other.sum 
    case Inr(Inl(string)) => string.toInt 
    case Inr(Inr(_)) => sys.error("Impossible") 
} 

मैं भी व्यक्तिगत रूप से सिर्फ appro करने के लिए नेविगेट करने लगता है Inr और Inl के साथ प्रतिलिपि में priate स्थिति थोड़ा counterintuitive।

object losToInt extends shapeless.Poly1 { 
    implicit val atList: Case.Aux[List[Int], Int] = at { 
    case 0 :: second :: Nil => second 
    case first :: Nil => first 
    case Nil => -1 
    case other => other.sum 
    } 

    implicit val atString: Case.Aux[String, Int] = at(_.toInt) 
} 

def f(a: ListOrString): Int = a.fold(losToInt) 

अब संकलक आप असंभव मामलों को संभालने के लिए बिना exhaustivity सत्यापित करेंगे:

सामान्य तौर पर यह एक बहुरूपी समारोह मूल्य के साथ coproduct से अधिक गुना बेहतर है।

+0

मैं समझने के लिए 'मामले लीग (0 :: दूसरा :: शून्य) => second' वास्तव में है संघर्ष। यदि यह एक प्रोडक्ट है, निश्चित रूप से यह '0' और' second' दोनों नहीं हो सकता है तो यह मैच कब होता है? – fommil

+0

@fommil 'इनल' के अंदर का हिस्सा सिर्फ एक सादा पुरानी सूची से मेल खाता है, इसलिए यह किसी भी समय' प्रोडक्ट [ListOrString] (xs) 'से मेल खाता है, जहां 'xs' में वास्तव में दो तत्व हैं और' 0' है पहले वाला। –

+0

ओह, मैं देखता हूं, धन्यवाद – fommil

4

मैंने अभी बेकार एक पुल अनुरोध here सबमिट किया है जो आपकी आवश्यकताओं के लिए अच्छा काम कर सकता है। (ध्यान दें कि यह केवल एक पुल अनुरोध है और संशोधनों से गुजर सकता है या अस्वीकार कर दिया जा सकता है ... लेकिन अगर आप इसे उपयोगी पाते हैं तो मशीनरी लेना और इसे अपने कोड में इस्तेमाल करना स्वतंत्र महसूस करें।)

प्रतिबद्ध संदेश से:

[...] प्रकार इंट के Coproduct ग: +: स्ट्रिंग: +: बूलियन: +: CNIL एक डबल में तह किया जा सकता है इस प्रकार है:

val result = c.foldCases[Double] 
       .atCase(i => math.sqrt(i)) 
       .atCase(s => s.length.toDouble) 
       .atCase(b => if (b) 100.0 else -1.0) 

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

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

अपने उदाहरण के लिए, आप ऐसा कर सकता है:

def f(a: ListOrString): Int = a.foldCases[Int] 
    .atCase(list => list match { 
     case 0 :: second :: Nil => second 
     case first :: Nil => first 
     case Nil => -1 
     case other => other.sum 
    }) 
    .atCase(s => s.toInt) 
संबंधित मुद्दे