Scala 使while循环更具规模esqe

Scala 使while循环更具规模esqe,scala,Scala,下面的循环完成了什么,这一点非常清楚。我有点认为它可以做得更像scala风格,但我看不太清楚。我发布这篇文章是想看看是否有人比我更有灵感 var bunnies: List[Bunny] = List.fill(nBunniesAtStart)(generateInitialBunny) var doContinue = true while (doContinue) { val prevBunnies = bunnies bunnies = evolveOneY

下面的循环完成了什么,这一点非常清楚。我有点认为它可以做得更像scala风格,但我看不太清楚。我发布这篇文章是想看看是否有人比我更有灵感

  var bunnies: List[Bunny] = List.fill(nBunniesAtStart)(generateInitialBunny)
  var doContinue = true

  while (doContinue) {
    val prevBunnies = bunnies

    bunnies = evolveOneYear(bunnies)

    print(bunnies, prevBunnies)

    if (bunnies.isEmpty) {
      println("\n\n\n  No more bunnies...")
      doContinue = false
    } else {
      println("Hit Enter to continue, or q + Enter to quit.\n")
      doContinue = readLine.isEmpty()
    }
  }
我写这段代码是为了回答一个问题

编辑
多亏了@wingedsubmariner和@Kigyo,我有了这个选择:

  val startBunnies: List[Bunny] = List.fill(nBunniesAtStart)(generateInitialBunny)
  userInputLoop(startBunnies, "")

  @tailrec
  def userInputLoop(bunnies: List[Bunny], userInput: String): Unit = {
    if (userInput.nonEmpty) println("Exiting")
    else evolveOneYear(bunnies) match {
      case Nil => 
        print(Nil, bunnies)
        println("No more bunnies...")
      case newBunnies =>
        print(newBunnies, bunnies)
        userInputLoop(newBunnies, readLine())
    }
  }

编辑2

另一个尝试,基于Chris Martin和Ben Kovitz的一些想法:

  class NoMoreBunniesException extends Exception("No more bunnies...")
  class UserStoppageException extends Exception("Exiting at your request.")

  def doesUserWantToContinue(): Try[_] = {
    println("Hit Enter to continue, or q + Enter to quit.\n");
    if (readLine().isEmpty) Success() else Failure(new UserStoppageException)
  }

  def validateBunnies(bunnies: List[Bunny]): Try[_] = {
    if (bunnies.isEmpty) Failure(new NoMoreBunniesException)
    else Success()
  }

  def checkNotEmptyAndUserContinuation(bunnies: List[Bunny]): Try[_] =
    validateBunnies(bunnies).flatMap(_ => doesUserWantToContinue)

  val firstBunnies = List.fill(nBunniesAtStart)(generateInitialBunny)
  println(s"${buildBunniesReport(firstBunnies).toString}\n\n")

  val timeline = Stream.iterate(firstBunnies)(evolveOneYear)
  val timelineWithPrev = timeline.tail.zip(timeline)
  val statusAndReportTimeline = timelineWithPrev.map {
    case (bunnies, prevBunnies) =>
      (checkNotEmptyAndUserContinuation(bunnies), buildFullReport(bunnies, prevBunnies))
  }

  // main loop including user interaction  
  statusAndReportTimeline.takeWhile {
    case (Success(_), _) => true
    case (Failure(e), report) => { println(s"${report}\n\n${e.getMessage}"); false }
  }.foreach { case (_, report) => println(report) }

您可以使用尾部递归函数而不是while循环,并省略变量,从而使scala更为惯用:

import scala.annotation.tailrec

val startBunnies = List.fill(nBunniesAtStart)(generateInitialBunny)

@tailrec
def loop(prevBunnies: List[Bunny]): Unit = {
  val bunnies = evolveOneYear(prevBunnies)

  print(bunnies, prevBunnies)

  if (bunnies.isEmpty) {
    println("\n\n\n  No more bunnies...")
  } else {
    println("Hit Enter to continue, or q + Enter to quit.\n")
    if (readLine.isEmpty)
      loop(bunnies)
  }
}
loop(startBunnies)

您可以使用尾部递归函数而不是while循环,并省略变量,从而使scala更为惯用:

import scala.annotation.tailrec

val startBunnies = List.fill(nBunniesAtStart)(generateInitialBunny)

@tailrec
def loop(prevBunnies: List[Bunny]): Unit = {
  val bunnies = evolveOneYear(prevBunnies)

  print(bunnies, prevBunnies)

  if (bunnies.isEmpty) {
    println("\n\n\n  No more bunnies...")
  } else {
    println("Hit Enter to continue, or q + Enter to quit.\n")
    if (readLine.isEmpty)
      loop(bunnies)
  }
}
loop(startBunnies)
//分离功能逻辑。。。
val firstBunnies=List.fill(nBunniesAtStart)(generateInitialBunny)
val timeline=Stream.iterate(firstBunnies)(evolveOneYear)
对于((以前的bunnies,bunnies)
//分离函数逻辑。。。
val firstBunnies=List.fill(nBunniesAtStart)(generateInitialBunny)
val timeline=Stream.iterate(firstBunnies)(evolveOneYear)

对于((以前的兔子,兔子)这里有一个更实用(也许也更深奥)的解决方案:


这里有一个更实用(也许也更深奥)的解决方案:


<> P>我越是这样想,越是对在人类的时间尺度上阻塞<代码>输入流<代码>的想法感到困扰。 设置:

import akka.actor.{Actor, ActorRef, ActorSystem, Props}

// The actor system
val system = ActorSystem()

// The actors: a bunny farm, and a console to interact with it
val farm = system.actorOf(Props[BunnyFarm])
system.actorOf(Props(classOf[BunnyConsole], farm))

// Keep alive until the actor system terminates
system.awaitTermination()
兔子农场:

object BunnyFarm {

  // Define the messages that a bunny farm uses
  case object Advance
  case class Result(bunnies: Seq[Bunny], previousBunnies: Seq[Bunny])
}

class BunnyFarm extends Actor {
  import BunnyFarm._

  // A bunny farm's state consists of a list of its bunnies
  var bunnies = List.fill(nBunniesAtStart)(generateInitialBunny)

  def receive = {

    case Advance =>

      // Advance the state of the farm one year
      val previousBunnies = bunnies
      bunnies = evolveOneYear(bunnies)

      // Reply to the sender with the result
      sender ! Result(bunnies = bunnies, previousBunnies = previousBunnies)
  }
}
控制台界面:

class BunnyConsole(farm: ActorRef) extends Actor with akka.camel.Consumer {

  // Read from stdin
  def endpointUri = "stream:in"

  // Initially advance the farm once
  farm ! BunnyFarm.Advance

  def receive = {
    case m: akka.camel.CamelMessage => self forward m.bodyAs[String]

    // Each string message represents a line of user input
    case s: String => s match {
      case "" => farm ! BunnyFarm.Advance
      case _ => quit()
    }

    // When the bunny farm sends a result...
    case r: BunnyFarm.Result =>

      println(s"Previous bunnies: ${r.previousBunnies}")
      println(s"New bunnies:      ${r.bunnies}")

      if (r.bunnies.nonEmpty) {
        println("Hit Enter to continue, or q + Enter to quit.")
      } else {
        println("No more bunnies...")
        quit()
      }
  }

  // Terminate the actor system, thus halting the program
  def quit() = context.system.shutdown()
}
依赖项:

  • com.typesafe.akka:akka-actor
  • com.typesafe.akka:akka camel
  • org.apache.camel:camel流

编辑-为简洁起见,对同一解决方案进行了重构

设置:

import akka.actor.{Actor, ActorSystem, Props}

val system = ActorSystem()
system.actorOf(Props(classOf[BunnyConsole]))
system.awaitTermination()
控制台:

class BunnyConsole extends Actor with akka.camel.Consumer {

  def endpointUri = "stream:in"
  var bunnies = List.fill(nBunniesAtStart)(generateInitialBunny)
  advance()

  def receive = {
    case m: akka.camel.CamelMessage => m.bodyAs[String] match {
      case "" => advance()
      case _ => quit()
    }
  }

  def advance() {
    val previousBunnies = bunnies
    bunnies = evolveOneYear(bunnies)
    print(bunnies, previousBunnies)
    if (bunnies.nonEmpty) {
      println("Hit Enter to continue, or q + Enter to quit.")
    } else {
      println("No more bunnies...")
      quit()
    }
  }

  def quit() = context.system.shutdown()
}

<> P>我越是这样想,越是对在人类的时间尺度上阻塞<代码>输入流<代码>的想法感到困扰。 设置:

import akka.actor.{Actor, ActorRef, ActorSystem, Props}

// The actor system
val system = ActorSystem()

// The actors: a bunny farm, and a console to interact with it
val farm = system.actorOf(Props[BunnyFarm])
system.actorOf(Props(classOf[BunnyConsole], farm))

// Keep alive until the actor system terminates
system.awaitTermination()
兔子农场:

object BunnyFarm {

  // Define the messages that a bunny farm uses
  case object Advance
  case class Result(bunnies: Seq[Bunny], previousBunnies: Seq[Bunny])
}

class BunnyFarm extends Actor {
  import BunnyFarm._

  // A bunny farm's state consists of a list of its bunnies
  var bunnies = List.fill(nBunniesAtStart)(generateInitialBunny)

  def receive = {

    case Advance =>

      // Advance the state of the farm one year
      val previousBunnies = bunnies
      bunnies = evolveOneYear(bunnies)

      // Reply to the sender with the result
      sender ! Result(bunnies = bunnies, previousBunnies = previousBunnies)
  }
}
控制台界面:

class BunnyConsole(farm: ActorRef) extends Actor with akka.camel.Consumer {

  // Read from stdin
  def endpointUri = "stream:in"

  // Initially advance the farm once
  farm ! BunnyFarm.Advance

  def receive = {
    case m: akka.camel.CamelMessage => self forward m.bodyAs[String]

    // Each string message represents a line of user input
    case s: String => s match {
      case "" => farm ! BunnyFarm.Advance
      case _ => quit()
    }

    // When the bunny farm sends a result...
    case r: BunnyFarm.Result =>

      println(s"Previous bunnies: ${r.previousBunnies}")
      println(s"New bunnies:      ${r.bunnies}")

      if (r.bunnies.nonEmpty) {
        println("Hit Enter to continue, or q + Enter to quit.")
      } else {
        println("No more bunnies...")
        quit()
      }
  }

  // Terminate the actor system, thus halting the program
  def quit() = context.system.shutdown()
}
依赖项:

  • com.typesafe.akka:akka-actor
  • com.typesafe.akka:akka camel
  • org.apache.camel:camel流

编辑-为简洁起见,对同一解决方案进行了重构

设置:

import akka.actor.{Actor, ActorSystem, Props}

val system = ActorSystem()
system.actorOf(Props(classOf[BunnyConsole]))
system.awaitTermination()
控制台:

class BunnyConsole extends Actor with akka.camel.Consumer {

  def endpointUri = "stream:in"
  var bunnies = List.fill(nBunniesAtStart)(generateInitialBunny)
  advance()

  def receive = {
    case m: akka.camel.CamelMessage => m.bodyAs[String] match {
      case "" => advance()
      case _ => quit()
    }
  }

  def advance() {
    val previousBunnies = bunnies
    bunnies = evolveOneYear(bunnies)
    print(bunnies, previousBunnies)
    if (bunnies.nonEmpty) {
      println("Hit Enter to continue, or q + Enter to quit.")
    } else {
      println("No more bunnies...")
      quit()
    }
  }

  def quit() = context.system.shutdown()
}

这里还有另一种方法,目的是清晰和简单

import util.control.Breaks._

val bunniesStream: Stream[List[String]] = firstBunnies #:: bunniesStream.map(evolveOneYear)

breakable {
  for (bunnies <- bunniesStream) {
    println(bunnies)
    if (bunnies.isEmpty) {
      println("No more bunnies...");
      break
    } else {
      println("Hit Enter to continue, or q + Enter to quit.\n");
      if (!readLine().isEmpty) break
    }
  }
}
import util.control.Breaks_
val bunniesStream:Stream[List[String]=firstBunnies#:::bunniesStream.map(evolveOneYear)
易碎的{

对于(兔子)来说,这里还有另一种方法,目的是清晰和简单

import util.control.Breaks._

val bunniesStream: Stream[List[String]] = firstBunnies #:: bunniesStream.map(evolveOneYear)

breakable {
  for (bunnies <- bunniesStream) {
    println(bunnies)
    if (bunnies.isEmpty) {
      println("No more bunnies...");
      break
    } else {
      println("Hit Enter to continue, or q + Enter to quit.\n");
      if (!readLine().isEmpty) break
    }
  }
}
import util.control.Breaks_
val bunniesStream:Stream[List[String]=firstBunnies#:::bunniesStream.map(evolveOneYear)
易碎的{

对于(bunnies这里还有另一种方法。这太可怕了。我把它贴在这里,希望它能激励人们以一种更具可读性或至少是传统的方式重新实现它的基本思想

val bunniesStream = Stream.iterate(firstBunnies)(evolveOneYear)

case class Interaction(bunnies: List[String]) {
  lazy val print: Unit = println(bunnies)

  lazy val continue: Boolean = {
    print
    if (bunnies.isEmpty) { println("No more bunnies..."); false }
    else userOK
  }

  lazy val userOK: Boolean = {
    println("Hit Enter to continue, or q + Enter to quit.\n");
    readLine().isEmpty
  }
}

bunniesStream.map(Interaction).takeWhile(_.continue).force
我在这里尝试实现的想法是通过延迟评估获取用户的输入。在函数式中执行循环很困难的是,在读取用户输入之前,您不知道何时停止循环,但您可能需要在读取用户输入之前停止循环


最后一行将所有输入和输出捆绑到一个表达式中。如果没有
.force
,则该表达式应计算为一个对象,然后可以根据需要传递给其他函数。这似乎符合函数式编程的精神。在执行
.force
之前,不应发生任何输入或输出。除非它发生了,因为se我的方法有一些根本性的错误。我不太清楚它是什么。也许这个错误与我在
交互
课程中将决策与输入/输出混为一谈有关。

这里是另一种方法。它很可怕。我把它贴在这里,希望它能激励人们重新实现我的基本想法n以更具可读性或至少是传统的方式

val bunniesStream = Stream.iterate(firstBunnies)(evolveOneYear)

case class Interaction(bunnies: List[String]) {
  lazy val print: Unit = println(bunnies)

  lazy val continue: Boolean = {
    print
    if (bunnies.isEmpty) { println("No more bunnies..."); false }
    else userOK
  }

  lazy val userOK: Boolean = {
    println("Hit Enter to continue, or q + Enter to quit.\n");
    readLine().isEmpty
  }
}

bunniesStream.map(Interaction).takeWhile(_.continue).force
我在这里尝试实现的想法是通过延迟评估获取用户的输入。在函数式中执行循环很困难的是,在读取用户输入之前,您不知道何时停止循环,但您可能需要在读取用户输入之前停止循环


最后一行将所有输入和输出捆绑到一个表达式中。如果没有
.force
,则该表达式应计算为一个对象,然后可以根据需要传递给其他函数。这似乎符合函数式编程的精神。在执行
.force
之前,不应发生任何输入或输出。除非它发生了,因为se我的方法有一些根本性的错误。我不太清楚它是什么。也许这个错误与我在
交互
类中将决策与输入/输出混合有关。

你也可以在
进化一年(prevBunnies)上进行模式匹配
打印调用迫使我们使用
val
而不是
val
if else
@Kigyo。好吧,在模式匹配中,每个案例都可以有一个打印,但我想这是多余的。你也可以将打印集成到每个案例的现有打印中。我的自我只是找到
的打印无
-案例不相关:显式递归不是在特殊情况下需要避免的吗?也就是说,难道没有方便的函数组合符可以隐藏递归并让你更清楚地表达流吗?@BenKovitz如果可能的话,一定要避免。然而,这不是一个可以轻易避免的地方。我认为禁止写你的own-tail递归方法主要来源于它,这在集合中通常不是必需的-初学者会编写一个,因为他们不了解折叠的一般性。您还可以在
evo上进行模式匹配