Scala:以理解方式迭代数组的索引,但确保结果类型与数组类型相同
Scala: Iterate an Array's indices in for-comprehension but ensure result type is same Array type
我知道在 for-comprehension 中找到的第一个序列的序列类型定义了推导式的输出类型。但是我需要在不牺牲理解语法的使用的情况下解决这个问题。
假设我有一些名为 v
的 Array[Double]
和一些名为 condition
的复杂谓词函数,它基于 索引 v
,如果 condition(i)
的计算结果为真,那么我想保留 v
的元素。它类似于使用 filter
,除了过滤发生在索引上,而不是 v
.
的值上
我想通过对索引的理解来有效地表达这一点,但我希望结果的类型是 Array[Double]
而不是 Array.indices
的类型。
这是一个最小的示例,其中 condition
只是一个计算索引是否为偶数的玩具示例。实际用例涉及检查二维数组中的索引,其中某些图像梯度条件为真,但想法与这个简单示例相同。
scala> val v = Array[Double](8.0, 9.0, 10.0, 11.0, 12.0)
v: Array[Double] = Array(8.0, 9.0, 10.0, 11.0, 12.0)
scala> def condition(x: Int): Boolean = {x % 2 == 0} // but could be complex
condition: (x: Int)Boolean
scala> for (i <- v.indices if condition(i)) yield v(i)
res136: scala.collection.immutable.IndexedSeq[Double] = Vector(8.0, 10.0, 12.0)
for-comprehension 的输出是 scala.collection.immutable.IndexedSeq[Double]
类型,但我正在寻找如何确保它只是 Array[Double]
,而不需要显式类型转换。
基本上,如果我从 foo.indices
迭代,并且生成的项目来自 foo(i)
,我正在寻找一种方法来自动使用 foo
的类型作为结果,所以容器类型与迭代的任何类型的索引相匹配。
Scala 中正确的习语是什么,用于确保需要专门针对索引范围的 for-comprehension 的结果类型,但结果的容器类型应与索引对象的容器类型相匹配-正在使用而不是索引序列本身的类型(这对理解来说无关紧要)?
使用zipWithIndex
。在Array
的情况下,它会产生Array[(Double, Int)]
,而不是一些奇怪的IndexedSeq
:
for ((a, i) <- v.zipWithIndex if condition(i)) yield a
使用breakOut
import collection.breakOut
val res: Array[Double] = (
for (i <- v.indices if condition(i)) yield v(i)
)(breakOut)
最后用toArray
转换一下结果,以后有需要的时候再优化一下。
另请注意,通常可以为所有具有 Traverse 类型类的集合定义 zipWithIndex
。在假设有 Traverse[F]
可用的情况下,将 F[A]
转换为 F[(A, Int)]
是一种通用方法。
我知道在 for-comprehension 中找到的第一个序列的序列类型定义了推导式的输出类型。但是我需要在不牺牲理解语法的使用的情况下解决这个问题。
假设我有一些名为 v
的 Array[Double]
和一些名为 condition
的复杂谓词函数,它基于 索引 v
,如果 condition(i)
的计算结果为真,那么我想保留 v
的元素。它类似于使用 filter
,除了过滤发生在索引上,而不是 v
.
我想通过对索引的理解来有效地表达这一点,但我希望结果的类型是 Array[Double]
而不是 Array.indices
的类型。
这是一个最小的示例,其中 condition
只是一个计算索引是否为偶数的玩具示例。实际用例涉及检查二维数组中的索引,其中某些图像梯度条件为真,但想法与这个简单示例相同。
scala> val v = Array[Double](8.0, 9.0, 10.0, 11.0, 12.0)
v: Array[Double] = Array(8.0, 9.0, 10.0, 11.0, 12.0)
scala> def condition(x: Int): Boolean = {x % 2 == 0} // but could be complex
condition: (x: Int)Boolean
scala> for (i <- v.indices if condition(i)) yield v(i)
res136: scala.collection.immutable.IndexedSeq[Double] = Vector(8.0, 10.0, 12.0)
for-comprehension 的输出是 scala.collection.immutable.IndexedSeq[Double]
类型,但我正在寻找如何确保它只是 Array[Double]
,而不需要显式类型转换。
基本上,如果我从 foo.indices
迭代,并且生成的项目来自 foo(i)
,我正在寻找一种方法来自动使用 foo
的类型作为结果,所以容器类型与迭代的任何类型的索引相匹配。
Scala 中正确的习语是什么,用于确保需要专门针对索引范围的 for-comprehension 的结果类型,但结果的容器类型应与索引对象的容器类型相匹配-正在使用而不是索引序列本身的类型(这对理解来说无关紧要)?
使用
zipWithIndex
。在Array
的情况下,它会产生Array[(Double, Int)]
,而不是一些奇怪的IndexedSeq
:for ((a, i) <- v.zipWithIndex if condition(i)) yield a
使用
breakOut
import collection.breakOut val res: Array[Double] = ( for (i <- v.indices if condition(i)) yield v(i) )(breakOut)
最后用
toArray
转换一下结果,以后有需要的时候再优化一下。另请注意,通常可以为所有具有 Traverse 类型类的集合定义
zipWithIndex
。在假设有Traverse[F]
可用的情况下,将F[A]
转换为F[(A, Int)]
是一种通用方法。