سؤال

لقد كنت أتعلم سكالا وأقول إنها لغة رائعة حقًا. أحب بشكل خاص إمكانات مطابقة الأنماط والأفراد الحرفيين ، لكنني أتيت من جافا سكريبت ، خلفية روبي وواحدة من أنماطاتي المفضلة في هذه اللغات هي وظيفة تعريف الأسلوب الكسول ونمط تعريف الطريقة. مثال في JavaScript

var foo = function() {
  var t = new Date();
  foo = function() {
    return t;
  };
  return foo();
};

يعمل نفس الرمز مع Minor Tweaks في Ruby حيث يمكنك فقط استخدام كائن Singleton لإعادة تعريف الطريقة بعد إجراء الحساب. يأتي هذا النوع من الأشياء في متناول الجميع عندما يشارك حساب مكلف ولا تعرف في وقت مبكر إذا كنت ستحتاج إلى النتيجة. أعلم أنه في Scala يمكنني استخدام ذاكرة التخزين المؤقت لمحاكاة نفس النتيجة ، لكنني أحاول تجنب عمليات الفحص المشروطة وحتى الآن أعادت تجاربي نتائج سلبية. هل يعرف أي شخص ما إذا كان هناك وظيفة تعريف كسول أو نمط تعريف الطريقة في سكالا؟

ملاحظة: رمز JavaScript هو من Peter Michaux's موقع.

هل كانت مفيدة؟

المحلول

يبدو أن كل هذا الرمز المعقد في JavaScript يحاول فقط تخزين قيمة التاريخ. في Scala ، يمكنك تحقيق نفس الشيء بشكل تافه:

lazy val foo = new Date

وإذا كنت لا ترغب حتى في صنع VAL ، ولكن تريد الاتصال بوظيفة من شأنها أن تنفذ الرمز باهظ الثمن فقط إذا احتجت إليه ، يمكنك ذلك

def maybeExpensive(doIt: Boolean, expensive: => String) {
  if (doIt) println(expensive)
}
maybeExpensive(false, (0 to 1000000).toString)  // (0 to 1000000).toString is never called!
maybeExpensive(true, (0 to 10).toString)        // It is called and used this time

حيث النمط expensive: => String يطلق عليه معلمة اسمها ، والتي يمكنك التفكير فيها ، "أعطني شيئًا من شأنه أن يولد سلسلة عند الطلب." لاحظ أنه إذا كنت تستخدمه مرتين ، فسيقوم بتجديده في كل مرة ، وهو المكان الذي يأتي فيه نمط Randall Schultz في:

def maybeExpensiveTwice(doIt: Boolean, expensive: => String) {
  lazy val e = expensive
  if (doIt) {
    println(e)
    println("Wow, that was " + e.length + " characters long!")
  }
}

أنت الآن تنشئ فقط إذا كنت في حاجة إليها (عبر المعلمة ذات الاسم) و قم بتخزينه وإعادة استخدامه إذا كنت بحاجة إليه مرة أخرى (عبر Val Lazy).

لذا افعل ذلك بهذه الطريقة ، وليس طريقة JavaScript ، على الرغم من أنك استطاع اجعل Scala تبدو مثل JavaScript.

نصائح أخرى

سكالا lazy valS ، التي لا يتم تقييم المهيئات التي لا يتم تقييمها إلا حتى يتم استخدام VAL. يمكن استخدام Vals البطيئة كطريقة المتغيرات المحلية.

تحتوي Scala أيضًا على معلمات طريقة الاسم ، والتي يتم لفها في تعبيرات المعلمة الفعلية في thunk ويتم تقييم thunk في كل مرة يتم الإشارة إلى المعلمة الرسمية في الجسم.

يمكن استخدامها معًا لتحقيق دلالات التقييم البطيئة مثل الافتراضي في Haskell (على الأقل في فهمي المحدود للغاية لـ Haskell).

def meth(i: => Int): Something = {
  //        ^^^^^^ by-name parameter syntax
  lazy val ii = i
  // Rest of method uses ii, not i
}

في هذه الطريقة ، سيتم تقييم التعبير المستخدم كمعلمة فعلية إما مرات صفر (إذا لم يستخدم مسار التنفيذ الديناميكي للطريقة أبدًا ii) أو مرة واحدة (إذا كان يستخدم ii مرة أو أكثر).

يمكنك تحديد فال كسول وهو وظيفة:

lazy val foo = {
  val d = new Date
  () => { d }
}

println(foo())

foo() سيعود الآن كائن التاريخ نفسه في كل مرة ، كائن سيتم تهيئته في المرة الأولى التي يتم فيها استدعاء FOO.

لشرح الكود قليلاً ، يتم استدعاء المرة الأولى FOO () { val d = new Date; () => { d } } يتم تنفيذها ، يتم تعيين D لقيمة تاريخ جديدة ثم يقوم بتقييم التعبير الأخير () => { d } وتعيينها إلى قيمة FOO. ثم FOO هي وظيفة بدون معلمات تعود د.

أعتقد أن بعض المستجيبين كانوا في حيرة من أمرهم بالطريقة التي صاغتها السؤال. بناء Scala الذي تريده هنا هو تعريف كسول بسيط:

lazy val foo = new java.util.Date

سيحدث بناء كائن التاريخ مرة واحدة على الأكثر ويتم تأجيله حتى المرجع الأول إلى FOO.

لم أكن أعرف شيئًا عن روبي ، لكن Scala لديها نمط كائن Singleton أيضًا:

Welcome to Scala version 2.8.0.r22634-b20100728020027 (Java HotSpot(TM) Client VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala> object LazyInit {                                       
     |     val msec = { println("Hi,I'm here!");   System.currentTimeMillis }
     | }
defined module LazyInit

scala> System.currentTimeMillis                                              
res0: Long = 1282728315918

scala> println(System.currentTimeMillis +" : " + LazyInit.msec)              
Hi,I'm here!
1282728319929 : 1282728319930

scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728322936 : 1282728319930

scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728324490 : 1282728319930

scala> 

إذا كنت ترغب في الحصول على وظيفة ، يمكنك جعلها نوعًا فرعيًا من نوع الوظيفة:

scala> object LazyFun extends (() => Long) {            
     |     val msec = System.currentTimeMillis          
     |     def apply() = msec                           
     | }
defined module LazyFun

scala> System.currentTimeMillis                         
res2: Long = 1282729169918

scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729190384 : 1282729190384

scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729192972 : 1282729190384

scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729195346 : 1282729190384

أعتقد أن ما تعنيه "الوظيفة الكسول" هي وظيفة وظيفة حرفية أو مجهولة.

في Scala ، يمكنك أن تفعل أشياء مثل هذا ، تشبه إلى حد كبير رمز JavaScript الذي نشرته.

val foo = () => {
    val t = new Date()
    val foo = () => {t}

    foo()
}

println ("Hello World:" + foo())

الفرق الرئيسي هو:

  • لا يمكنك إعادة تقييم FOO الخارجي
  • لا توجد كلمة رئيسية "دالة" ، بدلاً من ذلك يمكنك استخدام شيء مثل (s: string) => {code}
  • البيان الأخير هو قيمة الإرجاع للكتلة ، لذلك لا تحتاج إلى إضافة "عودة".
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top