【问题标题】:Scala: understanding parametric polymorphismScala:理解参数多态性
【发布时间】:2014-01-14 14:06:22
【问题描述】:

有什么区别

def drop1[A](l: List[A]) = l.tail

def drop1(l: List[Int]) = l.tail

只要用法看起来像

drop1(List(1,2,3))

?

什么时候应该使用其中一种,为什么?虽然我可以理解第二个示例,但我并不真正理解第一个示例的目的。

【问题讨论】:

    标签: scala types parametric-polymorphism


    【解决方案1】:

    真的很简单。您的第一个示例涉及泛型的概念。

    泛型有一个简单的目标,使某些方法泛型,例如不依赖于类型。

    让我们看看这个简单的例子。假设我想为List 写一个drop1 方法。

    我可以为每一种类型写一个:(缓慢,重复):

    def drop1(l: List[Int]): List[Int] = l.tail // this only works for Int
    
    def drop1(l: List[String]): List[String] = l.tail // this only works for String 
    

    您可以了解如何为每种类型编写上述内容。为了克服这个问题,你有泛型:

    def drop1[A](l: List[A]): List[A] = l.tail // this works for any given type.
    

    这实质上是说:无论列表中包含的类型是什么,都给我尾巴。 我不需要为几乎无限的类型编写数千个drop1 的变体,而只需编写一个。

    现在在 Scala 中,您的实现最好通过以下方式完成:

    implicit class ListOps[A](val l: List[A]) extends AnyVal {
       def drop1: List[A] = l match {
         case head :: tail => tail
         case Nil => Nil
       }
    }
    // you can now have
    List(1, 2, 3).drop1
    

    重命名众所周知的库方法通常也是一个坏主意。 tail 操作是不安全的,drop 是安全的。你造成的只是混乱,因为有一个默认的drop 方法。

    List(1, 2, 3) drop 1
    

    【讨论】:

    • 感谢您的回答。为什么在我的第一个示例中需要第一个 [A]?这只是语法要求吗?
    • 是的,所以编译器可以区分你引入的类型和一些已经定义的 A 类型。为什么会呢?为了防止你犯错,并明确你的意图。
    • 谢谢。事实上,我在阅读本教程时提出了这个问题。
    • 一个更深层次的原因(在某些人看来,“参数多态性”中“参数”一词的来源)称为参数性。具体来说,您的 drop1 专用于 Int 对其可以做什么的保证较少。例如,它可以查看第五个 int,如果它等于 74,它可以删除五个元素而不是一个。参数化的,理论上,对元素一无所知,只能对列表的结构进行操作。当然,Scala 公开了像 .isInstanceOf 这样的 JVM 缺陷来解决这个问题,但从理论上讲,至少参数 drop1 更值得信赖。
    【解决方案2】:

    简而言之——一些操作不依赖于特定类型并且可以被抽象化。数苹果和数橙子本质上是相同的操作。如果您要重用算法,那么将某些类型抽象出来而不是编写会更聪明

    def countOranges(xs: List[Orange]) = { some code } 
    def countApples(xs: List[Apple]) = { the very same code }
    

    【讨论】:

      【解决方案3】:

      我有点晚了,如果你对 Java 中的泛型有一个想法,那么可以在这里进行类比:-

      Java --> 泛型类的对象可以传入方法参数。

      class Test<T> {
          // An object of type T is declared
          T obj;
          Test(T obj) {  this.obj = obj;  }  // constructor
          // some other methods in class 
      }
      
      Test<String> testobj = new Test<String>();
      
      public void function(testobj){ 
      // do something with testobj 
      }
      

      Scala --> 与通用参数函数在 scala 中的工作方式相同。这里,[A] 定义了 scala 中的泛型类型

      def drop1[A](l: List[A]) = l.tail
      

      上述函数的用法:-

      scala>drop1(List(1,2,3))   // output List(2, 3)
      scala>drop1(List(1.0,2.0,3.0)) // output List(2.0, 3.0)
      scala>drop1(List('a','b','c'))  // output List(b, c)
      

      说明:- 只需传递任何类型的列表,它就像一个魅力。语法如下:-

      def function[Type](param:Type):Type = {do something}
      

      【讨论】:

        【解决方案4】:
        //snippet to explain scala parametric polymorphism
        
        object MethodParametricPolymorphism {
          //parametric polymorphism is similar/same as of java generics
          def countOranges(oranges : List[Orange]) = oranges.size;
          def countApples(apples : List[Apple]) = apples.size
          //if algo is same then paramatric polymorphism can be used
          def count[A](items : List[A]) = items.size
        
          val oranges : List[Orange] =  List( new Orange(1.1))
          val apples : List[Apple] =  List( new Apple(2.1), new Apple(2.2))
        
          countOranges(oranges);
          countApples(apples);
          //using polymorphic typed method
          count(oranges);
          count(apples);
        
          case class Orange ( weight: Double)
          case class Apple ( weight: Double)
         }
        

        【讨论】:

          猜你喜欢
          • 2017-07-17
          • 1970-01-01
          • 1970-01-01
          • 2016-03-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多