【问题标题】:Using partial functions to model Set in Scala, e.g. type alias: Set = Int => Boolean在 Scala 中使用部分函数对 Set 进行建模,例如类型别名:Set = Int => Boolean
【发布时间】:2016-08-11 01:44:57
【问题描述】:

我试图了解类型别名的工作原理以及扩展,一个以type Set = Int => Boolean 为模型的简单Set 类型将如何被重写为具有部分函数的类。

背景:我知道我可以像上面一样创建类型别名,并将其用作简单的函数集数据类型:

type Set = Int => Boolean
def union(s: Set, t: Set): Set = (x => s(x) || t(x))
def contains(s: Set, n: Int): Boolean = s(n)
val s1 = Set(3)
val s2 = Set(7)
val u1 = union(s1, s2)
contains(u1, 3)                // res4: Boolean = true

一切顺利。需要一些思考,但我知道它是如何工作的; u1 本质上变成了一个偏函数,基于:

union(s: (Int => Boolean), t: (Int => Boolean)): Int => Boolean

所以如果我想把它变成一个类,我的第一次尝试是这样的:

case class Set(v: Int) {
    def apply(i: Int) = i == v
    def contains(n: Int): Boolean = this(n)
    def union(n: Set): Set = (x => n(x) || apply(x))  // Missing param
}

但这不起作用(union 的定义有编译错误,x 是缺少参数)。

在摆弄了一段时间之后,我不知道如何在不采用更命令式的方法(例如实际创建基于集合的 Set 类)的情况下实现类似的类型。必须有一种方法可以以纯粹的功能方式做到这一点......

【问题讨论】:

  • 我看不到不诉诸面向对象的方法来实现您想要的。 union 的问题在于它应该是一个 Set,但它被定义为一个函数。为了使它成为一个 Set,我们需要一个 Set 的扩展实例,其中 apply 可以满足任何集合,这几乎违背了基于函数的实现的要点。
  • 类型别名仅通过替换起作用。
  • 我认为您的范围内有 3 个 Set 定义,哈哈... xD 1. 您的类型别名 Set(function), 2. val s1 = Set(3) (scala.Set ) 3. 你的case class Set。并不奇怪它不起作用......不,联合不是一个偏函数。
  • 另外,这是一项来自(Scala 中的 FP)[coursera.org/learn/progfun1] 的任务,所以我们不应该根据learner.coursera.help/hc/en-us/articles/… 来回答它。
  • 以上是两个不同的(例如不一起编译)代码块。这也是 Programming Scala 书中介绍的一个主题,我正试图更彻底地理解它......是的,在 Odersky 的课堂上重新引入,我已经 100% 毕业了......不,不相关。

标签: scala type-alias


【解决方案1】:

您需要包装Int => Boolean,而不是Int。所以

case class Set(f: Int => Boolean) {
  def apply(i: Int) = f(i)
  def union(s: Set) = Set(x => this(x) || s(x))
  // or Set(union(f, s.f)) to use existing definition of union
}

对于你自己定义的Setapply 使它成为一个单元素集,所以你不能定义union 也就不足为奇了:两个单元素集的并集可以有多个元素。

Scala 对值和类型有单独的命名空间。 type Setclass Set 仅定义 type Set,而 val Setobject Setvar Set 定义一个值,而 case class 定义两者。现在,Set(3) 需要一个名为Set;在您的第一个代码示例中没有这样的值,因此它使用来自scala.Predef 的值,它是自动导入的。 scala.Predef.Set(3) 返回一个scala.collection.Set[Int],它恰好扩展了Int => Boolean,这就是union(s1, s2) 编译的原因。 case class 定义了一个类型一个值;所以上面的代码Set(3) 不能编译,因为这个值被用来代替scala.Predef.Set

【讨论】:

  • 这很有趣——没有想过包装Int => Boolean——但是,它没有到达所需的端点,这是一个类似于基于类型别名的API的API。也就是说,例如能够val s1 = Set(3)s1.union(s2)
  • 如果要写val s1 = Set(3),只需要在object Set中定义def apply(i: Int)即可。请注意,在您的第一段代码中Set(3) 使用scala.collection.Set,而不是您定义的代码! union 之所以有效,是因为标准 Set[A] 扩展了 A => Boolean
  • 我已经添加了更详细的解释。
  • 啊!加佐克斯。难怪我得到了如此奇怪的结果。是的,改为“CSet”,现在事情变得更有意义了。谢谢,这是我一直缺少的信息 - 并解释了为什么它的行为方式。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2013-10-05
  • 2021-08-14
  • 1970-01-01
  • 2019-05-24
  • 2011-05-23
  • 1970-01-01
  • 2011-04-28
  • 1970-01-01
相关资源
最近更新 更多