【问题标题】:Default value for method parameter is not considered不考虑方法参数的默认值
【发布时间】:2019-09-11 05:08:56
【问题描述】:

我已经为“Empty”案例对象以及“Top”案例类中的参数“currentStack”提供了默认值,但是如果我在调用 push 方法时省略了该参数,我会收到以下消息

"error: not enough arguments for method push: (newTop
: A, currentStack: Main.Stack[A])Main.Stack[A].
Unspecified value parameter currentStack.
                    currentBracket == '{') isBracketingValid(rest, bracketStack.push(currentBracket))".

我尝试构建一个空堆栈以及一个已经填充的堆栈,并在它们上调用 push 方法,它可以添加一个元素。一旦我尝试通过进一步的推送调用添加另一个元素,我就会收到上述错误消息。

lazy val s1 = Empty

println(s1.push(1)) // <- works

//println(s1.push(1).push(2)) <- doesn't work

lazy val s2 = Top(3, Top(4, Empty))

println(s2.push(1)) // <- works

//println(s2.push(1).push(2)) <- doesn't work

这里是堆栈定义:

sealed trait Stack[+A] {
    def push[A] (newTop: A, currentStack: Stack[A]): Stack[A] = ???

    def pop: (Option[A], Stack[A]) = ???
}

case object Empty extends Stack[Nothing] {
    override def push[A] (newTop: A, currentStack: Stack[A] = Empty): Stack[A] = Top(newTop, currentStack)

    override def pop: (Option[Nothing], Stack[Nothing]) = (None, Empty)
}

case class Top[A](val top: A, val rest: Stack[A]) extends Stack[A] {
    override def push[A] (newTop: A, currentStack: Stack[A] = Top(top, rest)): Stack[A] = Top(newTop, currentStack)

    override def pop: (Option[A], Stack[A]) = (Some(top), rest)
}

def isBracketingValid(bracketString: String): Boolean = {
    def isBracketingValid(bracketList: List[Char], bracketStack: Stack[Char]): Boolean = bracketList match {
        case Nil => bracketStack == Empty
        case currentBracket :: rest => {
            lazy val previousBracket = bracketStack.pop._1.getOrElse('$')

            lazy val isRestValid = isBracketingValid(rest, bracketStack.pop._2)

            if (currentBracket == '(' ||
                currentBracket == '[' ||
                currentBracket == '{') isBracketingValid(rest, bracketStack.push(currentBracket))
            else if (currentBracket == ')') previousBracket == '(' && isRestValid
            else if (currentBracket == ']') previousBracket == '[' && isRestValid
            else if (currentBracket == '}') previousBracket == '{' && isRestValid
            else false
        }
    }
    isBracketingValid(bracketString.toList, Empty)
}

编辑

在 Luis 提示使用“this”之后重写了 Stack 定义,因此不会导致上述问题,因为没有传入任何堆栈,但我仍然有兴趣了解原因。

sealed trait Stack[+A] {
    def push[A] (newTop: A): Top[A] = ???

    def pop: (Option[A], Stack[A]) = ???
}

case object Empty extends Stack[Nothing] {
    override def push[A] (newTop: A): Top[A] = Top(newTop, this)

    override def pop: (Option[Nothing], Stack[Nothing]) = (None, Empty)
}

case class Top[A](val top: A, val rest: Stack[A]) extends Stack[A] {
    override def push[A] (newTop: A): Top[A] = Top(newTop, this.asInstanceOf[Stack[A]])

    override def pop: (Option[A], Stack[A]) = (Some(top), rest)
}

编辑 2

由于 Luis 的洞察力,重构了堆栈定义,不使用“asInstanceof”,而是使用较低的类型界限来实现目标。并且还检查了为什么案例分类通常应该是最终的。

sealed trait Stack[+A] {
    def push[B >: A] (newTop: B): Top[B] = ???

    def pop: (Option[A], Stack[A]) = ???
}

case object Empty extends Stack[Nothing] {
    override def push[A] (newTop: A): Top[A] = Top(newTop, this)

    override def pop: (Option[Nothing], Stack[Nothing]) = (None, Empty)
}

final case class Top[+A] (val top: A, val rest: Stack[A]) extends Stack[A] {
    override def push[B >: A] (newTop: B): Top[B] = Top(newTop, this)

    override def pop: (Option[A], Stack[A]) = (Some(top), rest)
}

【问题讨论】:

  • 为什么要将Stack 传递给Stack 方法?
  • 构造一个新的Stack,从而确保调用方法的那个栈保持不变。
  • 我还是不明白。你总是想要this,然后你返回一个新的,它共享这个。因为,您的 Stackimmutable,所以这不是问题。它就像一个普通的 List
  • 感谢使用“this”关键字的提示,现在代码更简单了,如果这样重写,问题当然会消失,但我仍然想了解原因。
  • 你不应该使用asInstanceOf 几乎从不(如果你正在学习,你应该假设它不存在)。你可以像这样重写override def push[B &gt;: A](newTop: B): Stack[B] = Top(newTop, this) (另外,你应该在A中标记你的Top类协变。-final case class Top[+A] ...。 - 现在,问题是,即使您的两个覆盖具有默认值,特征本身的方法也没有它,而这就是您调用的方法。

标签: scala methods default-value


【解决方案1】:

所以,总结一下。问题是,即使在您的两个子类中,您已将 push 方法覆盖为具有默认值。
trait 上的方法签名没有这样的默认值。而且,由于这是您调用的那个,编译器发出了正确的错误。
您可以进行模式匹配,以了解您拥有Stack 的哪个特定case,这样编译器就会找到具有默认值的签名。但是,由于默认值始终是与 this 具有相同形状的 Stack,并且您真正需要的只是使用 this (因为作为不可变集合,可以有结构共享),最好只重写方法。

以下是您的Stack 的实现,它更简洁(恕我直言)

sealed trait Stack[+A] {
  final def push[B >: A](newTop: B): Stack[B] =
    Top(newTop, this)

  final def pop: (Option[A], Stack[A]) = this match {
    case Top(top, rest) => (Some(top), rest)
    case Empty          => (None, Empty)
  }
}

final case class Top[+A](top: A, rest: Stack[A]) extends Stack[A]
case object Empty extends Stack[Nothing]

【讨论】:

  • 再次感谢您的回答和额外的指导。最终版本的 pop 方法中存在一个小错误,顶部需要位于 Some() 类中。
  • @Mahmoud 对,快速复制和粘贴,抱歉。不客气。
猜你喜欢
  • 2021-07-20
  • 2015-03-18
  • 2014-03-25
  • 1970-01-01
  • 2012-09-18
  • 1970-01-01
  • 1970-01-01
  • 2012-08-04
  • 2021-11-18
相关资源
最近更新 更多