2012-03-16 17 views
8

कहें कि मेरे पास एक विशेषता है जिसमें दो सूचियां हैं। कभी-कभी मुझे उसमें दिलचस्पी है, कभी-कभी टीथ में।कॉल स्टैक के नीचे राज्य-चयन संदर्भ को पार करने से बचने के लिए "कार्यात्मक तरीका" क्या है?

trait ListHolder { 
    val listOne = List("foo", "bar") 
    val listTwo = List("bat", "baz") 
} 

मैं जिनमें से मैं संदर्भ मैं सूचियों के बीच चयन करने की आवश्यकता है शीर्ष पर फ़ंक्शन कॉल की एक श्रृंखला है, है, लेकिन निचले भाग में, जिनमें से मैं विशेषता का उपयोग करें।

अनिवार्य प्रतिमान में, मैं कार्यों के माध्यम से संदर्भ नीचे से गुजरती हैं:

class Imperative extends Object with ListHolder { 
    def activeList(choice : Int) : List[String] = { 
    choice match { 
     case 1 => listOne 
     case 2 => listTwo 
    } 
    } 
} 

def iTop(is : List[Imperative], choice : Int) = { 
    is.map{iMiddle(_, choice)} 
} 

def iMiddle(i : Imperative, choice : Int) = { 
    iBottom(i, choice) 
} 

def iBottom(i : Imperative, choice : Int) = { 
    i.activeList(choice) 
} 

val ps = List(new Imperative, new Imperative) 
println(iTop(ps, 1)) //Prints "foo, bar" "foo,bar" 
println(iTop(ps, 2)) //Prints "bat, baz" "bat, baz" 

वस्तु उन्मुख प्रतिमान में, मैं संदर्भ नीचे गुजर से बचने के लिए आंतरिक स्थिति का उपयोग कर सकते हैं:

class ObjectOriented extends Imperative { 
    var variable = listOne 
} 

def oTop(ps : List[ObjectOriented], choice : Int) = { 
    ps.map{ p => p.variable = p.activeList(choice) } 
    oMiddle(ps) 
} 

def oMiddle(ps : List[ObjectOriented]) = oBottom(ps) 

def oBottom(ps : List[ObjectOriented]) = { 
    ps.map(_.variable) //No explicitly-passed-down choice, but hidden state 
} 

val oops = List(new ObjectOriented, new ObjectOriented) 

println(oTop(oops, 1)) 
println(oTop(oops, 2)) 

एक कार्यात्मक भाषा में एक समान परिणाम प्राप्त करने के लिए बेवकूफ तरीका क्या है?

यही है, मैं उपरोक्त से आउटपुट के समान होने के लिए निम्न के आउटपुट को पसंद करूंगा।

class Functional extends Object with ListHolder{ 
    //IDIOMATIC FUNCTIONAL CODE 
} 

def fTop(fs : List[Functional], choice : Int) = { 
    //CODE NEEDED HERE TO CHOOSE LIST 
    fMiddle(fs) 
} 

def fMiddle(fs : List[Functional]) = { 
    //NO CHANGES ALLOWED 
    fBottom(fs) 
} 

def fBottom(fs : List[Functional]) = { 
    fs.map(_.activeList) //or similarly simple 
} 

def fs = List(new Functional, new Functional) 

println(fTop(fs, 1)) 
println(fTop(fs, 2)) 

अद्यतन: इस ठीक से कार्यात्मक माना जाएगा?

class Functional extends Imperative with ListHolder{} 

class FunctionalWithList(val activeList : List[String]) extends Functional{} 

def fTop(fs : List[Functional], band : Int) = { 
    fMiddle(fs.map(f => new FunctionalWithList(f.activeList(band)))) 
} 

def fMiddle(fs : List[FunctionalWithList]) = { 
    //NO CHANGES ALLOWED 
    fBottom(fs) 
} 

def fBottom(fs : List[FunctionalWithList]) = { 
    fs.map(_.activeList) 
} 

