2013-06-12 11 views
13

क्या यह निर्धारित करना संभव है कि आलसी वैल शुरू हो जाए, इसे शुरू किए बिना?यह जांचने के लिए कि क्या आलसी वैल शुरू किए बिना शुरू किया गया है?

object TheApp { 
    lazy val optionalSubsystem = { 
     // ... 
     subsystem 
    } 

    def main(args: Array[String]) { 
     bootSubsystemA(this) 
     bootSubsystemB(this) 

     if (/* optionalSubsystem is initialized */) { 
      // more dependencies 
     } 
    } 
} 
+0

नहीं, लेकिन क्या निर्धारित करता है कि वैकल्पिक सबसिस्टम को लाया जा रहा है या नहीं? क्या यह रनटाइम जानकारी है? या संकलन समय की जानकारी? – stew

+0

सभी रनटाइम। मुझे एक रास्ता मिला: 'वैकल्पिक सबसिस्टम' का दुष्प्रभाव है-यह एक अभिनेता को बूट करता है। मैं साइड इफेक्ट की जांच करता हूं, जब तक इसका उत्तर बेहतर न हो जाए। –

+0

"मैं एक अक्का अभिनेता को बूट करता हूं। अगर अभिनेता बूट हो जाता है, तो मैं इसकी निगरानी करना चाहता हूं"। इस मामले का उपयोग प्रश्न में कहा जाना चाहिए, अगर आप वास्तव में जानना चाहते हैं। –

उत्तर

12

यह वास्तव में आपके प्रश्न का उत्तर नहीं है, और जब लोग ऐसा करते हैं तो मुझे नफरत है, लेकिन मैं वैसे भी ऐसा करने जा रहा हूं। मुझे लगता है कि सबसे अच्छी प्रतिक्रिया यह है कि: आलसी वैल इसके लिए उपयुक्त नहीं है, इसलिए एक प्रकार को परिभाषित करें जो आपको आवश्यकतानुसार समर्थन देता है।

आप optionalSubsystem() बजाय optionalSubsystem के रूप में चर का उल्लेख करना होगा, लेकिन यह, एक अच्छी बात है, क्योंकि डिजाइन आप चाहते हैं के साथ कि संदर्भ प्राप्त करने के एक observably पक्ष प्रभावशाली प्रक्रिया है।

class Lazy[A](f: => A, private var option: Option[A] = None) { 

    def apply(): A = option match { 
    case Some(a) => a 
    case None => val a = f; option = Some(a); a 
    } 

    def toOption: Option[A] = option 

} 

scala> val optionalSubsystem = new Lazy { "a" } 
optionalSubsystem: Lazy[java.lang.String] = [email protected] 

scala> optionalSubsystem.toOption.isDefined 
res1: Boolean = false 

scala> optionalSubsystem() 
res2: java.lang.String = a 

scala> optionalSubsystem.toOption.isDefined 
res12: Boolean = true 

संपादित - यहाँ टॉमस Mikula को कुछ संशोधनों के धन्यवाद के साथ एक और संशोधन है:

import scala.language.implicitConversions 

object Lazy { 

    def lazily[A](f: => A): Lazy[A] = new Lazy(f) 

    implicit def evalLazy[A](l: Lazy[A]): A = l() 

} 

class Lazy[A] private(f: => A) { 

    private var option: Option[A] = None 

    def apply(): A = option match { 
    case Some(a) => a 
    case None => val a = f; option = Some(a); a 
    } 

    def isEvaluated: Boolean = option.isDefined 

} 

इस की मदद से आप lazily { ... } बजाय new Lazy { ... }, और optionalSubsystem बजाय optionalSubsystem() लिखें।

scala> import Lazy._ 
import Lazy._ 

scala> val optionalSubsystem = lazily { "a" } 
optionalSubsystem: Lazy[String] = [email protected] 

scala> optionalSubsystem.isEvaluated 
res0: Boolean = false 

scala> optionalSubsystem: String 
res1: String = a 

scala> optionalSubsystem.isEvaluated 
res2: Boolean = true 
+0

बेहतर डिजाइन * अच्छा * है। मुझे सही दिशा बताने के लिए धन्यवाद। –

+0

यह थ्रेड-सुरक्षित नहीं है। कोई भी कह सकता है, "इसे बनाना आसान है", या, "कई धागे/कलाकारों को समन्वयित करने के बेहतर तरीके हैं।" –

+0

क्या किसी ने कहा था कि यह थ्रेड-सुरक्षित होना चाहिए था? –

3

आप कुछ इस तरह कर सकते हैं:

object TheApp { 

    private var _optionalSubsystemInitialized = false 

    def optionalSubsystemInitialized = _optionalSubsystemInitialized 

