Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
scalacheck任意隐式和递归生成器_Scala_Recursive Datastructures_Scalacheck - Fatal编程技术网

scalacheck任意隐式和递归生成器

scalacheck任意隐式和递归生成器,scala,recursive-datastructures,scalacheck,Scala,Recursive Datastructures,Scalacheck,我看到scalacheck有一个非常明显的bug,如果它真的存在,我就看不出人们是如何将它用于递归数据结构的 在scalacheck接管之前,当构造任意值时,此程序失败,出现一个StackOverflowerError。请注意,树类型和树s的生成器是逐字提取的 更奇怪的是,如果更改二叉树结构,使值存储在节点s而不是叶s上,并将叶和节点定义更改为: def leafs: Gen[Leaf] = Gen.value(Leaf()) def nodes: Gen[Node] = for {

我看到scalacheck有一个非常明显的bug,如果它真的存在,我就看不出人们是如何将它用于递归数据结构的

在scalacheck接管之前,当构造
任意
值时,此程序失败,出现一个
StackOverflowerError
。请注意,
类型和
s的生成器是逐字提取的

更奇怪的是,如果更改二叉树结构,使值存储在
节点
s而不是
s上,并将
节点
定义更改为:

  def leafs: Gen[Leaf] = Gen.value(Leaf())

  def nodes: Gen[Node] = for {
    x <- ints     // Note: be sure to ask for x first, or it'll StackOverflow later, inside scalacheck code!
    left <- trees
    right <- trees
  } yield Node(left, right, x)
def leafs:Gen[Leaf]=Gen.value(Leaf())
def节点:Gen[节点]=用于{

x问题在于,当Scala计算
时,它会以无休止的递归结束,因为
是根据自身定义的(通过
节点
)。但是,当您将
以外的其他表达式作为for表达式的第一部分放在
节点
中时,Scala将延迟对for表达式其余部分的求值(以
map
flatMap
调用链包裹),并且不会发生无限递归

正如pedrofurla所说,如果
中的一个是非严格的,这可能不会发生(因为Scala不会立即计算参数)。但是,您可以使用
Gen.lzy
明确说明懒散。
lzy
接受任何生成器,并延迟对该生成器的求值,直到它真正被使用。因此,以下更改解决了您的问题:

def trees: Gen[Tree] = Gen.lzy(Gen.oneOf(leafs, nodes))
尽管上面下面的答案在程序启动时去掉了常量
StackOverflowerError
,但在我实际要求scalacheck检查属性时,我仍然会在三次中打一次
StackOverflowerError
。(我将上面的
Main
更改为运行
。检查
40次,会看到它成功两次,然后由于堆栈溢出而失败,然后成功两次,以此类推。)

最后,我不得不将一个硬块放到递归的深度,我想这就是将来在递归数据结构上使用scalacheck时要做的:

  def leafs: Gen[Leaf] = for {
    x <- ints
  } yield Leaf(x)

  def genNode(level: Int): Gen[Node] = for {
    left <- genTree(level)
    right <- genTree(level)
  } yield Node(left, right)

  def genTree(level: Int): Gen[Tree] = if (level >= 100) {leafs}
                                       else {leafs | genNode(level + 1)}
  lazy val trees: Gen[Tree] = genTree(0)
def leafs:Gen[Leaf]=for{

Daniel Martin自己的答案中对方法的一个轻微概括是使用。类似(未经测试):

def genTree()=Gen.size{size=>genTree0(size)}
def genTree0(最大深度:Int)=
if(maxDepth==0)leafs else Gen.oneOf(leafs,genNode(maxDepth))
def genNode(maxDepth:Int)=用于{

depthL推测:是否第一个
树总是计算两个参数,而使用
的树总是计算两个参数,如果
不计算呢?遗憾的是,
其中一个确实很严格:在scalacheck代码中,这仍然有大约三分之一的时间失败,但它确实回答了我的问题,并且确实移动了
堆栈溢出错误
任意
对象的创建中退出,并进入其被评估的位置。接受这个答案,但我将分别发布我最终必须做的事情,以帮助通过web搜索找到它的人。
def trees: Gen[Tree] = Gen.lzy(Gen.oneOf(leafs, nodes))
  def leafs: Gen[Leaf] = for {
    x <- ints
  } yield Leaf(x)

  def genNode(level: Int): Gen[Node] = for {
    left <- genTree(level)
    right <- genTree(level)
  } yield Node(left, right)

  def genTree(level: Int): Gen[Tree] = if (level >= 100) {leafs}
                                       else {leafs | genNode(level + 1)}
  lazy val trees: Gen[Tree] = genTree(0)
def genTree() = Gen.sized { size => genTree0(size) }

def genTree0(maxDepth: Int) = 
  if (maxDepth == 0) leafs else Gen.oneOf(leafs, genNode(maxDepth))

def genNode(maxDepth: Int) = for {
  depthL <- Gen.choose(0, maxDepth - 1)
  depthR <- Gen.choose(0, maxDepth - 1)
  left <- genTree0(depthL)
  right <- genTree0(depthR)
} yield Node(left, right)

def leafs = for {
  x <- ints
} yield Leaf(x)