在尝试了解伴随对象时,我写了遵循代码,该代码计算了一类实例化的次数。我必须使用" var"来保持计数。是否有一种"功能编程"方法来实现相同的任务,即使用不变变量。
class C {
C.counter+=1
def someCFunction = {println ("some C function. Counter is "+C.counter)}
}
object C{
var counter:Int=0 //I do not want to use var
}
val c1 = new C
c1.someCFunction
val c2 = new C
c2.someCFunction
这是状态单元的好用例。而不是修改已适当的变量,而是创建一个新值,然后将其传递。
import cats.data.State
class C {}
object C { val counter: State[Int, Unit] = State.pure() }
def createNewC: State[Int, C] = {
// increment the count, and return a new instance of C
C.counter.modify(_ + 1).map(_ => new C)
}
val countAll = for {
c0 <- createNewC
c1 <- createNewC
c2 <- createNewC
c3 <- createNewC
} yield {
// use your instance of C in here
()
}
// actually run your program, start the counter at 0
countAll.run(0).value._1 // 4
注意:此处是来自猫项目。
纯粹功能性程序的重要特性之一,该程序避免了可变变量和其他副作用,即表达式评估的值仅取决于表达式本身。它不取决于评估哪些顺序的内容(从左到右,右至左,严格,懒惰),操作系统的状态,一天中的时间等。
尤其是,这意味着在纯粹的功能设置中,每个调用new C
都会返回一个完全相同的计数器对象。这通常是一件好事,因为它使您的程序更容易推理,但是它会阻碍您在那里尝试做的事情。要使c对象不同,您需要明确地通过其计数器值,说实话,这只是在地毯下扫除问题。
val c1 = new C(0)
val c2 = new C(1)
如果您想拥有一个像内部类变量这样的全局"计数器"变量,则使用一种在纯粹功能的设置中实现它的方法是将计数器值传递给需要计数器的每个函数并拥有这些功能功能还返回计数器的更新版本。简而言之:
def increment_counter(n: Int): Int = { n + 1)
def create_c(n: Int): (C, Int) = {
val c = new C(n)
val n' = increment_counter n
(c, n')
}
val n = 0
val (c1, n') = create_c(n)
val (c2, n'') = create_c(n')
val n' = increment_counter(n)
您可以通过状态单模式更好地构建它(大多数对单子的介绍可能会以此为例)。
但是,它很可能最终会比仅使用可变变量的计数器更复杂。实际上,我通常在功能性语言中使用这些"全球增量计数器"的可变变量,使我可以这样做。
并不是说 var
是一件坏事。这是有原因的语言。只是它代表的是,应在可能的情况下避免维持某种形式的可变状态的实体。当无法避免时,如果设计要求总的集体实例运行,则其范围应尽可能限制。
class C private { // private constructor, can only use factory method
def someCFunction = {println ("some C function. Counter is "+ C.getCount())}
}
object C{
private[this] var counter:Int = 0 // not even companions can see this
def apply() = {counter += 1; new C} // factory method
def getCount() = counter // accessor method
}
val c1 = C()
c1.someCFunction
val c2 = C()
c2.someCFunction