2013-03-29 30 views
9

मुझे अक्सर एक पैटर्न का सामना करना पड़ता है, इसलिए मैं सोच रहा था कि इसके लिए स्कैला लाइब्रेरी में कोई सुविधाजनक तरीका है या नहीं।जब तक यह लौटा नहीं जाता है तब तक फ़ंक्शन को आवर्ती कॉल करें

इसे f: A => Option[B] फ़ंक्शन दें। मैं f से , f(f(f(x).get).get...) से शुरू होने के साथ एक पुनरावर्ती कॉल करना चाहता हूं, fNone लौटाता है और अंतिम गैर-None मान देता है।

@tailrec 
def recurrentCallUntilNone[B](f: B => Option[B], x: B): B = f(x) match { 
    case Some(y) => recurrentCallUntilNone(f, y) 
    case None => x 
} 

यह पहले से ही मानक पुस्तकालय में लागू किया गया है:

मैं इस के लिए एक कार्यान्वयन लिखा था?

इसके लिए एक उपयोग उदाहरण एक सूची (जिपर) के लिए हो सकता है जो वर्तमान स्थिति को बनाए रखता है। next पर कॉल करके, None वापस लौटाया जाता है यदि वर्तमान स्थिति के बाद कोई तत्व नहीं है या उसी सूची के लिए Option है, लेकिन वर्तमान स्थिति में वृद्धि हुई है। उपर्युक्त विधि का उपयोग करके, end विधि का निर्माण किया जा सकता है जो सूची को अंत तक खोजता है।

+0

यह पुस्तकालय में नहीं है, और आप इसे सही तरीके से कर रहे हैं। –

+2

'@ tailrec' जोड़ें! –

+1

यह लगभग एक ''खुला'] है (http://daily-scala.blogspot.co.at/2009/09/unfoldleft-and-right.html)।लेकिन यह किसी भी libs में प्रतीत नहीं होता है। – phg

उत्तर

2

क्या आप trampoline की एक बहुत ही विशेष प्रकार की तरह दिखता है कर रहे हैं। अधिक सामान्य मामला Option की बजाय कक्षाओं में लिपटे कार्यों का उपयोग करता है और विभिन्न तर्क और वापसी प्रकारों का समर्थन करता है।

कैलिन-आंद्रेई बताते हैं कि TailCalls object का उपयोग कर मानक लाइब्रेरी में ट्रैम्पोलिन उपलब्ध हैं।

पहले लिंक से

:

def even2(n: Int): Bounce[Boolean] = { 
    if (n == 0) Done(true) 
    else Call(() => odd2(n - 1)) 
} 
def odd2(n: Int): Bounce[Boolean] = { 
    if (n == 0) Done(false) 
    else Call(() => even2(n - 1)) 
} 
trampoline(even2(9999)) 

sealed trait Bounce[A] 
case class Done[A](result: A) extends Bounce[A] 
case class Call[A](thunk:() => Bounce[A]) extends Bounce[A] 

def trampoline[A](bounce: Bounce[A]): A = bounce match { 
    case Call(thunk) => trampoline(thunk()) 
    case Done(x) => x 
} 

अब मानक पुस्तकालय

import scala.util.control.TailCalls._ 

def even2(n: Int): TailRec[Boolean] = { 
    if (n == 0) done(true) 
    else tailcall(odd2(n - 1)) 
} 
def odd2(n: Int): TailRec[Boolean] = { 
    if (n == 0) done(false) 
    else tailcall(even2(n - 1)) 
} 
even2(9999).result 
+0

मुझे ट्रैम्पोलिन के बारे में पता नहीं था, इसलिए इसके लिए धन्यवाद। मैंने पाया कि स्कैला लाइब्रेरी का वर्तमान संस्करण उन्हें ''टेलॉक' के माध्यम से समर्थन करता है (http://www.scala-lang.org/api/current/index.html#scala.util.control.TailCalls$)। आपका जवाब जो मैं चाहता हूं उसके सबसे नज़दीक है। क्या आप इसे संशोधित कर सकते हैं कि यह स्कैला लाइब्रेरी से ट्रैम्पोलिन का उपयोग करता है? तो मैं आपका जवाब स्वीकार कर सकता हूं। –

+1

वाह मुझे नहीं पता था कि वे मानक पुस्तकालय में जोड़े गए थे। StackOverflow बहुत अच्छा है मैं एक प्रश्न का उत्तर देता हूं और एक ही समय में कुछ नया सीखता हूं :-) – iain

1

एक सौंदर्य प्रतियोगिता के मामले में, आप एक ऐसा फ़ंक्शन बना सकते हैं जो मौजूदा व्यक्ति को उस राक्षस में बदल देता है जिसे आप करी के उपयोग के माध्यम से बात कर रहे थे।

def composeUntilTheEnd[B](f: Option[B] => Option[B])(x: Option[B]): Option[B] = 
     if (f(x) != None) composeUntilTheEnd(f)(f(x)) 
     else x 

def ff = composeUntilTheEnd((x:Option[Int]) => x)_ 

अब ff पर कॉल करना आपको इच्छित व्यवहार मिलता है।

+2

मुझे नहीं लगता कि यह कोई और सुंदर है कि पूछताछकर्ता द्वारा दिए गए कार्यान्वयन। इसके अलावा, यदि फ़ंक्शन साइड इफेक्ट्स कर रहा है, तो यह समाधान दो (x) दो बार कर रहा है जो अस्वीकार्य हो सकता है। – myyk

1

यदि आप चाहते हैं कि आप अपने विकल्पों से भाप बना सकते हैं और फिर अंतिम परिभाषित तत्व प्राप्त करने के लिए स्ट्रीम फ़ंक्शंस का उपयोग कर सकते हैं। (या अपरिभाषित तत्व से पहले अंतिम परिभाषित तत्व के बजाय)

def options[B](f: B => Option[B], initialValue: Option[B]): Stream[Option[B]] = { 
    initialValue #:: options(f, initialValue.map(f(_)).flatten) 
} 

options.takeWhile(_.isDefined).last 
2

साथ कैसे के बारे में:

Stream.iterate(Some(x)) { x => x.flatMap(f _) }.takeWhile { _.isDefined }.last 

अद्यतन

या यहां तक ​​कि neater IMHO (केवल एकल ट्रेवर्सल):

val result = { 
    val xs = Stream.iterate(Some(x)) { x => x.flatMap(f _) } 
    (xs zip xs.tail) collectFirst { 
    case (Some(x), None) => x 
    } get 
} 

ध्यान दें कि यह collectFirst कॉल करने के लिए, क्योंकि हम एक None (अन्यथा अनंत लूप संभव होगा) के साथ शुरू नहीं कर सकते सुरक्षित है।

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

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