Reputation: 1403
I would like to continuously prompt the user to enter a date in a given format, until he/she gets it right.
This is what I've done:
def readDate(prompt: String): Date = {
var date: Option[Date] = None
Iterator.continually {
val startDateString = readLine(prompt)
val startDate = catching(classOf[ParseException]).opt(asDate(startDateString))
date = startDate
startDate
}.takeWhile(_ == None).foreach {
date =>
println("Incorrect format. Try again.")
}
date.get
}
where asDate
just uses SimpleDateFormat.parse
on the entered String.
Now, this seems to work, but I'm pretty sure it's not the right way.
I don't really understand how to handle these chained iterators (since both Iterator.continually and takeWhile return an instance of AbstractIterator).
I have basically two questions:
1) Is there a way to "return" startDate
from Iterator.continually
? I've tried and failed map
-ping it. I want this in order to get rid of the var date
and date = startDate
.
2) If I didn't want anything to happen between the reads, what would I do with the last foreach? I've seen that nothing works if I just remove it (I think because of next() not being invoked), but is it OK to leave it there like this:
takeWhile(_ == None).foreach { date => {}}
?
Is there a better way than the "empty" foreach?
Thanks!
Upvotes: 2
Views: 151
Reputation: 51109
I would write it as
def readDate(prompt: String): Date =
try asDate(readLine(prompt))
catch { case e: ParseException =>
println("Incorrect format. Try again.")
readDate(prompt)
}
Upvotes: 2
Reputation: 167891
You should use find
instead of takeWhile
--this will keep dropping entries until a good one comes through. Then you have an Option
, so you just need
def readDate(prompt: String): Date = {
Iterator.continually {
catching(classOf[ParseException]).opt(asDate( readLine(prompt) ))
}.find(_.isDefined).get
}
if you don't want to print anything. If you do want to print something, you can put it into the loop.
def readDate(prompt: String): Date = {
Iterator.continually {
catching(classOf[ParseException]).opt(asDate( readLine(prompt) )) match {
case None =>
println("Incorrect format. Try again.")
None
case x => x
}
}.find(_.isDefined).get
}
I'm not sure that this is dramatically clearer, but it's definitely shorter and does those things you wanted.
I'd probably use a tail-recursive function instead:
import annotation.tailrec
@tailrec def readDate(prompt: String, again: Boolean = false): Date = {
if (again) println("Incorrect format. Try again.")
catching(classOf[ParseException]).opt(asDate( readLine(prompt) )) match {
case Some(date) => date
case None => readDate(prompt, true)
}
}
The logic seems a bit clearer to me here.
Upvotes: 3