def fs = List(new Functional, new Functional) 

println(fTop(fs, 1)) 
println(fTop(fs, 2)) 
+0

छोटा नोट: आपको 'लिस्टहोल्डर के साथ ऑब्जेक्ट बढ़ाता है' लिखना नहीं है। बस 'लिस्टहोल्डर' बढ़ाएं (कक्षाएं गुणों का विस्तार कर सकती हैं)। – Jesper

उत्तर

5

ठीक है, कोई भी हमेशा इस तरह की चीज़ों को संभालने के लिए मोनैड और मोनैडिक समझ का उपयोग कर सकता है, लेकिन इस मामले का दिल यह है कि ढेर के नीचे विकल्पों को पारित करने के बजाय, आप स्टैक को फ़ंक्शन वापस कर देते हैं, जब तक कि कोई ऐसा न हो जानता है कि समस्या को हल करने के लिए इसे कैसे संभाला जा सकता है।

def fTop(fs : List[Functional]) = { 
    fMiddle(fs) 
} 

def fMiddle(fs : List[Functional]) = { 
    fBottom(fs) 
} 

def fBottom(fs : List[Functional]) = { 
(choice: Int) => fs map (_ activeList choice) 
} 

और फिर

println(fTop(fs)(1)) 

एक बार जब आप बात की इस तरह के पैटर्न को विकसित करने शुरू करते हैं, आप सभी प्रकार की monads (इकाई के प्रत्येक प्रकार के एक खास पैटर्न का प्रतिनिधित्व करता है) के साथ खत्म।

+0

मैं वास्तव में यहां एक मोनैड की अनुशंसा नहीं करता हूं लेकिन वह कोड इतना सुरुचिपूर्ण है कि मुझे इसे +1 करना होगा ... – Owen

+0

"इस मामले का दिल यह है कि ढेर के नीचे विकल्पों को पारित करने के बजाय, आप स्टैक को फ़ंक्शन वापस कर देते हैं," मुझे लगता है कि यह मूल अवधारणा है कि मेरा पुराना ओओपी दिमाग बार-बार याद करता है। –

+0

@LarryOBrien मुझे इसे सही करने के लिए दो बार कोड लिखना पड़ा। यह विदेशी भाषाओं की तरह है: इसे बोलने के बजाय विदेशी भाषा से/अनुवाद करना अधिक कठिन होता है। –

1

आपका पहला अनिवार्य संस्करण मेरे लिए सबसे अधिक कार्यात्मक दिखता है।

आम तौर पर रीडर मोनड और राज्य मोनैड ट्रांसफार्मर का उपयोग संदर्भ पास करने या कॉल स्टैक को खाली करने के लिए किया जाता है।

राज्य मोनैड के उदाहरण के लिए Scalaz state monad examples देखें और इसी तरह के प्रश्न और उत्तर के लिए इस स्कालज़ मेलिंग list thread देखें।

2

मुझे लगता है कि "अद्यतन" के तहत आपका उत्तर पूरी तरह से अच्छा है। हां, आप यहां रीडर मोनड का उपयोग कर सकते हैं। लेकिन परेशान क्यों है, जब आपके पास एक अच्छा समाधान है जो monads का उपयोग नहीं करता है?

डैनियल monadic समाधान, अगर तरीकों fTop और fMiddle शुरुआत और अधिक जटिल पाने के लिए सुंदर और सुरुचिपूर्ण है, लेकिन आप पाएंगे, कि अतिरिक्त वाक्य रचना का एक बहुत याद आ रही पैरामीटर "पर चलने" की जरूरत है।

मैं एक class का उपयोग कर लगता है कि संदर्भ स्टोर करने के लिए उचित है क्योंकि:

  • है यही कारण है कि कक्षाओं के लिए कर रहे हैं: कार्यों के बीच एक संदर्भ साझा करने के लिए।

  • स्कैला के पास मोनाड्स की तुलना में कक्षाओं के लिए बहुत अच्छा वाक्यविन्यास है।