استخدام الخيار في كل مكان يشعر بالحرج بعض الشيء. أفعل شيئا خاطئا؟

StackOverflow https://stackoverflow.com/questions/2476835

  •  21-09-2019
  •  | 
  •  

سؤال

نتيجة للمقالات التي قرأتها عن Option الفصل الذي يساعدك على تجنب NullPointerException ، بدأت في استخدامه في كل مكان. تخيل شيئًا كهذا:

var file:Option[File] = None

وبعد ذلك عندما أستخدمه:

val actualFile = file.getOrElse(new File("nonexisting"))
if(actualFile.getName.equals("nonexisting")) { // instead of null checking

}
else { // value of file was good

}

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

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

المحلول

عمومًا ليست فكرة جيدة للعودة Option ثم استخدم getOrElse لإنتاج بعض قيمة الحارس التي تعني "لم يتم العثور عليها". وهذا ما Option تم تصميمه من أجل: للدلالة على عدم العثور على القيمة!

Option يظهر حقًا قوتها عند استخدامها بالاقتران مع بنيات البرمجة الوظيفية مثل map و foreach. هذا هو الأكثر قوة عند التعامل مع خيارات متعددة. على سبيل المثال ، افترض أنني أكتب طريقة تأخذ سلسلة وتعيدني إلى ملف ، ولكن فقط في حالة وجود الملف وهو ملف وليس دليلًا:

import java.io._;
def niceFile1(s: String): File = {
  val f = new File(s);
  if (f.exists && !f.isDirectory) f else null
}
def niceFile2(s: String): Option[File] = {
  val f = new File(s);
  if (f.exists && !f.isDirectory) Some(f) else None
}

حتى الآن ، باستخدام null أسهل-على الأقل حتى تنسى أن هذا قد يعطيك null وستحصل على NPE. على أي حال ، دعنا الآن نحاول استخدامه.

def niceFopen1(s: String) = {
  val f = niceFile1(s);
  if (f!=null) new FileInputStream(f) else null;
}
def niceFopen2(s: String) = niceFile2(s).map(f => new FileInputStream(f))

انظر ماذا حدث! في الحالة السابقة ، كان علينا إجراء اختبارات منطقية باليد وإنشاء متغيرات مؤقتة. قرف! في الحالة الثانية ، map فعلت كل العمل القذر بالنسبة لنا: لم يتم تعيين أي منها على لا شيء ، و Some(file) تم تعيينه ل Some(fileinputstream). سهل!

لكنه يتحسن بعد. ربما نريد العثور على حجم مجموعة كاملة من الملفات:

def totalSize2(ss: Seq[String]) = {
  (0L /: ss.flatMap(niceFile2)){(sum,f) => sum+f.length}
}

انتظر ، ما الذي يحدث هنا-ماذا عن كل None؟ ألا يتعين علينا الانتباه والتعامل معهم بطريقة أو بأخرى؟ حسنًا ، هذا هو المكان flatMap يأتي في: إنه ينضم إلى جميع الإجابات في قائمة واحدة. None هو إجابة للطول صفر ، لذلك يتجاهل ذلك. Some(f) لديه إجابة واحدة-f-لذلك يضعها في القائمة. ثم نستخدم طية لإضافة جميع الأطوال-الآن أن جميع العناصر في القائمة صالحة. لطيفة جدا!

نصائح أخرى

انها فكرة جيدة ل لا عزم قيمة Option, ، لكن من أجل يتقدم المنطق لكل ما يحتوي على:

findFile.foreach(process(_))

في الأساس هذه العمليات أ File إذا تم العثور على المرء ولم يفعل شيئًا خلاف ذلك (وهو ما يعادل توماس أولاً for الفهم بسبب for يجمع إلى مكالمة إلى foreach). إنها نسخة أكثر إيجازًا من:

findFile match {
  case Some(f) => process(f)
  case None =>
}

ما هو أكثر من ذلك ، الشيء العظيم في ذلك هو أنه يمكنك سلسلة العمليات ، شيء مثل:

(findLiveFile orElse fileBackupFile orElse findTempFile).foreach(process(_)

في معظم الحالات ، ستستخدم مطابقة الأنماط

file match {
   case Some(f) => { .. } //file is there
   case _ => { .. } //file is not there 
}

إذا كنت مهتمًا بالملف فقط إذا كان هناك ، فيمكنك استخدام التعبير

for(f <- file) { //file is there 
}

يمكنك بعد ذلك سلسلة من التعبيرات للعمل على مستويات متعددة على الحاويات

for{ 
  option <- List(Some(1), None, Some(2))
  f <- option
} yield f

res0: List[Int] = List(1, 2)

بدلاً من ذلك ، يمكنك استخدام ISDEDING والحصول على:

if(option.isDefined) {
   val x = option.get;
} else {
}

GET لم يتم إهماله في Scala 2.8.0.beta-1.

هذا بديل:

var file:Option[File] = None
// ...
file map (new File(_)) foreach { fh =>
  // ...
}

ومع ذلك ، إذا كنت بحاجة إلى القيام بشيء ما إذا كان الملف موجودًا ، وشيء آخر إذا لم يحدث ذلك ، أ match البيان أكثر ملاءمة:

var file:Option[File] = None
// ...
file map (new File(_)) match {
  case Some(fh) =>
    // ...
  case None =>
    // ...
}

هذا هو نفسه إلى حد كبير مثل if بيان ، لكني أحب أن تتسلسل الطبيعة بشكل أفضل لأشياء مثل Option, ، حيث أريد استخراج قيمة كذلك.

في الغالب يعيد ما يقوله الجميع ، لكنني أعتقد أن هناك 4 مواقف مختلفة على الأقل ستواجهها عندما يكون لديك var قابلة للإلغاء مثل هذا:

1. يجب ألا يكون الملف فارغًا

load(file.get())

سوف ترمي استثناء عندما تحاول استخدام الملف فعليًا. فشل بسرعة ، ياي!

2. الملف فارغ ، ولكن في هذه الحالة لدينا افتراضي معقول:

load(file.getOrElse(new File("/defaultFile")))

3. فرعان للمنطق بناءً على ما إذا كان الملف موجودًا:

  file match {
    case Some(f) => { .. } //file is there
    case _ => { .. } //file is not there 
  }

4. لا يوجد إذا كان الملف فارغًا ، ويعرف أيضًا باسم فشل بصمت. لا أعتقد أن هذا هو الأفضل بالنسبة لي في الحياة الواقعية:

for (f <- file) {
//do some stuff
}

أو ، ربما أكثر وضوحا؟

if (f.isDefined) {
  //do some stuff
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top