我很好奇这个代码将如何在Scala中建模。
这是在golang中,它是递归函数类型:
type walkFn func(*int) walkFn
所以上面只是类型的定义,walk函数是一个函数,它接受一个整数的指针,并返回一个walk函数。
示例实现如下:
func walkForward(i *int) walkFn {
*i += rand.Intn(6)
return pickRandom(walkEqual, walkBackward)
}
func walkBackward(i *int) walkFn {
*i += -rand.Intn(6)
return pickRandom(walkEqual, walkForward)
}
您可以在此处运行以下代码:http://play.golang.org/p/621lCnySmy
是否可以在Scala中编写类似此模式的内容?
答案 0 :(得分:8)
这是可能的。您可以使用存在类型来欺骗"欺骗" scala的循环参考限制:
type A[T <: A[_]] = Int => (Int, T)
lazy val walkEqual: A[A[_]] = (i: Int) =>
(i + Random.nextInt(7) - 3, if (Random.nextBoolean) walkForward else walkBackward)
lazy val walkForward: A[A[_]] = (i: Int) =>
(i + Random.nextInt(6), if (Random.nextBoolean) walkEqual else walkBackward)
lazy val walkBackward: A[A[_]] = (i: Int) =>
(i - Random.nextInt(6), if (Random.nextBoolean) walkEqual else walkForward)
def doWalk(count: Int, walkFn: A[_] = walkEqual, progress: Int = 0): Unit =
if (count > 0) {
val (nextProgress, nextStep: A[_] @unchecked) = walkFn(progress)
println(nextProgress)
doWalk(count - 1, nextStep, nextProgress)
}
结果:
scala> doWalk(10)
2
5
2
0
-3
-5
-4
-8
-8
-11
或者喜欢在@Travis Brown中添加:
val locations = Stream.iterate[(Int,A[_] @unchecked)](walkEqual(0)) {
case (x: Int, f: A[_]) => f(x)
}.map(_._1)
scala> locations.take(20).toList
res151: List[Int] = List(-1, 1, 1, 4, 1, -2, 0, 1, 0, 1, 4, -1, -2, -4, -2, -1, 2, 1, -1, -2)
答案 1 :(得分:5)
Scala的一个令人遗憾的事实是不支持递归类型别名。在REPL中执行以下操作会产生以下结果:
scala> type WalkFn = Function[Int, WalkFn]
<console>:7: error: illegal cyclic reference involving type WalkFn
type WalkFn = Function[Int, WalkFn]
^
另一个注意事项是,Scala不允许您通过引用修改值(通常不赞成,不,完全厌恶功能编程范例)。
然而,不要沮丧!还有其他选择。特征可以是自引用的,函数只是Scala中的类。因此,我们可以使用特征为通用递归WalkFn建模。此外,我们可以接受不可变值并让我们的函数返回下一个进度,而不是通过引用变更参数。
由于以下内容包含循环引用(WalkForward - &gt; WalkBackward,WalkBackward - &gt; WalkForward等),因此在运行以下内容之前,您需要在scala :paste
中键入REPL
示例(因此Scala编译器将在一个步骤中编译所有3个Walk{Forward,Backward,Equal}
实现。
首先:
$ scala
Welcome to Scala version 2.11.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_05).
Type in expressions to have them evaluated.
Type :help for more information.
scala> :paste
// Entering paste mode (ctrl-D to finish)
现在,粘贴代码:
import scala.util.Random
object Helpers {
def pickRandom[A](items: A*) =
items(Random.nextInt(items.length))
}
trait WalkFn extends (Int => (Int, WalkFn)) {}
object WalkForward extends WalkFn {
def apply(i: Int) =
( i + Random.nextInt(6),
Helpers.pickRandom(WalkEqual, WalkBackward) )
}
object WalkEqual extends WalkFn {
def apply(i: Int) =
( i + (Random.nextInt(7) - 3),
Helpers.pickRandom(WalkForward, WalkBackward) )
}
object WalkBackward extends WalkFn {
def apply(i: Int) =
( Random.nextInt(6) - 3,
Helpers.pickRandom(WalkEqual, WalkForward) )
}
def doWalk(count: Int, walkFn: WalkFn = WalkEqual, progress: Int = 0): Unit =
if (count > 0) {
val (nextProgress, nextStep) = walkFn(progress)
println(nextProgress)
doWalk(count - 1, nextStep, nextProgress)
}
doWalk(20)
然后,按照说明点击ctrl-D
。
享受醉酒的功能!
答案 2 :(得分:5)
我认为在Scala中更加惯用于分解迭代部分。例如,我们可以定义一个状态机:
import scala.util.Random
sealed trait Walker {
def i: Int
def advance: Walker
}
case class WalkEqual(i: Int) extends Walker {
def advance = {
val next = i + Random.nextInt(7) - 3
if (Random.nextBoolean) WalkForward(next) else WalkBackward(next)
}
}
case class WalkForward(i: Int) extends Walker {
def advance = {
val next = i + Random.nextInt(6)
if (Random.nextBoolean) WalkEqual(next) else WalkBackward(next)
}
}
case class WalkBackward(i: Int) extends Walker {
def advance = {
val next = i - Random.nextInt(6)
if (Random.nextBoolean) WalkEqual(next) else WalkForward(next)
}
}
然后我们可以写下以下内容:
val locations = Stream.iterate[Walker](WalkEqual(0))(_.advance).map(_.i)
这是我们步行者访问的无限位置。我们可以像这样使用它:
scala> locations.take(10).foreach(println)
0
0
-1
2
1
0
-5
-5
-10
-6
我们也可以采用有限数量的这些并通过编写locations.take(100).toList
在具体实现的集合(例如列表)中收集它们。