Scala:泛型和隐式

Posted

技术标签:

【中文标题】Scala:泛型和隐式【英文标题】:Scala: generics and implicit 【发布时间】:2021-08-25 17:35:55 【问题描述】:

我必须开发类 StackMachine[T]。如果 T = Boolean,那么应该有逻辑运算。如果 T = Int,Double,Long 等,应该有算术运算。首先我开发了 Stack[T] 类。

class Stack[T](val stack: List[T]) 
    val length: Int = stack.length
    def isEmpty: Boolean = length == 0
    def push(x: T): Stack[T] = 
      new Stack[T](x :: stack)
    

     def peak: T = 
       if (this.isEmpty)
         throw new ArrayIndexOutOfBoundsException
         else stack.head
     
    def pop(): Stack[T] = 
      if (this.isEmpty)
        throw new ArrayStoreException()
      val x :: xs = stack
      new Stack[T](xs)
    

薄是我不知道如何开发 StackMachine[T] 取决于类型的操作的存在。 我试过这个:

case class StackMachine[T](val stack:Stack[T])
    def const(x: T): StackMachine[T] = new StackMachine[T](new Stack[T](this.stack.push(x).stack))
    def dup: StackMachine[T] = new StackMachine[T](new Stack[T](this.stack.push(this.stack.peak).stack))
    def swap: StackMachine[T] = 
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      val secondPeak = secondStack.peak
      val finalStack = secondStack.pop().push(startPeak)
      StackMachine[T](stack)
    
    def and(): StackMachine[Boolean] = 
      val startStack = this.stack.asInstanceOf[Stack[Boolean]]
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      val secondPeak = secondStack.peak
      StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak && secondPeak).stack))

    

    def or: StackMachine[Boolean] = 
      val startStack = this.stack.asInstanceOf[Stack[Boolean]]
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      val secondPeak = secondStack.pop().peak
      StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak || secondPeak).stack))
    

    def xor: StackMachine[Boolean] = 
      val startStack = this.stack.asInstanceOf[Stack[Boolean]]
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      val secondPeak = secondStack.pop().peak
      StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak ^ secondPeak).stack))
    

    def sum(input : T)(implicit N: Numeric[T])  = 
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.plus(startPeak,input)).stack))
    

    def dif(input : T)(implicit N: Numeric[T])  = 
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.minus(startPeak,input)).stack))
    

    def mul(input : T)(implicit N: Numeric[T])  = 
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.toDouble(startPeak).*(N.toDouble(input)).asInstanceOf[T]).stack))
    

    def div(input : T)(implicit N: Numeric[T])  = 
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.toDouble(startPeak)./(N.toDouble(input)).asInstanceOf[T]).stack))
    

    def min(input : T)(implicit N: Numeric[T])  = 
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.min(startPeak,input)).stack))
    
    def max(input : T)(implicit N: Numeric[T])  = 
      val startStack = this.stack
      val startPeak = startStack.peak
      val secondStack = startStack.pop()
      StackMachine[T](new Stack[T](secondStack.push(N.max(startPeak,input)).stack))
    



  

但这是错误的,因为操作不应该有输入参数,因为所有变量都必须从堆栈中获取。不仅如此,这样我就无法创建 diff 和 mul 函数。 我想使 StackMachine[T] 抽象并使用隐式对象,但失败了,因为在这种情况下,我的函数不能返回 StackMachine。可能是我对隐式理解不够好还是有另一种方法?

【问题讨论】:

您可以使用def and()(implicit ev: T <:< Boolean): StackMachine[Boolean],对于数字运算,您可以查看Numeirc typeclass - Thought。说实话。我想我会做的是为不同的机器设置不同的类。 我也会这样做,但这是任务。说实话,我不明白这种结构有什么帮助,因为 ev 没有布尔运算 ev 暗示 TBoolean 因此,您应该能够将 T 用作 Boolean 在该方法的主体中。但是,如果这是预期的解决方案,您应该已经知道了。 - 你能分享一下你的任务的字面意思吗? (也许你不能因为抄袭)。如果我们能知道您应该在此处使用哪个主题/技术(如果这是家庭作业) 或者您要解决的元问题是什么(如果这是工作). 好的,但是我有一个类型不匹配,因为在StackMachine[Boolean](new Stack[Boolean](secondStack.push(startPeak && secondPeak).stack)) secondStack.push 需要 T 但找到布尔值。 任务正文:在实验室工作过程中,需要开发一个广义类(类变体见附表)。该任务的一个特殊特征是对正在开发的类的对象的某些操作的含义取决于该类被参数化的类型。此外,即使某些操作的存在也可能取决于典型参数。此功能是使用隐式对象和隐式方法参数实现的。 【参考方案1】:

是的,看来该项目打算使用typeclass 来解决。

例如,查看这个 Boolean-like and 的小号:

sealed trait BehavesAsBoolean[T] 
  def and(t1: T, t2: T): T


object BehavesAsBoolean 
  implicit final val BooleanBehavesAsBoolean: BehavesAsBoolean[Boolean] =
    new BehavesAsBoolean[Boolean] 
      override def and(b1: Boolean, b2: Boolean): Boolean =
        b1 && b2
    


final class StackMachine[T](stack: Stack[T]) 
  def and(implicit ev: BehavesAsBoolean[T]): Option[StackMachine[T]] =
    for 
      // I changed the implementation of pop to return an Option[(T, Stack[T])]
      (b1, s2) <- stack.pop
      (b2, s3) <- s2.pop
     yield 
      new StackMachine(s3.push(ev.and(b1, b2)))
    

当然,您可能仍然更喜欢抛出异常而不是使用Option 无论如何,我希望这可以帮助您完成代码。


可以看到运行here的代码

【讨论】:

感谢您的帮助

以上是关于Scala:泛型和隐式的主要内容,如果未能解决你的问题,请参考以下文章

15Scala隐式转换和隐式参数

Scala 系列(十三)—— 隐式转换和隐式参数

Scala 学习笔记之隐式参数和隐式转换并用

Scala和隐式导入?

使用泛型类和隐式强制转换修复枚举限制

Scala函数编程和隐式转换