2013-01-21 39 views
13

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

import shapeless._ 
val list1 = 1 :: "one" :: HNil 
val list2 = 2 :: "two" :: HNil 
// Has value (1, 2) :: (1, "two") :: ("one", 2) :: ("one", "two") :: HNil 
val list3 = allPairs(list1, list2) 

कोई विचार यह कैसे करना है?

इसके अलावा, मैं जोर देना चाहता हूं कि मैं फ़ंक्शन, कोड के एक रेखांकित ब्लॉक की तलाश में नहीं हूं।

+0

सटीक रूप से आप "_function_" और "कोड के एक रेखांकित ब्लॉक" के बीच क्या अंतर आकर्षित करते हैं? –

+1

@RandallSchulz: नीचे ट्रैविस के पहले उत्तर और लिफ्टए 2 का उपयोग करके उनके अंतिम उत्तर के बीच अंतर देखें। – emchristiansen

उत्तर

16

आप एक for -comprehension या map का एक संयोजन और समारोह शाब्दिक यहाँ के साथ flatMap उपयोग कर सकते हैं नहीं है (जैसा कि अन्य उत्तर सुझाव है), के बाद से HList पर इन तरीकों higher rank functions आवश्यकता होती है। तुम सिर्फ दो स्थिर टाइप किया सूचियों है, तो यह आसान है:

import shapeless._ 

val xs = 1 :: 'b :: 'c' :: HNil 
val ys = 4.0 :: "e" :: HNil 

object eachFirst extends Poly1 { 
    implicit def default[A] = at[A] { a => 
    object second extends Poly1 { implicit def default[B] = at[B](a -> _) } 
    ys map second 
    } 
} 

val cartesianProductXsYs = xs flatMap eachFirst 

निम्नलिखित (उचित रूप से टाइप किया) हमें देता है कौन सा:

(1,4.0) :: (1,e) :: ('b,4.0) :: ('b,e) :: (c,4.0) :: (c,e) :: HNil 

एक विधि है कि जटिल काम है HList तर्क के साथ यह कर देगा लेखन। यहां एक त्वरित उदाहरण दिया गया है कि यह कैसे किया जा सकता है (कुछ और अधिक सामान्य मशीनरी के साथ)।

मैं यह ध्यान में रखकर शुरू करूंगा कि हम दो सामान्य सूचियों के कार्टेशियन उत्पाद को "उठाने" के रूप में सोचने के बारे में सोच सकते हैं जो दो तर्क लेता है और उन्हें सूचियों के लिए आवेदक मज़ेदार में टुपल के रूप में लौटाता है। उदाहरण के लिए, आप the following in Haskell लिख सकते हैं:

import Control.Applicative (liftA2) 

cartesianProd :: [a] -> [b] -> [(a, b)] 
cartesianProd = liftA2 (,) 

हम बहुरूपी द्विआधारी समारोह है कि (,) यहाँ से मेल खाती है लिख सकते हैं:

val xs = 1 :: 'b :: 'c' :: HNil 
val ys = 4.0 :: "e" :: HNil 
:

import shapeless._ 

object tuple extends Poly2 { 
    implicit def whatever[A, B] = at[A, B] { case (a, b) => (a, b) } 
} 

और हमारे उदाहरण परिभाषित संपूर्णता के लिए फिर से सूचीबद्ध करता है

अब हम liftA2 नामक एक विधि की ओर काम करेंगे जो हमें निम्नलिखित लिखने की अनुमति देगा:

liftA2(tuple)(xs, ys) 

और सही परिणाम प्राप्त करें। नाम liftA2 थोड़ा भ्रामक है, क्योंकि हमारे पास वास्तव में कोई आवेदक फ़ैक्टर उदाहरण नहीं है, और चूंकि यह सामान्य नहीं है- मैं flatMap और map नामक विधियों के मॉडल पर काम कर रहा हूं HList पर, और कुछ के लिए सुझावों के लिए खुला हूं बेहतर।

trait ApplyMapper[HF, A, X <: HList, Out <: HList] { 
    def apply(a: A, x: X): Out 
} 

object ApplyMapper { 
    implicit def hnil[HF, A] = new ApplyMapper[HF, A, HNil, HNil] { 
    def apply(a: A, x: HNil) = HNil 
    } 
    implicit def hlist[HF, A, XH, XT <: HList, OutH, OutT <: HList](implicit 
    pb: Poly.Pullback2Aux[HF, A, XH, OutH], 
    am: ApplyMapper[HF, A, XT, OutT] 
) = new ApplyMapper[HF, A, XH :: XT, OutH :: OutT] { 
    def apply(a: A, x: XH :: XT) = pb(a, x.head) :: am(a, x.tail) 
    } 
} 

और अब के साथ मदद करने के लिए एक प्रकार वर्ग:

अब हम एक प्रकार वर्ग है कि हम एक HList पर एक Poly2 लेने के लिए, आंशिक रूप से कुछ करने के लिए इसे लागू होते हैं, और नक्शा जिसके परिणामस्वरूप एकल समारोह की अनुमति देगा की जरूरत है उठाने:

trait LiftA2[HF, X <: HList, Y <: HList, Out <: HList] { 
    def apply(x: X, y: Y): Out 
} 

object LiftA2 { 
    implicit def hnil[HF, Y <: HList] = new LiftA2[HF, HNil, Y, HNil] { 
    def apply(x: HNil, y: Y) = HNil 
    } 

    implicit def hlist[ 
    HF, XH, XT <: HList, Y <: HList, 
    Out1 <: HList, Out2 <: HList, Out <: HList 
    ](implicit 
    am: ApplyMapper[HF, XH, Y, Out1], 
    lift: LiftA2[HF, XT, Y, Out2], 
    prepend : PrependAux[Out1, Out2, Out] 
) = new LiftA2[HF, XH :: XT, Y, Out] { 
    def apply(x: XH :: XT, y: Y) = prepend(am(x.head, y), lift(x.tail, y)) 
    } 
} 

और अंत में हमारे विधि में ही:

def liftA2[HF, X <: HList, Y <: HList, Out <: HList](hf: HF)(x: X, y: Y)(implicit 
    lift: LiftA2[HF, X, Y, Out] 
) = lift(x, y) 

और यह सब अब liftA2(tuple)(xs, ys) काम करता है।

scala> type Result = 
    | (Int, Double) :: (Int, String) :: 
    | (Symbol, Double) :: (Symbol, String) :: 
    | (Char, Double) :: (Char, String) :: HNil 
defined type alias Result 

scala> val res: Result = liftA2(tuple)(xs, ys) 
res: Result = (1,4.0) :: (1,e) :: ('b,4.0) :: ('b,e) :: (c,4.0) :: (c,e) :: HNil 

जैसे हम चाहते थे।

+2

यह अच्छा है। क्या आप दिखा सकते हैं कि आप xs और ys को पैरामीटर के रूप में लेने के तरीके में इसे कैसे पैक करेंगे? मैंने इसे इस तरह से करने की कोशिश की और बुरी तरह विफल रही। –

+0

@ रेजीजियन-गिल्स: यह एक अच्छा सवाल है-यह पता चला है कि आपको किसी विधि को सामान्यीकृत करने के लिए कुछ और मशीनरी की आवश्यकता होगी। मैं अब इसमें एक स्टैब ले रहा हूँ। –

+0

सवाल एक समारोह के लिए पूछता है; मुझे लगता है कि यह कठिन हिस्सा है। – emchristiansen