嘿,我对函数式编程很陌生,那么编写代码和平的最像scala的方式是什么?
var ANSWERS_ARRAY // Is array
for ((rows, i) <- SOME_2D_ARRAY.view.zipWithIndex)
for ((col, j) <- rows.view.zipWithIndex)
if (col == SOME_VALUE)
// add tuple of (SOME_VALUE, (i,j)) to an ANSWERS_ARRAY
这里我循环遍历2D数组并检查指定的元素是否与条件匹配。如果是的话,我把它放到一个新的阵列。那有一两个班轮吗?因为这段代码很丑陋且效率低下。
答案 0 :(得分:2)
一个简单的解决方案,使用zipWithIndex
和for-comprehension:
val SOME_2D_ARRAY: List[List[Int]] = ???
val SOME_VALUE: Int = ???
val ANSWERS_ARRAY: List[(Int, (Int, Int))] = for {
(rows, i) <- SOME_2D_ARRAY.zipWithIndex
(col, j) <- rows.zipWithIndex
if col == SOME_VALUE
} yield (SOME_VALUE, (i,j))
如果您想实现延迟搜索(如在您的代码段中),那么您可以实现它。请注意,在此版本中,ANSWERS_ARRAY
是一个惰性集合。
val ANSWERS_ARRAY = for {
(rows, i) <- SOME_2D_ARRAY.view.zipWithIndex
(col, j) <- rows.view.zipWithIndex
if col == SOME_VALUE
} yield (SOME_VALUE, (i,j))
答案 1 :(得分:1)
代码的逻辑起初并不是非常实用。
在函数式编程中,您通常会处理不可变数据,在该数据上应用函数来获取新的不可变值。在这里,你试图强制修改answer_array的值。
这样做的惯用方法如下:
val answerArray = my2DArray.zipWithIndex.flatMap {
case (row, i) => row.zipWithIndex.filter{
case (col, j) => col == someValue
}.map {
case (col, j) => (someValue, (i, j))
}
}
这不是非常易读的代码,特别是初学者,所以让我们分解它:
首先我们使用flatMap
遍历我们的2D数组(带索引),它使用一个函数来对每个元素进行操作。此函数必须为每个元素返回另一个数组,并将所有这些数组展平为一个数组(因此flatMap
中的“平面”)。
数组的元素是初始数组的元素对,即一行及其索引,因此我们在这一对上进行模式匹配,以便能够正确定义我们的函数。
现在,对于每一行,我们想做什么?只保留满足条件的元素,所以我们filter
使用谓词(这只是函数返回Boolean
的一个奇特名称),返回只包含满足条件的值的数组
但是,我们不想要这些值,而是对它们进行转换,因此我们将map
应用于数组。 map
与flatMap
相同,只是它的参数不需要返回数组,因为返回值不会被展平(它只是通过以下方式生成第一个数组的元素的图像数组)给定的功能)。
我们想要对剩余元素应用什么功能?好吧,我们只想要三联(someValue, (i, j))
请注意我使用了camelCase,这也是在scala中命名变量的惯用方法(虽然这没有必要)
好的,所以我们有一些很好的代码可以做我们想要的,但它不是很易读。值得庆幸的是,这种语言作为一种很好的语法糖,需要理解才能将flatMap
s,map
和filter
替换为更为迫切的风格。请注意,这只是为了方便编写代码,并且在编译时,代码将被我上面描述的内容(或接近它的内容)所取代。
val answerArray = for {
(row, i) <- 2DArray.zipWithIndex
(col, j) <- row.zipWithIndex
if col == someValue
} yield (someValue, (i, j))
每条<-
行遍历rhs数组,将lhs名称分配给其元素(某些模式匹配作为奖励)
每个if
行代表我们要保留的值的条件
yield
部分是我们想要在最终数组的每个元素中获取的值,具体取决于我们在每个数组中提取的值。