使用N参数实现def作为Function类型的值

时间:2010-01-28 14:44:10

标签: scala lazy-evaluation currying

我可以使用def实现val,其中def不带参数:

trait T { def foo: Int }
class C(val foo: Int) extends T

为什么这不能扩展到实现def将N args带到FunctionN的val?我希望有可能实现类似的东西:

def expensiveOperation(p: Int => Boolean) : List[Int]

使用惰性函数val。类似的东西:

val expensiveOperation = {
    val l = //get expensive list
    l.filter _ //partially applied function
}

我知道这种语法似乎不适用于2.8。有什么我不知道的,为什么我不能实现 def将参数作为功能val

4 个答案:

答案 0 :(得分:2)

现在,编辑后,我想我明白你在追求什么。但是你不能做你想要的,因为类型签名不匹配。

def x: Int = 5
val x: Int = 5

在这两种情况下,您都不提供任何东西并返回Int(在本例中为5)。太好了!

def expensive(p: Int => Boolean): List[Int]

现在你提供一些东西。但是val只是某个存储对象。您可以向标签引用的对象提供 ,但这与向标签“x”提供内容不同。

如果你想要val覆盖它,那么你需要使用def:

def expensive: (Int=>Boolean)=>List[Int]

现在你有一些你没有参数调用的东西,它返回的东西可以采用Int =>布尔函数并给你一个List [Int]作为回报。这正是你用val得到的 - 在这两种情况下,你都有一些返回具有你想要的功能的对象的名称。

(在Scala中,vals实际上是作为隐藏字段实现的,getter方法不带任何参数并返回隐藏字段中的任何内容。所以它实际上就像def一样。)

答案 1 :(得分:2)

val不接受参数,因为它是计算并存储在字段中的。但我可能只是推荐你过去两天在邮件列表上发表的大量帖子。

或者,或者,让我们从Java的角度考虑它,因为Scala在jvm级别与Java兼容,并且它肯定必须遵守jvm规则。让我们从第一堂课开始:

abstract class X {
  def expensiveOperation(p: Int => Boolean) : List[Int] 
}

现在,我们来扩展它:

abstract class Y extends X {
  override val expensiveOperation: ((Int) => Boolean) => List[Int]
}

因此,从Java开始,我们知道类X的方法为expensiveOperation,它接收Function1[Int, Boolean]并返回List[Int]

现在我们去上课Y。当然,它必须定义相同的方法,但它还必须定义getter expensiveOperation,它不接收任何参数并返回Function1[Function1[Int, Boolean],List[Int]]

它可能是可行的,只要X中不存在这种额外的方法。所以我们来定义它:

class Z extends Y {
  override val extensiveOperation = new Function1[Function1[Int, Boolean], List[Int]] {
    def apply(p: Int => Boolean) = List range (1, 10) filter p
  }
}

如何定义? Scala是否将apply的主体复制为expensiveOperation的主体(接收参数的主体,而不是获取参数的主体)?它可能仍然可行。不过,让我们试试别的东西:

class W(f: ((Int) => Boolean) => List[Int]) extends Y {
  override val extensiveOperation = f
}

现在,我们如何覆盖参数接收extensiveOperation?我想我们可以这样写:

override def extensiveOperation(p: Int => Boolean) = extensiveOperation.apply(p)

这是可行的。但我个人认为这有点令人费解。我的建议:写一个简短的SID,并就Scala邮件列表达成一些协议。但是,如果没有代码来实现它,我认为它没有太大的机会被采用 - Scala必须跟踪每个函数类型val,以确定它是否覆盖了def

答案 2 :(得分:1)

你总是可以把它转发给你的val:

trait T {
  def expensiveOperation(p: Int => Boolean) : List[Int]
}

class C extends T {
  def expensiveOperation(p: Int => Boolean): List[Int] = {
      expensiveOperationVal(p) 
  }
  val expensiveOperationVal = { p: (Int=>Boolean) =>
    // ... lazy stuff
    List(1,2,3)
  }
}

而且,虽然这不能回答你的问题,但看起来除非你的// ... get expensive list代码依赖于谓词p,否则你可以做类似的事情:

class C extends T {
  def expensiveOperation(p: Int => Boolean): List[Int] = {
      myExpensiveList filter p 
  }
  lazy val myExpensiveList = {
    val l = // ... expensive stuff
    l
  }
}

答案 3 :(得分:0)

编辑:好吧,我没弄明白你的意思。但如果你的意思是我的意思......

您可以使用缩写语法创建应用操作的val:

val expensive = (p: (Int) => Boolean) => {
  val l = List(1,2,3,4)
  l filter p
}
scala> expensive(_<3)
res1: List[Int] = List(1,2)

但这实际上并没有缓存列表,这就是我认为你想要的。原因是这个简短的语法将所有内容放在=&gt;之后。进入Function1的apply方法。您可能希望列表存储如下:

val expensive = new Function1[Int=>Boolean,List[Int]] {
  lazy val l = List(1,2,3,4)
  def apply(p: (Int) => Boolean) = { l filter p }
}

并且我知道并没有一个很好的速记。

编辑:如果您很乐意在该区块之外创建列表,那么有一个简写(请参阅评论):

val expensive = List(1,2,3,4).filter _

scala> expensive(_ < 3)
res6: List[Int] = List(1, 2)