如何建模递归函数类型?

时间:2015-01-24 19:57:10

标签: scala

我很好奇这个代码将如何在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中编写类似此模式的内容?

3 个答案:

答案 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在具体实现的集合(例如列表)中收集它们。