Scala删除Seq元素遇类型歧义错误:Seq[String(in method...)]是什么?
先来说说你遇到的Seq[String(in method drop_Val_List_In_List)]到底是什么——这其实是命名冲突导致的泛型参数混淆!你在定义drop_Val_List_In_List方法时,写了def drop_Val_List_In_List[String](...),这里的[String]并不是Scala标准库中的scala.String类型,而是你给这个方法声明的一个泛型类型参数的名字。相当于你把泛型参数命名成了String,它和我们常用的字符串类型完全是两回事,这就导致方法的返回值类型被推断成了这个自定义泛型参数的Seq,而实际处理后得到的tmp是标准的Seq[java.lang.String](Scala的String其实是Java的别名),两者类型不匹配,所以抛出了那个错误。
修复代码的第一步:修正泛型参数
把方法的泛型参数改成通用的名称(比如A),避免遮蔽标准类型:
// Method that drops one element in sequence def drop_Val_In_List[A](ls: Seq[A], value: A): Seq[A] = { val index = ls.indexOf(value) if (index < 0) { ls } else if (index == 0) { ls.tail } else { val (a, b) = ls.splitAt(index) a ++ b.tail } } val KeepCols = Seq("id", "type", "month", "car", "road") // Generalization of the above method to drop multiple elements - 修正泛型参数 def drop_Val_List_In_List[A](ls: Seq[A], in_ls: Seq[A]): Seq[A] = { var tmp = ls for(x <- in_ls ){ tmp = drop_Val_List_In_List(tmp, x) } tmp } val tmp = drop_Val_List_In_List(KeepCols, List("id", "type", "month")) // 此时tmp的值就是 Seq("car", "road")
这样修改后,类型混淆的问题就解决了,方法可以正确推断类型,返回符合预期的Seq。
更Scala风格的优化:去掉var,用函数式写法
Scala更推荐使用不变性和函数式操作,我们可以用foldLeft替代var和for循环,代码更简洁且符合函数式编程习惯:
def drop_Val_List_In_List[A](ls: Seq[A], in_ls: Seq[A]): Seq[A] = { in_ls.foldLeft(ls) { (currentSeq, valueToRemove) => drop_Val_In_List(currentSeq, valueToRemove) } }
foldLeft会从初始的ls开始,依次对in_ls中的每个元素调用drop_Val_In_List,把每次处理后的结果作为下一次的输入,最后返回最终的Seq,完全不需要可变变量var。
额外小提示
其实Scala的Seq已经提供了现成的方法,如果你只是想删除包含在另一个Seq中的元素,完全可以不用自己实现这些方法,一行代码就能搞定:
// 方法1:用filterNot配合Set提高查找效率 val tmp = KeepCols.filterNot(List("id", "type", "month").toSet) // 方法2:直接用diff方法求差集 val tmp = KeepCols diff List("id", "type", "month")
这两个方法都能直接得到你想要的Seq("car", "road"),而且效率更高(尤其是filterNot配合toSet,查找元素是O(1)的)。
内容的提问来源于stack exchange,提问作者James Flash

