2012-04-18 9 views
6

कहते हैं कि मैं एक समारोह के एक तर्कस्काला समारोह परिवर्तन

def fun(x: Int) = x 

लेने उस आधार पर मिल गया है, मैं एक ही बुला सम्मेलन के साथ एक नया समारोह उत्पन्न करना चाहते हैं, लेकिन वह अपने तर्कों के लिए कुछ परिवर्तन लागू कर देंगे मूल समारोह में प्रतिनिधि से पहले। उसके लिए, मैं कर सकता

def wrap_fun(f: (Int) => Int) = (x: Int) => f(x * 2) 
wrap_fun(fun)(2) // 4 

एक ही बात कर के बारे में कैसे जा सकते हैं, किसी भी arity कि केवल तर्क का हिस्सा आम में करने के लिए परिवर्तन लागू करने के के कार्यों को छोड़कर?

def fun1(x: Int, y: Int) = x 
def fun2(x: Int, foo: Map[Int,Str], bar: Seq[Seq[Int]]) = x 

wrap_fun(fun1)(2, 4) // 4 
wrap_fun(fun2)(2, Map(), Seq()) // 4 

कैसे एक wrap_fun परिभाषा ऊपर आमंत्रण बनाने की तरह लग रहे काम करेंगे?

+0

Fwiw, ऐसी चीजें गतिशील भाषाओं में वास्तव में सरल हो सकती हैं: http://ideone.com/MYP2W। – missingfaktor

उत्तर

6

इस समारोह arity से अधिक सार संक्षेप तक काफी हद तक सीधी shapeless's सुविधाओं का उपयोग करने में किया जा सकता है,

import shapeless._ 
import HList._ 
import Functions._ 

def wrap_fun[F, T <: HList, R](f : F) 
    (implicit 
    hl : FnHListerAux[F, (Int :: T) => R], 
    unhl : FnUnHListerAux[(Int :: T) => R, F]) = 
     ((x : Int :: T) => f.hlisted(x.head*2 :: x.tail)).unhlisted 

val f1 = wrap_fun(fun _) 
val f2 = wrap_fun(fun1 _) 
val f3 = wrap_fun(fun2 _) 

नमूना आरईपीएल सत्र,

scala> f1(2) 
res0: Int = 4 

scala> f2(2, 4) 
res1: Int = 4 

scala> f3(2, Map(), Seq()) 
res2: Int = 4 

ध्यान दें कि आप लिपटे समारोह तुरंत लागू नहीं कर सकते (प्रश्न में के रूप में) के बजाय एक नियत वैल के माध्यम से की तुलना में (जैसा कि मैंने ऊपर किया है), क्योंकि लिपटे समारोह का स्पष्ट तर्क सूची wrap_fun की अंतर्निहित तर्क सूची के साथ भ्रमित हो जाएगा। निकटतम हम विचाराधीन फार्म के लिए प्राप्त कर सकते हैं स्पष्ट रूप से नीचे के रूप में apply विधि,

scala> wrap_fun(fun _).apply(2) 
res3: Int = 4 

scala> wrap_fun(fun1 _).apply(2, 4) 
res4: Int = 4 

scala> wrap_fun(fun2 _).apply(2, Map(), Seq()) 
res5: Int = 4 

यहाँ apply का स्पष्ट उल्लेख वाक्य रचना (अपने निहित तर्क सूची के साथ wrap_fun की) पहली आवेदन बंद के निशान नाम के लिए है दूसरे एप्लिकेशन से (इसके स्पष्ट तर्क सूची के साथ परिवर्तित फ़ंक्शन का)।

+0

यह बहुत अच्छा है! क्या गिटहब पेज पर (विशेष रूप से, 'FnHListerAux' और' FnUnHListerAux') के अधिक स्पष्ट दस्तावेज प्राप्त करने की संभावना है (इसे रीडमे में या विकी पर रखें)? – Destin

+0

धन्यवाद। हां, यह मेरी TODO सूची पर है, लेकिन यदि आपके पास समय और झुकाव दस्तावेज के लिए पुल अनुरोध बहुत स्वागत है :-) –

2

चूंकि विभिन्न तर्कों को लेते हुए कार्य अलग-अलग, असंबद्ध प्रकार हैं, आप इसे सामान्य रूप से नहीं कर सकते हैं। trait Function1 [-T1, +R] extends AnyRef और कुछ भी नहीं। आपको प्रत्येक धर्मार्थ के लिए एक अलग विधि की आवश्यकता होगी।

+2

