clistdinscala

clistdinscala,scala,command-line-interface,stdin,slick,Scala,Command Line Interface,Stdin,Slick,我在Scala中创建了一个控制台接口应用程序作为todo列表。我的数据访问层使用Slick 3,我的界面使用简单的StdIn方法。但是我在读台词方面有些困难。我的主菜单运行良好,而内部菜单有时运行异常。特别是,当我第一次输入命令时,我没有得到任何结果,只是再次显示相同的菜单。然后我输入任何命令并得到结果。如果我试着输入一些3d时间的命令,我的程序就会停止运行System.exit。 以下是我的界面代码: object UserInterface { def displayMainMenu()

我在Scala中创建了一个控制台接口应用程序作为todo列表。我的数据访问层使用Slick 3,我的界面使用简单的StdIn方法。但是我在读台词方面有些困难。我的主菜单运行良好,而内部菜单有时运行异常。特别是,当我第一次输入命令时,我没有得到任何结果,只是再次显示相同的菜单。然后我输入任何命令并得到结果。如果我试着输入一些3d时间的命令,我的程序就会停止运行System.exit。 以下是我的界面代码:

object UserInterface {
  def displayMainMenu(): Unit ={
    println("Main menu:" + " \n1 - Login" + "\n2 - Exit")
    println("\nChoose the operation you want to perform:")
    val inputMainMenu = readInt()
    buildMainMenu(inputMainMenu)
  }

  def buildMainMenu(inputNumber: Int) =  inputNumber match {
    case 1 => enterSystem()
    case 2 => System.exit(0)
    case _ => println("Your input was wrong. Try again"); displayMainMenu()
  }

  def enterSystem(): Unit ={
    println("Input you login, please:")
    val inputLogin = readLine()
    println("Input you password, please:")
    val inputPassword = readLine()

    val checkLogin = Await.result(DAO.checkUserLogin(inputLogin, inputPassword), Duration.Inf).toString
    val userId = DAO.selectUserId(inputLogin)

    def changeOutputs(checkLogin: String):Unit = checkLogin match {
      case "true" => println("You have successfully entered"); displayInnerMenu(); buildMenu(userId)
      case "false" => println("Your input for login or password is wrong. Please, try again"); displayMainMenu()
      case _ => println("Your input is wrong"); displayMainMenu()
    }
    changeOutputs(checkLogin)
  }

  def buildMenu(userId: Long): Unit ={
    def chooseOption(number: Int):Unit = number match {
      case 1 => displayFinishedTasks(userId)
      case 2 => displayUnfinishedTasks(userId)
      case 3 => addTask(userId)
      case 4 => deleteTask()
      case 5 => markTaskAsFinished(userId)
      case 6 => displayMainMenu()
      case _ => println("Your input is wrong"); displayMainMenu()
    }
    val inputNum = displayInnerMenu()
    chooseOption(inputNum)

  }

  def displayInnerMenu():Int ={
    println("TODO List:" + "\n1 - Display finished tasks" + "\n2 - Display unfinished tasks"
      + "\n3 - Add task" + "\n4 - Delete task" + "\n5 - Mark task as finished" + "\n6 - Get back to the main menu")
    println("\nChoose the operation you want to perform:")
    val inputNum = readInt()
    inputNum
  }

  def displayAllTasks(id: Long) = {
    println()
    println("User's tasks:\n" + Await.result(DAO.selectTasksByUser(id), Duration.Inf).toList.toString)
    displayInnerMenu()
  }

  def displayFinishedTasks(id: Long) = {
    println()
    println("User's finished tasks:\n" + Await.result(DAO.selectFinishedTasks(id), Duration.Inf).toList.toString)
    displayInnerMenu()
  }

  def displayUnfinishedTasks(id: Long) = {
    println()
    println("User's unfinished tasks:\n" + Await.result(DAO.selectUnfinishedTasks(id), Duration.Inf).toList.toString)
    displayInnerMenu()
  }

  def addTask(id: Long) = {
    println()
    println("Input the task name you want to create, please:")
    val taskName = readLine()
    Await.result(DAO.addTask(taskName, id), Duration.Inf)
    displayInnerMenu()
  }

  def deleteTask() = {
    println()
    println("Choose the task you want to delete, please:")
    val taskId = readLong()
    Await.result(DAO.deleteTask(Some(taskId)), Duration.Inf)
    displayInnerMenu()
  }

  def markTaskAsFinished(id: Long) = {
    println()
    println("Choose the task you want to mark as finished, please:")
    val taskId = readLong()
    Await.result(DAO.finishTask(Some(taskId), id), Duration.Inf)
    displayInnerMenu()
  }

}

我想要的是某种无限循环,这样我可以根据需要多次执行命令或设置限制。那么,我可以在这段代码中引入哪些更改?我将非常感谢您的帮助

您的特别麻烦似乎来自以下事实:
changeOutputs
in
进入系统
调用
显示内部菜单
,该菜单从输入中读取一个
Int
,但对它没有任何用处。在调用
displayInnerMenu
的大多数地方,您可能应该调用
buildMenu

另外,您似乎应该提高调试技能。这是一项至关重要的技能,而且这段代码调试起来并不难

从更广泛的角度来看,这是一个复杂的话题,没有简单的最佳答案。但是肯定有不好的,不幸的是你的就是其中之一。代码中我最不喜欢的是菜单项标题和菜单项操作之间的代码大分隔。(只需想象在中间添加新的菜单项需要什么,或者创建一个更深的菜单,其中有一些在级别之间共享的项目。)所以我会重新编写大部分代码。与FP相比,我更喜欢OOP,我会这样做:

object UserInterface {

  // should be non-generic for simplicity of the rest of the code
  trait MenuAndStateNG {
    def runMenu(): MenuAndStateNG
  }

  trait MenuItem[S] {
    val name: String

    def doAction(state: S, curMenu: MenuAndStateNG): MenuAndStateNG
  }

  case class Menu[S](header: String, items: Seq[MenuItem[S]]) {}

  case class MenuAndState[S](menu: Menu[S], state: S) extends MenuAndStateNG {

    def runMenu(): MenuAndStateNG = {
      var inputNum: Int = -1
      var isFirstRun = true

      // we use 1-based indices in the menu
      while (inputNum <= 0 || inputNum > menu.items.length) {
        if (!isFirstRun) {
          println("Your input was wrong. Try again")
        }
        isFirstRun = false

        println(menu.header + ":")
        println(menu.items.zipWithIndex.map({ case (item, index) => s"${index + 1} - ${item.name}" }).mkString("\n"))
        println("Choose the operation you want to perform:")

        inputNum = StdIn.readInt()
      }
      println()
      val nextMenu = menu.items(inputNum - 1).doAction(state, this)
      nextMenu
    }
  }


  // most of menu items doesn't change current menu
  // let's make it easier to implement
  trait SimpleMenuItem[S] extends MenuItem[S] {
    override def doAction(state: S, curMenu: MenuAndStateNG): MenuAndStateNG = {
      doSimpleAction(state)
      curMenu
    }

    def doSimpleAction(state: S): Unit
  }


  def start(): Unit = {
    var curMenu: MenuAndStateNG = MenuAndState(mainMenu, ())
    var isFirstRun = true
    while (true) {
      if (!isFirstRun) {
        println
      }
      isFirstRun = false
      curMenu = curMenu.runMenu()
    }
  }


  private val loginItem = new MenuItem[Unit] {
    override val name = "Login"

    override def doAction(state: Unit, curMenu: MenuAndStateNG): MenuAndStateNG = {
      println("Input you login, please:")
      val inputLogin = StdIn.readLine()
      println("Input you password, please:")
      val inputPassword = StdIn.readLine()

      val checkLogin = Await.result(DAO.checkUserLogin(inputLogin, inputPassword), Duration.Inf).toString
      val userId = DAO.selectUserId(inputLogin)

      checkLogin match {
        case "true" =>
          println("You have successfully entered")
          MenuAndState(userMenu, userId)
        case "false" =>
          println("Your input for login or password is wrong. Please, try again")
          curMenu
        case _ =>
          println("Your input is wrong")
          curMenu
      }
    }
  }

  private val exitItem = new MenuItem[Unit] {
    override val name = "Exit"

    override def doAction(state: Unit, curMenu: MenuAndStateNG): MenuAndStateNG = {
      System.exit(0)
      null // null is bad but it doesn't matter by now
    }
  }


  private val displayFinishedTasks = new SimpleMenuItem[Int] {
    override val name: String = "Display finished tasks"

    override def doSimpleAction(state: Int): Unit = {
      println("User's finished tasks:\n" + Await.result(DAO.selectFinishedTasks(id), Duration.Inf).toList.toString)
    }
  }

  private val displayUnfinishedTasks = new SimpleMenuItem[Int] {
    override val name: String = "Display unfinished tasks"

    override def doSimpleAction(state: Int): Unit = {
      println("User's unfinished tasks:\n" + Await.result(DAO.selectUnfinishedTasks(id), Duration.Inf).toList.toString)
    }
  }

  private val displayAllTasks = new SimpleMenuItem[Int] {
    override val name: String = "Display all tasks"

    override def doSimpleAction(state: Int): Unit = {
      println("User's tasks:\n" + Await.result(DAO.selectTasksByUser(id), Duration.Inf).toList.toString)
    }
  }

  private val addTask = new SimpleMenuItem[Int] {
    override val name: String = "Add task"

    override def doSimpleAction(state: Int): Unit = {
      println("Input the task name you want to create, please:")
      val taskName = readLine()
      Await.result(DAO.addTask(taskName, id), Duration.Inf)
    }
  }
  private val deleteTask = new SimpleMenuItem[Int] {
    override val name: String = "Delete task"

    override def doSimpleAction(state: Int): Unit = {
      println("Choose the task you want to delete, please:")
      val taskId = readLong()
      Await.result(DAO.deleteTask(Some(taskId)), Duration.Inf)
    }
  }

  private val markTaskFinished = new SimpleMenuItem[Int] {
    override val name: String = "Mark task as finished"

    override def doSimpleAction(state: Int): Unit = {
      println("Choose the task you want to mark as finished, please:")
      val taskId = readLong()
      Await.result(DAO.finishTask(Some(taskId), id), Duration.Inf)
    }
  }

  private val logoutTask = new MenuItem[Int] {
    override val name = "Get back to the main menu"

    override def doAction(state: Int, curMenu: MenuAndStateNG): MenuAndState[Unit] = {
      MenuAndState(mainMenu, ())
    }
  }

  val mainMenu: Menu[Unit] = Menu("Main menu", List(loginItem, exitItem))

  val userMenu: Menu[Int] = Menu("User menu", List(
    displayAllTasks,
    displayFinishedTasks,
    displayUnfinishedTasks,
    addTask,
    deleteTask,
    markTaskFinished,
    logoutTask))

}
对象用户界面{
//应为非泛型,以简化其余代码
性状MenuAndStateNG{
def runMenu():MenuAndStateNG
}
特征菜单项[S]{
val名称:String
def doAction(状态:S,当前菜单:菜单和状态):菜单和状态
}
案例类菜单[S](标题:字符串,项:Seq[MenuItem[S]]){}
案例类MenuAndState[S](菜单:菜单[S],状态:S)扩展了MenuAndStateNG{
def runMenu():MenuAndStateNG={
变量inputNum:Int=-1
var isFirstRun=true
//我们在菜单中使用基于1的索引
while(inputNum menu.items.length){
如果(!isFirstRun){
println(“您的输入错误,请重试”)
}
isFirstRun=false
println(menu.header+“:”)
println(menu.items.zipWithIndex.map({case(item,index)=>s“${index+1}-${item.name}”).mkString(“\n”))
println(“选择要执行的操作:”)
inputNum=StdIn.readInt()
}
println()
val nextMenu=menu.items(inputNum-1).doAction(状态,this)
下一个菜单
}
}
//大多数菜单项不会更改当前菜单
//让我们让它更容易实现
trait simplenumitem[S]扩展了MenuItem[S]{
覆盖def doAction(状态:S,curMenu:MenuAndStateNG):MenuAndStateNG={
行动(州)
curMenu
}
def操作(状态:S):单位
}
def start():单位={
变量curMenu:MenuAndStateNG=MenuAndState(主菜单,())
var isFirstRun=true
while(true){
如果(!isFirstRun){
普林顿
}
isFirstRun=false
curMenu=curMenu.runMenu()
}
}
private val loginItem=新菜单项[单位]{
覆盖val name=“登录”
覆盖def doAction(状态:单位,当前菜单:菜单和状态):菜单和状态={
println(“请输入您的登录名:”)
val inputLogin=StdIn.readLine()
println(“请输入您的密码:”)
val inputPassword=StdIn.readLine()
val checkLogin=Await.result(DAO.checkUserLogin(inputLogin,inputPassword),Duration.Inf).toString
val userId=DAO.selectUserId(inputLogin)
检查登录匹配{
大小写“true”=>
println(“您已成功输入”)
菜单和状态(用户菜单,用户ID)
大小写“false”=>
println(“您的登录或密码输入错误。请重试”)
curMenu
案例=>
println(“您的输入错误”)
curMenu
}
}
}
private val exitItem=新菜单项[单位]{
覆盖val name=“退出”
覆盖def doAction(状态:单位,当前菜单:菜单和状态):菜单和状态={
系统退出(0)
null//null很糟糕,但现在已经不重要了
}
}
private val displayFinishedTasks=新SimpleNuItem[Int]{
覆盖val名称:String=“显示已完成的任务”
覆盖操作(状态:Int):单位={
println(“用户完成的任务:\n”+wait.result(DAO.selectFinishedTasks(id),Duration.Inf)。toList.toString)
}
}
private val displayUnfinishedTasks=新SimpleNuItem[Int]{
覆盖val名称:String=“显示未完成的任务”
覆盖操作(状态:Int):单位={
println(“用户未完成的任务:\n”+wait.result(DAO.selectUnfinishedTasks(id),Duration.Inf)。toList.toString)
}
}
private val displayAllTasks=new simplenumitem[Int]{
覆盖val名称:String=“显示所有任务”
覆盖操作(状态:Int):单位={
println(“用户的任务:\n”+wait.result(DAO.selectTasksByUser(id),Duration.Inf).toList.toString)
}
}
private val addTask=new simplenumitem[Int]{
覆盖val名称:String=“添加任务”
覆盖操作(状态:Int):单位={
println(“请输入要创建的任务名称:”)
val taskName=readLine()
wait.result(DAO.addTask(taskName,id),Duration.Inf)
}
}
private val deleteTask=新SimpleNuitem[Int]{
覆盖val名称:String=“删除任务”
覆盖操作(状态:Int):单位={
println(“请选择要删除的任务:”)
val taskId=readLong