在Scala中迭代元组
在Scala中迭代元组
在Scala中,我们可以按以下方式获得元组的迭代器:\n
val t = (1, 2) val it = t.productIterator
\n甚至可以这样做:\n
it.foreach( x => println(x.isInstanceOf[Int]) )
\n返回true,但在没有使用asInstanceOf[Int]的情况下,我们无法对迭代器的值进行简单操作,因为:\n
it.foreach( x => println(x+1) )
\n会返回错误:类型不匹配;找到:Int(1);需要:String\n我理解Integer与Int之间的问题,但isInstanceOf[Int]的有效性仍然有些令人困惑。\n如何以最佳方式对元组执行这些操作?请注意,元组可以包含整数和双精度浮点数等多种类型,因此转换为列表可能并不总是适用。
在Scala中,有一种类型叫做HList,它是tuple和List的结合体。你可以通过导入shapeless库来使用HList。下面的代码演示了如何将tuple转换为HList,并对HList进行迭代。
import shapeless._ val t = 1 :: 2 :: HNil val lst = t.toList lst.foreach( x => println(x+1) )
但是如果你已经有一个tuple,是否可以直接将其转换为HList,而不需要手动构建HList呢?在stackoverflow上有很多关于HList的问题,你可以参考这些问题来寻找答案。
我在stackoverflow上找到了这个问题的答案:github.com/milessabin/shapeless/blob/master/examples/src/main/…。是的,你可以将tuple转换为HList,反之亦然。
如果你喜欢某个答案,请记得接受它作为解决方案。
迭代Scala元组的问题出现的原因是元组不必是同质的,并且编译器没有尝试在元素之间应用魔术类型统一。以(1,“hello”)作为这样一个异构元组的例子(Tuple2[Int,String])。这意味着x的类型为Any(而不是Int!)。尝试使用原始元组进行it.foreach((x:Int)=> println(x)),以获得更好的错误消息,指示迭代器未统一元组元素的类型(它是一个Iterators[Any])。报告的错误应该类似于:
error: type mismatch;
found : (Int) => Unit
required: (Any) => ?
(1, 2).productIterator.foreach( (x: Int) => println(x) )
在这种特殊情况下,可以使用isInstanceOf[Int]来从类型系统给出的Any中细化类型,因为我们从手动代码检查中知道它将与给定的元组“安全”。这是迭代器/类型涉及的另一个示例:
(1, 2) // -> Tuple2[Int,Int]
.productIterator // -> Iterator[Any]
.map(_.asInstanceOf[Int]) // -> Iterator[Int]
.foreach(x => println(x+1))
虽然我建议将元组视为同质元素的有限集合而不是序列,但可以使用与处理任何Iterator[Any]相同的规则,例如使用模式匹配(例如match)根据实际对象类型进行区分。 (在这种情况下,代码使用了隐式的PartialFunction。)
(1, "hello").productIterator
.foreach {
case s: String => println("string: " + s)
case i: Int => println("int: " + i)
}
虽然在这种情况下,使编译器统一这种情况的类型可能是可能的,但这听起来像是一个特殊情况,需要额外的工作来获得最小的收益。通常,像列表这样的序列-而不是元组-被用于同质元素,并且编译器/类型系统正确地给出了我们对于List(1,2)这样的好的细化(它被类型化为List[Int],如预期的那样)。
谢谢,我喜欢你用模式匹配来处理这个问题的方式。
模式匹配确实是比isInstanceOf更好的样式-这就是为什么后者(故意)有一个如此冗长且丑陋的名称。它是更好的样式,因为它以一种较少容易出错的方式统一了isInstanceOf和asInstanceOf。
元组的productIterator使用each和case进行魔法操作!谢谢