2013-03-02 6 views
5

क्या कोई स्काला फ़ंक्शन कॉल करने का कोई तरीका है जो अलग-अलग मानकों को लेता है, एक सरणी (ईसीएमएस्क्रिप्ट 6 में जावास्क्रिप्ट Spreads के समान)?'फैल' पैरामीटर?

ys = [10.0, 2.72, -3.14] 
f(x, ...ys); 

साफ वाक्य रचना होगा:

f(1, ys) 

लेकिन यह संभव हो सकता है प्रतीत नहीं होता है। यहां तक ​​कि f(1, ys:_*) काम नहीं करता है (और न तो f(ys:_*) करता है, क्योंकि संकलक बहुत कम पैरामीटर रिपोर्ट करता है - केवल पहला भरा हुआ है)।

उदाहरण:

def f (a:Int, b:Float, c:Float, d:Float): String 

val x = 1 
val ys = List(10.0, 2.72, -3.14) // note: non-Objects 
val result = f(x, ys) // intuitive, but doesn't work 

उपयोग की स्थिति: मौजूदा तरीकों है कि व्यक्ति पैरामीटर स्वीकार में (संग्रह से) परीक्षण डाटा इंजेक्शन। चूंकि ये परीक्षण के मामले हैं, यह ठीक है अगर ys में #params मेल नहीं खाता है और यह या तो रनटाइम त्रुटि या गलत परिणाम देता है।

सवाल यह है कि क्या स्काला एक पैरामीटर को कॉल करने के लिए एक साफ वाक्यविन्यास की अनुमति देता है जो पैरामीटर के संग्रह के बाद व्यक्तिगत पैरामीटर लेता है - चाहे वह अच्छा डिज़ाइन (हालांकि राय निश्चित रूप से स्वागत है)।

+4

यदि ys' में 3 से कम तत्व या उससे अधिक हो तो क्या होना चाहिए? – huynhjl

+0

फ़ंक्शन 'f' को ओवरलोड करना दिमाग में आता है। इससे आपको आपके द्वारा प्रदान किए गए तरीके से फ़ंक्शन को कॉल करने की अनुमति मिल जाएगी, लेकिन यह अन्य स्थितियों में –

उत्तर

3

एक टुपल के रूप में एक सूची को पास करना आसान नहीं है, क्योंकि प्रकार बहुत अच्छी तरह मेल नहीं खाते हैं (बाद में इस पर अधिक)। पर्याप्त shoehorning और चिकनाई कुछ भी साथ हालांकि फिट बैठता है:

"My hack" should { 
    "allow a function to be called with Lists" in { 

    def function(bar:String, baz:String)= bar+baz 


    //turn the function into something that accepts a tuple 
    val functionT = function _ 
    val functionTT = functionT.tupled 

    val arguments = List("bar", "baz") 

    //Give the compiler a way to try and turn a list into a tuple of the size you need 
    implicit def listToTuple(args:List[String]) = args match { 
     case List(a, b) => (a, b) 
     case _ => throw IllegalArgumentException("Trying to pass off %s as a pair".format(args)) 
    } 

    //Shove it in and hope for the best at runtime 
    val applied = functionTT(arguments) 
    applied === "barbaz" 
    } 
} 

आप, या दो अलग-अलग समूहों में तर्क Schönfinkling द्वारा सूची में अतिरिक्त तर्कों जोड़कर इस दृष्टिकोण का विस्तार कर सकते हैं। मैं उस तरह से नहीं जाऊंगा।

मेरी टिप्पणियों से आपने देखा होगा कि मुझे यह डिज़ाइन पसंद नहीं है जो इस प्रश्न को पॉप अप करने का कारण बनता है। मैंने जो कोड दिखाया वह अनिवार्य रूप से कोड है जो फ़ंक्शन को किसी भी तरह से लपेट रहा है। यह ठीक से क्यों नहीं करते?

स्प्रे को देखते हुए आप देख सकते हैं कि उनकी पूरी विधि अलग-अलग मानकों के एक टन को स्वीकार करती है। इस निफ्टी चाल के लिए उन्होंने इसका उपयोग किया है, उन्होंने Magnet Pattern नाम दिया है। आप वही काम कर सकते हैं और अपने चुंबक को अलग-अलग टुपल्स के लिए निहित रूपांतरण लागू कर सकते हैं जिन्हें आप स्वीकार करना चुनते हैं।

