2011-09-30 12 views
20

में फ़ोल्डलिफ्ट का उपयोग करके करीबी फ़ंक्शन पर एक तर्क सूची लागू करना क्या तर्कों की सूची पर foldLeft करना संभव है, जहां गुना को दिया गया प्रारंभिक मान पूरी तरह से घुमावदार फ़ंक्शन है, ऑपरेटर apply है, और सूची है कार्य f पर पास किए जाने वाले तर्कों की एक सूची?स्कैला

scala> val f = (i: Int, j: Int, k: Int, l: Int) => i+j+k+l 
f: (Int, Int, Int, Int) => Int = <function4> 

कौन सा हम निश्चित रूप से सीधे उपयोग कर सकते हैं:

scala> f(1, 2, 3, 4) 
res1: Int = 10 

या करी और एक समय में तर्क लागू:

उदाहरण के लिए, मान लीजिए कि च परिभाषित किया गया है के रूप में करते हैं

scala> f.curried 
res2: Int => Int => Int => Int => Int = <function1> 

scala> f.curried.apply(1).apply(2).apply(3).apply(4) 
res3: Int = 10 

पहली नज़र में यह foldLeft के लिए नौकरी की तरह दिखता है।

मेरे foldLeft का उपयोग कर apply के इस क्रम का वर्णन पहला प्रयास लगता है:

scala> List(1, 2, 3, 4).foldLeft(f.curried)({ (g, x) => g.apply(x) }) 
हालांकि

, कि निम्न त्रुटि पैदावार:

<console>:9: error: type mismatch; 
found : Int => Int => Int => Int 
required: Int => Int => Int => Int => Int 
       List(1, 2, 3, 4).foldLeft(f.curried)({ (g, x) => g.apply(x) }) 

त्रुटि संदेश के मेरे पढ़ने कि प्रकार निष्कर्ष है g के लिए कुछ संकेत की आवश्यकता होगी।

समाधान के लिए मैं देख रहा हूँ सब कुछ अपने मूल अभिव्यक्ति में असंशोधित छोड़ देता है g के प्रकार को छोड़कर:

List(1, 2, 3, 4).foldLeft(f.curried)({ (g: ANSWER, x) => g.apply(x) }) 

मेरी पहली सोचा था कि एक संघ प्रकार यहाँ उपयोगी होगा। मैंने माइल्स सबिन को करी-हॉवर्ड का उपयोग करके यूनियन प्रकारों का व्युत्पन्न देखा है, इसलिए यदि वह पहला झुकाव सच है, तो मुझे समस्या को हल करने के लिए आवश्यक मूल मशीनरी दिखाई देती है।

हालांकि: यहां तक ​​कि यदि यूनियन प्रकार का उत्तर है तो यह उपयोगी होगा यदि मैं "सभी प्रकार के संघ को एक समारोह के पूरी तरह से घुमावदार प्रकार से सभी के साथ विवाहित कार्य के प्रकार के साथ संदर्भित करता हूं लेकिन अंतिम तर्क दिया गया है "। संघ प्रकार में

T1 => ... => Tn 

: दूसरे शब्दों में, एक तरह से प्रकार चालू करने के लिए

(T1 => ... => Tn) |∨| ... |∨| (Tn-1 => Tn) 

ऊपर g के लिए प्रकार के रूप में उपयोगी होगा।

एक List पर एक foldLeft कर चर्चा मामला है, जहां T1Tn-1 के माध्यम से सभी एक ही हैं सीमित करता है।

(T1 =>)+ Tn 

उस प्रकार का वर्णन करेगा जो मैं g के लिए प्रदान करना चाहता हूं।

विशेष मामले के बारे में मैं मनमाने ढंग से लंबी श्रृंखला की आवश्यकता नहीं है पूछ रहा हूँ, इसलिए हम

(T1 =>){1,4} Tn 

कि नहीं कर रहे हैं प्रकार की जंजीरों के लिए ऐसा करने के लिए इच्छुक में आगे देखते हुए का उपयोग कर पुनरावर्तक पर सीमा प्रदान कर सकता है बराबर है, हालांकि, शायद प्रकार पर कुछ जादुई समारोह है कि सभी प्रत्यय सेट में श्रृंखला को कांट-छांट और अधिक उपयोगी है:

Suffixes(T1 => ... => Tn) 

को लागू यह अच्छी तरह से इस समय मेरी स्काला क्षमता से परे है। ऐसा करने के बारे में कोई संकेत नहीं है कि इसकी सराहना की जाएगी। चाहे यह स्कैला के मौजूदा प्रकार सिस्टम या कंपाइलर प्लगइन के माध्यम से या न तो उन्नत उपयोग के साथ किया जा सकता है या नहीं, मुझे नहीं पता।