मैं नहीं कहूंगा 'नहीं कर सकता'। [बेकार] (https://github.com/milessabin/shapeless) में arity पर सारण करने के लिए कुछ शानदार विशेषताएं हैं। चूंकि [liftO] (https://github.com/milessabin/shapeless/blob/master/src/main/scala/shapeless/lift.scala) मनमाना arity के कार्यों के साथ काम कर सकते हैं, यह संभव होना चाहिए। – leedm777

+1

@dave निराकार सभी संभव मामलों लिख कर यह संभव बनाता है - दोनों tuples और कार्यों केवल 22 arity तक जा, क्योंकि स्काला उन पर सार नहीं या तो है, तो हर एक को परिभाषित किया जाना है। –

+1

@daniel नहीं, सभी मामलों की गणना करने में निराकार के साथ ऐसा करना ... मेरा उत्तर को देखने की आवश्यकता नहीं है। –

1

जबकि मैंने लुइगी के उत्तर के लिए वोट दिया और सहमति व्यक्त की- क्योंकि, आप जानते हैं ... वह सही है; Scala इस तरह की चीज़ के लिए प्रत्यक्ष, अंतर्निहित समर्थन नहीं है-यह ध्यान देने योग्य है कि आप जो करने का प्रयास कर रहे हैं वह असंभव नहीं है; यह सिर्फ इतना है कि इसे खींचने के लिए दर्द का थोड़ा सा दर्द होता है, और, अक्सर, आप वांछित धैर्य प्रति एक अलग विधि को लागू करने के लिए सबसे अच्छे हैं।

उसने कहा, हालांकि ... हम वास्तव में यह HList एस कर सकते हैं। यदि आप इसे आज़माने में रुचि रखते हैं, तो स्वाभाविक रूप से, आपको HList कार्यान्वयन प्राप्त करने की आवश्यकता होगी। मैं माइल्स सबिन के उत्कृष्ट shapeless प्रोजेक्ट और HList एस के कार्यान्वयन का उपयोग करने की सलाह देता हूं। वैसे भी, यहाँ है कि कुछ है कि तुम क्या लगता है के लिए समान सिद्ध इसके उपयोग का एक उदाहरण है की तलाश में जा रहे हैं:

import shapeless._ 

trait WrapperFunner[T] { 
    type Inputs <: HList 
    def wrapFun(inputs: Inputs) : T 
} 

class WrapsOne extends WrapperFunner[Int] { 
    type Inputs = Int :: HNil 
    def wrapFun(inputs: Inputs) : Int = { 
    inputs match { 
     case num :: HNil => num * 2 
    } 
    } 
} 

class WrapsThree extends WrapperFunner[String] { 
    type Inputs = Int :: Int :: String :: HNil 
    def wrapFun(inputs: Inputs) : String = { 
    inputs match { 
     case firstNum :: secondNum :: str :: HNil => str + (firstNum - secondNum) 
    } 
    } 
} 

object MyApp extends App { 

    val wo = new WrapsOne 
    println(wo.wrapFun(1 :: HNil)) 
    println(wo.wrapFun(17 :: HNil)) 
    //println(wo.wrapFun(18 :: 13 :: HNil)) // Would give type error 

    val wt = new WrapsThree 
    println(wt.wrapFun(5 :: 1 :: "your result is: " :: HNil)) 
    val (first, second) = (60, 50) 
    println(wt.wrapFun(first :: second :: "%s minus %s is: ".format(first, second) :: HNil)) 
    //println(wt.wrapFun(1 :: HNil)) // Would give type error 

} 

में चल रहा है MyApp परिणाम:

2 
34 
your result is: 4 
60 minus 50 is: 10 

या, अपने विशेष मामले के करीब बढ़ा:

import shapeless._ 

trait WrapperFunner[T] { 
    type Inputs <: HList 
    def wrapFun(inputs: Inputs) : T 
} 

trait WrapperFunnerBase extends WrapperFunner[Int] { 
    // Does not override `Inputs` 
    def wrapFun(inputs: Inputs) : Int = { 
    inputs match { 
     case (num: Int) :: remainder => num 
    } 
    } 
} 

class IgnoresNothing extends WrapperFunnerBase { 
    type Inputs = Int :: HNil 
} 

class IgnoresLastTwo extends WrapperFunnerBase { 
    type Inputs = Int :: Int :: String :: HNil 
} 

object MyApp extends App { 

    val in = new IgnoresNothing 
    println(in.wrapFun(1 :: HNil)) 
    println(in.wrapFun(2 :: HNil)) 
    //println(in.wrapFun(3 :: 4 :: HNil)) // Would give type error 

    val ilt = new IgnoresLastTwo 
    println(ilt.wrapFun(60 :: 13 :: "stupid string" :: HNil)) 
    println(ilt.wrapFun(43 :: 7 :: "man, that string was stupid..." :: HNil)) 
    //println(ilt.wrapFun(1 :: HNil)) // Would give type error 

} 

परिणामों में:

1 
2 
60 
43 
+0

आपने वास्तव में इसका मतलब थोड़ा सा बना दिया है मुझे डर है! आकारहीन का उपयोग करके एक बहुत ही सरल समाधान के लिए मेरा जवाब देखें। –

+0

ओह ... एस/माध्य/भोजन/ –

+0

@ माइल्सस्बिन हां, मुझे उम्मीद है कि मेरा तरीका सबसे अच्छा संभव नहीं होगा। एक बहुत बेहतर समाधान की पेशकश के लिए धन्यवाद! – Destin

6

सामान्य रूप से स्कैला में, आप जो करना चाहते हैं उसे हासिल करने का एक और तरीका है।

यहाँ एक ले Function1 की compose के साथ मिलकर पहले तर्क की currying पर आधारित है:

def fun1(x : Int)(y : Int) = x 
def fun2(x : Int)(foo : Map[Int, String], bar : Seq[Seq[Int]]) = x 

def modify(x : Int) = 2*x 

जिसके परिणामस्वरूप प्रकार के रूप में आरईपीएल आपको दिखाता है कि हो जाएगा:

fun1: (x: Int)(y: Int)Int 
fun2: (x: Int)(foo: Map[Int,String], bar: Seq[Seq[Int]])Int 
modify: (x: Int)Int 

और रैपिंग के बजाय फ़ंक्शन fun1 और fun2, आप compose उन्हें तकनीकी रूप से, वे अब Function1 ऑब्जेक्ट्स दोनों हैं।

(fun1 _ compose modify)(2)(5) 
(fun2 _ compose modify)(2)(Map(), Seq()) 
जो दोनों

, आप समारोह से fun1 के आवेदन भेद करने के लिए _ जोड़ने के लिए है कि 4. दी वापस आ जाएगी, वाक्य रचना कि अच्छा नहीं है दिया: यह आप की तरह निम्नलिखित कॉल करने के लिए अनुमति देता है ऑब्जेक्ट स्वयं (जिस पर आप इस मामले में compose विधि को कॉल करना चाहते हैं)।

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