+0

+1 रचनात्मकता और एक काफी साफ समाधान के लिए विफल हो जाएगी। लेकिन किसी भी विचार को कैसे करना है) उदाहरण के रूप में, tuple से पहले अधिक तर्क के साथ समारोह को बुलाओ? और बी) इसे किसी भी पैरामीटर के लिए काम करने की अनुमति देता है (केवल दो या तीन या कितने मामलों के रूप में हार्ड-कोडित नहीं हैं)? –

+1

यदि आपको किसी भी पैरामीटर की आवश्यकता है, तो आपको कोड जनरेशन या प्रतिबिंब की आवश्यकता है। अतिरिक्त पैरामीटर के साथ आप सूची में हेरफेर कर सकते हैं, या पूरी चीज को निहित रूपांतरण में संभाल सकते हैं। मैं कुछ संकेत देने के लिए उत्तर अपडेट करूंगा, लेकिन मैं मुख्य रूप से एक बेहतर विकल्प जोड़ूंगा। – iwein

+1

2.10 में नई मैक्रो सुविधाओं के साथ ऐसा करने का एक तरीका है। मेरे पास इसे कोड करने का समय नहीं है। यद्यपि आप कुछ लागू कर सकते हैं जैसे कि (एफ, तर्क ...) और परिणाम जो आप ढूंढ रहे हैं (प्रकार सुरक्षा के साथ) प्राप्त करें। – jroesch

3

मुझे लगता है कि आपको टाइप सुरक्षा को फेंकना होगा और प्रतिबिंब का उपयोग करना होगा।

scala> object Foo { 
| def doFoo(i:Int, s:String) = println(s * i) 
| } 

defined module Foo 

scala> val fooFunc = Foo.doFoo _ 
fooFunc: (Int, String) => Unit = <function2> 

scala> val applyMeth = fooFunc.getClass.getMethods()(0) 
applyMeth: java.lang.reflect.Method = public final void $anonfun$1.apply(int,java.lang.String) 

scala> val i:Integer = 5 

i: Integer = 5 

scala> val seq = Seq[Object](i,"do the foo! ") 
seq: Seq[Object] = List(5, "do the foo! ") 

scala> applyMeth.invoke(fooFunc, seq :_*) 
do the foo! do the foo! do the foo! do the foo! do the foo! 
res59: Object = null 

हालांकि, जब तक कि आप कुछ डीएसएल नहीं बना रहे हैं और वास्तव में इस तरह की सुविधा की आवश्यकता है, तो मैं एक और तरीका खोजने की कोशिश करता हूं। या तो विधियों को अधिभारित करें यदि यह आपके नियंत्रण में है या इसे कुछ मुखौटा वर्ग में लपेटें।

संपादित

टिप्पणी में Rubistro के सवालों answere करने के लिए।

ए) यह तकनीक foo.doFoo को कॉल करने के लिए कैसे काम करेगी? (यानी, आपके पास कोई ऑब्जेक्ट नहीं है - केवल एक वर्ग का एक उदाहरण जो डूफू को परिभाषित करता है।)

val fooFunc एक Function2 का एक उदाहरण यह है कि उदाहरण के समारोह जब applyMeth.invoke(fooFunc, seq :_*) लागू बुलाया जाता है कि लागू है।

बी) क्या यह विधि पैरामीटर को मूल्यों से पहले और बाद में करने के लिए पैरामीटर को पारित करने की अनुमति देती है?

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

class InvokationBuilder(meth:java.lang.reflect.Method, args: List[Object] = Nil) { 
    def invoke(instance:Object) = meth.invoke(instance, args.toSeq :_*) 
    def append(arg:Object) = new InvokationBuilder(meth, args :+ arg) 
    def prepend(arg:Object)= new InvokationBuilder(meth, arg :: args) 
} 
+1

पर रखूंगा ए) यह तकनीक foo.doFoo को कॉल करने के लिए कैसे काम करेगी? (यानी, आपके पास कोई ऑब्जेक्ट नहीं है - केवल एक वर्ग का एक उदाहरण है जो डूफू को परिभाषित करता है।) बी) क्या यह विधि पैरामीटर को * seq' में * * के बाद * और * के बाद पास करने की अनुमति देती है? –

+0

@Rubistro मैंने आपके प्रश्नों के बारे में कुछ और स्पष्टीकरण के साथ answere अद्यतन किया। –

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