जैसा कि नीचे दी गई टिप्पणियों में उल्लेख किया गया है, परिणाम को "यूनियन टाइप" इस उपयोग के मामले के लिए एकदम सही फिट नहीं है। मुझे नहीं पता कि इसे और क्या कहना है, लेकिन इस समय मेरे पास सबसे नज़दीकी विचार है। क्या इस भाषा के लिए अन्य भाषाओं का विशेष समर्थन है? यह कोक और आगादा में कैसे काम करेगा?

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

+2

मुझे यकीन है कि संघ प्रकार यहां जाने का रास्ता नहीं है नहीं कर रहा हूँ। ऐसा लगता है कि आप एचएलिस्ट के रूप में प्रतिनिधित्व किए गए तर्कों पर करीबी कार्य की एक गुना जैसी चीज के बाद हैं। मुझे लगता है कि ऐसा कुछ संभवतः करने योग्य है। किसी भी तरह, यह एक दिलचस्प समस्या है ... यदि मैं कुछ भी काम करने योग्य हूं तो मैं जांच करूँगा और ठीक से जवाब दूंगा। –

+0

वाह - धन्यवाद, माइल्स। मुझे यह सुनना अच्छा लगेगा कि आप इस समस्या के बारे में क्या करते हैं। मैं मानता हूं कि यूनियन प्रकार, प्रति से, ऐसा नहीं लगता है कि इस स्थिति में क्या कहा जाता है। –

+0

@ माइल्ससबिन मैंने अपने प्रश्न को स्पष्ट किया है और कुछ रूपों को व्यक्त किया है जो एक उत्तर ले सकते हैं। मैं अभी भी अस्पष्ट हूं कि यह संभव है या नहीं होना चाहिए, लेकिन किसी भी तरह से यह मुझसे बात करने के लिए एक अच्छा अभ्यास रहा है। –

उत्तर

27

यह काफ़ी आसान की तुलना में मैं शुरू में उम्मीद होने के लिए पता चला है:

इसे ध्यान में रखते

(बहुत अन-स्केला कोड चेतावनी)।

पहले हम

trait FoldCurry[L <: HList, F, Out] { 
    def apply(l : L, f : F) : Out 
} 

// Base case for HLists of length one 
implicit def foldCurry1[H, Out] = new FoldCurry[H :: HNil, H => Out, Out] { 
    def apply(l : H :: HNil, f : H => Out) = f(l.head) 
} 

// Case for HLists of length n+1 
implicit def foldCurry2[H, T <: HList, FT, Out] 
    (implicit fct : FoldCurry[T, FT, Out]) = new FoldCurry[H :: T, H => FT, Out] { 
    def apply(l : H :: T, f : H => FT) = fct(l.tail, f(l.head)) 
} 

// Public interface ... implemented in terms of type class and instances above 
def foldCurry[L <: HList, F, Out](l : L, f : F) 
    (implicit fc : FoldCurry[L, F, Out]) : Out = fc(l, f) 

हम की तरह उपयोग कर सकते हैं

sealed trait HList 

final case class HCons[H, T <: HList](head : H, tail : T) extends HList { 
    def ::[H1](h : H1) = HCons(h, this) 
    override def toString = head+" :: "+tail.toString 
} 

trait HNil extends HList { 
    def ::[H1](h : H1) = HCons(h, this) 
    override def toString = "HNil" 
} 

case object HNil extends HNil 
type ::[H, T <: HList] = HCons[H, T] 

फिर हम एक प्रकार वर्ग की सहायता से उपपादन द्वारा हमारे गुना की तरह समारोह को परिभाषित कर सकते एक सरल HList परिभाषित करने की जरूरत, यह, आपके मूल उदाहरण के लिए,

val f1 = (i : Int, j : Int, k : Int, l : Int) => i+j+k+l 
val f1c = f1.curried 

val l1 = 1 :: 2 :: 3 :: 4 :: HNil 

// In the REPL ... note the inferred result type 
scala> foldCurry(l1, f1c) 
res0: Int = 10 

और हम टी का भी उपयोग कर सकते हैं वह एक ही असंशोधित अलग arity के और गैर वर्दी तर्क प्रकार के साथ कार्यों के लिए foldCurry,

val f2 = (i : Int, s : String, d : Double) => (i+1, s.length, d*2) 
val f2c = f2.curried 

val l2 = 23 :: "foo" :: 2.0 :: HNil 

// In the REPL ... again, note the inferred result type 
scala> foldCurry(l2, f2c) 
res1: (Int, Int, Double) = (24,3,4.0) 
3

