鉴于这一系列要求:
>输入来自1到10之间的基本无限的随机数流
>最终输出为SUCCEED或FAIL>在任何特定时间都可以有多个对象“收听”流,并且他们可以在不同的时间开始收听,因此他们可能对“第一”数字有不同的概念;因此,流的监听器需要与流本身分离.伪代码:
if (first number == 1) SUCCEED else if (first number >= 9) FAIL else { first = first number rest = rest of stream for each (n in rest) { if (n == 1) FAIL else if (n == first) SUCCEED else continue } }
这是一个可能的可变实现:
sealed trait Result case object Fail extends Result case object Succeed extends Result case object NoResult extends Result class StreamListener { private var target: Option[Int] = None def evaluate(n: Int): Result = target match { case None => if (n == 1) Succeed else if (n >= 9) Fail else { target = Some(n) NoResult } case Some(t) => if (n == t) Succeed else if (n == 1) Fail else NoResult } }
这会起作用,但闻起来对我来说. StreamListener.evaluate不是引用透明的.并且使用NoResult令牌感觉不对.它虽然清晰易用/代码,但确实具有优势.除此之外,还有一个功能性解决方案吗?
我想出了其他两个可能的选择:
>评估返回一个(可能是新的)StreamListener,但这意味着我必须使Result成为StreamListener的子类型,感觉不对.
>让评估将Stream [Int]作为参数,让StreamListener负责消耗尽可能多的Stream,以确定失败或成功.我在这种方法中看到的问题是,注册侦听器的类应该在生成每个数字后查询每个侦听器,并在失败或成功时立即采取适当的操作.通过这种方法,我不知道如何发生这种情况,因为每个监听器都强制评估Stream,直到完成评估.这里没有单一数字生成的概念.有没有标准的Scala / FP成语我在这里俯瞰?
考虑到您的第一个可能的选项,我不确定为什么您将Result作为StreamListener的子类型,而不是只使用与StreamListeners相关的Result的特定子类型.sealed trait Result sealed trait FinalizedResult extends Result trait StreamListener { def evaluate(n: Int): Result } case object Uninitialized extends Result with StreamListener { def evaluate(n: Int): Result = { n match { case i if (n == 1) => Succeed case i if (n >= 9) => Fail case _ => Initialized(n) } } } case class Initialized(target: Int) extends Result with StreamListener { def evaluate(n: Int): Result = { n match { case i if (n == target) => Succeed case i if (n == 1) => Fail case _ => this } } } case object Succeed extends FinalizedResult case object Fail extends FinalizedResult
但是,你是不是只是将可变性改为调用代码来跟踪对Results / StreamListeners的引用?
精彩评论