    lazy val optionalSubsystem = { 
     _optionalSubsystemInitialized = true 
     subsystem 
    } 

} 

क्या यह वास्तव में उचित एक lazy val के प्रवर्तन कोड में इस तरह के साइड इफेक्ट के लिए है एक और सवाल है।

+0

मैं कहूंगा कि यह सख्ती से, अनुचित है। मैं http://stackoverflow.com/questions/12959777/why-would-i-want-to-re-implement-lazy द्वारा उद्धृत बिट के साथ सहमत हूं: "आलस्य का उपयोग करने से बचें जब semantics द्वारा आलस्य की आवश्यकता है। इन मामलों में यह है स्पष्ट होना बेहतर है क्योंकि इससे लागत मॉडल स्पष्ट हो जाता है, और दुष्प्रभावों को अधिक सटीक रूप से नियंत्रित किया जा सकता है। " –

+0

@ChrisMartin - यदि आप उस उद्धरण से सहमत हैं, तो इसका मतलब यह है कि आप इसका अर्थ समझ सकते हैं? आपके द्वारा लिंक किया गया प्रश्न मेरा प्रश्न है, और किसी ने कभी भी संतोषजनक उत्तर पोस्ट नहीं किया है। – DaoWen

+0

@ दाओवेन - मैं आपके प्रश्न के बारे में सोच रहा हूं, और मैं किसी भी ठोस जवाब के साथ नहीं आ सकता। –

1

लेकिन निश्चित रूप से आप कर सकते हैं। एक क्षेत्र सिर्फ एक क्षेत्र है।

package lazyside 

object Lazy 

class Foo { 
    lazy val foo = 7 
    lazy val bar = { Lazy ; 8 } 
} 

object Test extends App { 
    import scala.reflect.runtime.{ currentMirror => cm } 
    import scala.reflect.runtime.universe._ 

    val x = new Foo 

    // method 1: reflect the underlying field 
    val im = cm reflect x 
    val f = (typeOf[Foo] declaration TermName("foo")).asTerm.accessed.asTerm 
    def foo_? = x synchronized ((im reflectField f).get != 0) 

    def yn(b: Boolean) = if (b) "yes" else "no" 
    Console println s"Is foo set yet? ${yn(foo_?)}" 

    // method 2: check a benign side effect like a class load 
    val m = classOf[ClassLoader].getDeclaredMethod("findLoadedClass", classOf[String]) 
    m setAccessible true 
    def bar_? = (m invoke (x.getClass.getClassLoader, "lazyside.Lazy$")) != null 
    Console println s"Is bar set yet? ${yn(bar_?)}" 

    Console println s"I see that foo is ${x.foo}." 
    Console println s"Is foo set yet? ${yn(foo_?)}" 
    Console println s"I see that bar is ${x.bar}." 
    Console println s"Is bar set yet? ${yn(bar_?)}" 
    Console println s"I see that x is loaded by a ${x.getClass.getClassLoader.getClass}" 
} 

चेतावनी foo_? की कि धागे की सुरक्षा आलसी गणना उदाहरण x की निगरानी के प्राप्त करने पर निर्भर करता है। इसे बदलने की बात है।

इसके अलावा, स्पष्ट रूप से, फ़ील्ड मान का परीक्षण केवल तभी काम करता है जब init मान डिफ़ॉल्ट मान (null.asInstanceOf[T]) नहीं है।

दूसरी विधि आलसी init द्वारा लोड होने वाली कक्षा Lazy$ पर निर्भर करती है। फू के अंदर वस्तु को गिलहरी करना थोड़ा सा सुरक्षित होगा। किसी भी मामले में, वह विशेष दुष्प्रभाव एक शॉट है। यह उपप्रणाली स्टार्ट-अप के उपयोग के मामले को पूरा कर सकता है।

unsurprising उत्पादन के साथ:

Is foo set yet? no 
Is bar set yet? no 
I see that foo is 7. 
Is foo set yet? yes 
I see that bar is 8. 
Is bar set yet? yes 
I see that x is loaded by a class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader 

2,11 में संकलित। 2.10 के लिए, TermName के बजाय newTermName का उपयोग करें।

+1

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

+0

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

0

नहीं सीधे, लेकिन क्यों तुम सिर्फ अपने तर्क के आसपास इस तरह बदलाव नहीं है:

object TheApp { 
    lazy val optionalSubsystem = { 
     // ... 
     subsystem 
     // more dependencies 
    } 

    def main(args: Array[String]) { 
     bootSubsystemA(this) 
     bootSubsystemB(this) 
    } 
} 

इस तरह "अधिक निर्भरता" इष्टतम समय लोड हो (सहित कभी नहीं अगर वे आवश्यक नहीं हैं)

+0

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

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