Scala懒惰评估和应用功能 [英] Scala lazy evaluation and apply function

查看:115
本文介绍了Scala懒惰评估和应用功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在按照一本书的示例在Scala中使用惰性评估来实现Steam类.

I'm following a book's example to implement a Steam class using lazy evaluation in Scala.

sealed trait Stream[+A]
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]

object Stream {
    def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = {
        lazy val head = hd
        lazy val tail = tl
        Cons(() => head, () => tail)
    }

    def empty[A]: Stream[A] = Empty

    def apply[A](as: A*): Stream[A] = {
        if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*))
    }
}

然后我使用一个简单的函数来测试它是否正常工作

Then I used a simple function to test if it's working

def printAndReturn: Int = {
    println("called")
    1
}

然后我像下面这样构造Stream:

Then I construct Stream like the following:

    println(s"apply: ${
        Stream(
            printAndReturn,
            printAndReturn,
            printAndReturn,
            printAndReturn
        )
    }")

输出如下:

called
called
called
called
apply: Cons(fpinscala.datastructures.Stream$$$Lambda$7/1170794006@e580929,fpinscala.datastructures.Stream$$$Lambda$8/1289479439@4c203ea1)

然后我使用cons构造了Stream:

Then I constructed Stream using cons:

println(s"cons: ${
    cons(
        printAndReturn,
        cons(
            printAndReturn,
            cons(printAndReturn, Empty)
        )
    )
}")

输出为:

cons: Cons(fpinscala.datastructures.Stream$$$Lambda$7/1170794006@2133c8f8,fpinscala.datastructures.Stream$$$Lambda$8/1289479439@43a25848)

所以这是两个问题:

  1. 使用apply函数构造Stream时,将评估所有printAndReturn.这是因为对apply(as.head, ...)的递归调用会评估每个头吗?
  2. 如果第一个问题的答案是正确的,那么如何更改apply使其不强制评估?
  1. When constructing Stream using the apply function, all printAndReturn are evaluated. Is this because the recursive call to apply(as.head, ...) evaluates every head?
  2. If the answer to the first question is true, then how do I change apply to make it not force evaluation?

推荐答案

  1. 否.如果在println上放置一个断点,您会发现在第一次创建Stream时实际上正在调用该方法.实际上,行Stream(printAndReturn, ...会调用您的方法,无论您将其放置在何处多次.为什么?考虑consapply的类型签名:

  1. No. If you put a breakpoint on the println you'll find that the method is actually being called when you first create the Stream. The line Stream(printAndReturn, ... actually calls your method however many times you put it there. Why? Consider the type signatures for cons and apply:

def cons[A](hd: => A, tl: => Stream[A]): Stream[A]

vs:

def apply[A](as: A*): Stream[A]

请注意,cons的定义的参数标记为=> A.这是一个按名称的参数.声明这样的输入会使它变得很懒,从而将其求值延迟到实际使用之前.因此,您的println将永远不会使用cons来调用.将此与apply进行比较.您没有使用by name参数,因此传递给该方法的所有内容都会自动进行评估.

Note that the definition for cons has its parameters marked as => A. This is a by-name parameter. Declaring an input like this makes it lazy, delaying its evaluation until it is actually used. Hence your println will never get called using cons. Compare this to apply. You're not using a by name parameter and therefore anything that gets passed in to that method will automatically get evaluated.

不幸的是,到目前为止,还没有一种超级简单的方法.您真正想要的是类似def apply[A](as: (=>A)*): Stream[A]的东西,但是不幸的是,Scala不支持按名称参数使用vararg.有关如何解决此问题的一些想法,请参见此答案.一种方法是在创建Stream时只包装函数调用:

Unfortunately there isn't a super easy way as of now. What you really want is something like def apply[A](as: (=>A)*): Stream[A] but unfortunately Scala does not support vararg by name parameters. See this answer for a few ideas on how to get around this. One way is to just wrap your function calls when creating the Stream:

Stream(
  () => printAndReturn,
  () => printAndReturn,
  () => printAndReturn,
  () => printAndReturn)

然后将延迟评估.

这篇关于Scala懒惰评估和应用功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