ตัวเลือกแรกของฉันมักจะใช้การเรียกซ้ำ มีขนาดกะทัดรัดน้อยกว่าเล็กน้อยอาจเร็วกว่า (ไม่ช้ากว่าแน่นอน) และในการยุติก่อนกำหนดสามารถทำให้ตรรกะชัดเจนยิ่งขึ้น ในกรณีนี้คุณต้องมีค่า def ที่ซ้อนกันซึ่งค่อนข้างอึดอัด:
def sumEvenNumbers(nums: Iterable[Int]) = {
def sumEven(it: Iterator[Int], n: Int): Option[Int] = {
if (it.hasNext) {
val x = it.next
if ((x % 2) == 0) sumEven(it, n+x) else None
}
else Some(n)
}
sumEven(nums.iterator, 0)
}
ทางเลือกที่สองของฉันคือการใช้return
เพราะมันทำให้ทุกอย่างยังคงอยู่และคุณจะต้องห่อพับdef
เพื่อที่คุณจะได้มีบางอย่างกลับมา - ในกรณีนี้คุณมีวิธีการอยู่แล้วดังนั้น:
def sumEvenNumbers(nums: Iterable[Int]): Option[Int] = {
Some(nums.foldLeft(0){ (n,x) =>
if ((n % 2) != 0) return None
n+x
})
}
ซึ่งในกรณีนี้มีขนาดกะทัดรัดกว่าการเรียกซ้ำมาก (แม้ว่าเราจะโชคร้ายอย่างยิ่งกับการเรียกซ้ำเนื่องจากเราต้องทำการแปลงซ้ำ / วนซ้ำ) กระแสการควบคุมที่น่ากลัวเป็นสิ่งที่ควรหลีกเลี่ยงเมื่อทุกอย่างเท่ากัน แต่นี่ไม่ใช่ ไม่เป็นอันตรายในการใช้งานในกรณีที่มีค่า
ถ้าฉันทำสิ่งนี้บ่อยๆและต้องการให้อยู่ตรงกลางของวิธีการที่ไหนสักแห่ง (ดังนั้นฉันจึงไม่สามารถใช้การส่งคืนได้) ฉันอาจจะใช้การจัดการข้อยกเว้นเพื่อสร้างโฟลว์การควบคุมที่ไม่ใช่ภายใน นั่นคือสิ่งที่ทำได้ดีและการจัดการข้อผิดพลาดไม่ใช่ครั้งเดียวที่มีประโยชน์ เคล็ดลับเดียวคือหลีกเลี่ยงการสร้างสแต็กแทร็ก (ซึ่งช้ามาก) และนั่นเป็นเรื่องง่ายเพราะลักษณะNoStackTrace
และลักษณะลูกของมันControlThrowable
ทำเพื่อคุณแล้ว Scala ใช้สิ่งนี้ภายในอยู่แล้ว (ในความเป็นจริงนั่นคือวิธีที่ใช้ผลตอบแทนจากด้านในพับ!) มาสร้างของเราเอง (ไม่สามารถซ้อนกันได้แม้ว่าจะสามารถแก้ไขได้):
import scala.util.control.ControlThrowable
case class Returned[A](value: A) extends ControlThrowable {}
def shortcut[A](a: => A) = try { a } catch { case Returned(v) => v }
def sumEvenNumbers(nums: Iterable[Int]) = shortcut{
Option(nums.foldLeft(0){ (n,x) =>
if ((x % 2) != 0) throw Returned(None)
n+x
})
}
แน่นอนว่าการใช้ที่return
นี่ดีกว่า แต่โปรดทราบว่าคุณสามารถใส่shortcut
ได้ทุกที่ไม่ใช่แค่การห่อวิธีการทั้งหมด
ต่อไปในบรรทัดสำหรับฉันคือการใช้การพับซ้ำ (ไม่ว่าจะด้วยตัวเองหรือเพื่อหาไลบรารีที่ทำ) เพื่อให้สามารถส่งสัญญาณการยุติก่อนกำหนด วิธีธรรมชาติสองวิธีในการทำเช่นนี้คือการไม่เผยแพร่คุณค่า แต่Option
มีค่าซึ่งNone
หมายถึงการยุติ หรือใช้ฟังก์ชันตัวบ่งชี้ที่สองที่ส่งสัญญาณว่าเสร็จสิ้น การพับแบบขี้เกียจของ Scalaz ที่ Kim Stebel แสดงไว้ครอบคลุมกรณีแรกแล้วดังนั้นฉันจะแสดงที่สอง (พร้อมการใช้งานที่ไม่แน่นอน):
def foldOrFail[A,B](it: Iterable[A])(zero: B)(fail: A => Boolean)(f: (B,A) => B): Option[B] = {
val ii = it.iterator
var b = zero
while (ii.hasNext) {
val x = ii.next
if (fail(x)) return None
b = f(b,x)
}
Some(b)
}
def sumEvenNumbers(nums: Iterable[Int]) = foldOrFail(nums)(0)(_ % 2 != 0)(_ + _)
(ไม่ว่าคุณจะดำเนินการยกเลิกโดยการเรียกซ้ำการส่งคืนความเกียจคร้าน ฯลฯ ขึ้นอยู่กับคุณ)
ฉันคิดว่ามันครอบคลุมตัวแปรที่สมเหตุสมผลหลัก ๆ นอกจากนี้ยังมีตัวเลือกอื่น ๆ อีกด้วย แต่ฉันไม่แน่ใจว่าทำไมจึงใช้ตัวเลือกนี้ในกรณีนี้ ( Iterator
ตัวมันเองจะทำงานได้ดีถ้ามี a findOrPrevious
แต่มันไม่มีและงานพิเศษที่ต้องทำด้วยมือทำให้เป็นตัวเลือกที่โง่ที่จะใช้ที่นี่)