ठीक है कोई स्केलज नहीं और कोई समाधान नहीं बल्कि एक स्पष्टीकरण। यदि आप अपने f.curried.apply का उपयोग 1 के साथ करते हैं और फिर आरईपीएल में 2 तर्कों के साथ रिटर्न-परिणाम प्रकारों का पालन करते हैं तो वास्तव में हर बार भिन्न होता है! FoldLeft काफी सरल है। यह आपके शुरुआती तर्क के साथ इस प्रकार के चरण में तय है जो f.curried है और चूंकि यह f.curried.apply (1) के समान हस्ताक्षर नहीं है, यह काम नहीं करता है। तो शुरुआती तर्क और परिणाम एक ही प्रकार का होना चाहिए। प्रकार foldLeft के प्रारंभिक योग के लिए संगत होना चाहिए। और आपका परिणाम भी इंट होगा ताकि पूरी तरह से काम न करे। उम्मीद है की यह मदद करेगा।

+0

मुझे लगता है कि मेरा प्रश्न कम हो सकता है "क्या स्कैला समर्थन संघ प्रकार करता है?" और यदि हां, तो क्या किसी भी वाक्य के पूर्ण रूप से करीबी प्रकार से सभी प्रकार के संघ के साथ सभी प्रकार के संघ के साथ सभी प्रकार के संघ के लिए सभी वाक्य के संघ के संदर्भ में कोई वाक्य रचनात्मक चीनी है? –

+0

@AdamP देखें http://www.chuusai.com/2011/06/09/scala-union-types-curry-howard/ –

+0

सीधे इस मामले में सबिन के नोटेशन का उपयोग बोझिल होगा। अगर "टी 1 => ... => टीएन" यूनियन में टाइप करें "(टी 1 => ... => टीएन) v ... v (टीएन -1 => टीएन)" चालू करने का कोई तरीका था कुछ विशेष ऑपरेटर, जो यहां बहुत उपयोगी होंगे। –

6

आपका फ़ंक्शन बिल्कुल 4 Int तर्कों की अपेक्षा करता है। foldLeft एक ऐसा कार्य है जो तत्वों की मनमानी संख्या पर लागू होता है। आप List(1,2,3,4) का उल्लेख करते हैं लेकिन यदि आपके पास List(1,2,3,4,5) या List() है तो क्या होगा?

List.foldLeft[B] भी उम्मीद है एक समारोह में एक ही प्रकार B वापस जाने के लिए है, लेकिन आपके मामले Int में और कुछ Function1[Int, _] एक ही प्रकार नहीं है।

जो भी समाधान आप साथ आते हैं वह सामान्य नहीं होगा। उदाहरण के लिए यदि आपका कार्य (Int, Float, Int, String) => Int प्रकार का है तो क्या होगा? इसके बाद आपको List[Any]

तो यह निश्चित रूप से List.foldLeft के लिए नौकरी की आवश्यकता होगी।

class Acc[T](f: Function1[T, _]) { 
    private[this] var ff: Any = f 
    def apply(t: T): this.type = { 
    ff = ff.asInstanceOf[Function1[T,_]](t) 
    this 
    } 
    def get = ff match { 
    case _: Function1[_,_] => sys.error("not enough arguments") 
    case res => res.asInstanceOf[T] 
    } 
} 

List(1,2,3,4).foldLeft(new Acc(f.curried))((acc, i) => acc(i)).get 
// res10: Int = 10 
+0

धन्यवाद, @huynjl। मैंने अपने प्रश्न को यह बताने के लिए स्पष्ट किया है कि मैं ऐसे उत्तर की तलाश में हूं जो 'जी' के लिए एक प्रकार जोड़ने के अलावा मेरी मूल अभिव्यक्ति को संशोधित नहीं करता है, लेकिन यह निश्चित रूप से काम पूरा हो जाता है। सामान्यता की कमी और 'List.foldLeft' की सीमाओं के संबंध में - क्या हम कुछ "गुना जैसी चीज़" परिभाषित कर सकते हैं (सबिन के वाक्यांश का उपयोग करने के लिए) जो टुपल्स पर भी काम करता है? –

+0

मैं अपने विशेष उदाहरण के 4-नेस के बारे में सोच रहा हूं। हालांकि यह सच है कि स्केल में अनगिनत लंबे प्रकार के हस्ताक्षर संभव नहीं हैं (जो मुझे पता है), और 'फ़ोल्ड लाइफ' का सामान्य उपयोग शायद ही कभी शून्य से सीमित होता है, यह वास्तव में इस उदाहरण की गुणवत्ता है जो इसे दिलचस्प बनाता है। मैं कई संकुचित लेकिन वैध परिस्थितियों की कल्पना कर सकता हूं जहां शून्य या ऑपरेटर इनपुट से पहले निष्पादन रोकता है, लेकिन यह एक ऐसा मामला है जो वास्तव में दिलचस्प और उपयोगी है (imho)। –

+0

@AdamPingel, तस्वीर में अनंत सूचियों को लाए बिना, यह एक स्पष्ट समाधान के बिना पहले से ही एक दिलचस्प समस्या थी। मुझे लगता है कि एचआईएलआईटी एवेन्यू कि माइल्स सुझाव एक दिलचस्प है। – huynhjl