יציאה מלולאה בסקאלה
התוכנית הבאה בפייתון קוראת שורות מהמשתמש, מדפיסה כל שורה חזרה ועוצרת כשמישהו כתב את המילה stop:
while True:
line = input()
print(line)
if line == "stop":
break
בואו ננסה לתרגם אותה לסקאלה.
1. גירסה 1 - מילה במילה
ניסיון לתרגום מילולי לסקאלה לא נשמע מסובך, רק צריך למצוא את המקבילה בסקאלה לכל פקודה בפייתון. לולאת while נשארת לולאת while, פקודת input הופכת ל readLine, תנאי נשאר תנאי - אבל מה לגבי ה break?
def v1(): Unit =
while (true) {
val line = readLine()
println(line)
if (line == "stop") {
// ???
}
}
דווקא הפקודה הכי חשובה בתוכנית לא קיימת בצורה מובנית בסקאלה.
2. גירסה 2 - ה break של סקאלה
הגישה של סקאלה ל break היא קצת מוזרה. במקום להוסיף מילה לשפה הם משתמשים במנגנון המובנה של Exception - מי שרוצה לעצור באמצע לולאה זורק Exception, וקוד שעוטף את הלולאה תופס את ה Exception וממשיך בתוכנית. יש קיצורי דרך כמובן אז הקלאס boundary אחראי על עטיפת הלולאה בקוד שתופס Exceptions והפונקציה break היא בסך הכל throw. הקוד נראה ככה:
def v2(): Unit =
boundary {
while (true) {
val line = readLine()
println(line)
if (line == "stop") {
break()
}
}
}
המימוש של break (מובנה בשפה, לא צריך לכתוב לבד) הוא:
def break[T](value: T)(using label: Label[T]): Nothing =
throw Break(label, value)
והמימוש של boundary (שוב לא צריך לכתוב לבד, מגיע עם השפה) הוא:
inline def apply[T](inline body: Label[T] ?=> T): T =
val local = Label[T]()
try body(using local)
catch case ex: Break[T] @unchecked =>
if ex.label eq local then ex.value
else throw ex
3. גירסה 3 - הגישה הפונקציונאלית
גירסה קצת פחות מוזרה של אותו קוד תשתמש ב takeWhile בתור תחליף ל while של פייתון. היתרון ב takeWhile הוא שלא צריך break, פשוט מסיימים את האיטרציה עם ערך "שקר" כדי לעצור. זה הקוד:
def v3(): Unit =
LazyList.from(0).takeWhile(_ => {
val line = readLine()
println(line)
line != "stop"
}).toList