【问题标题】:How to fill a ListBuffer[ List[Any] ] (Scala)?如何填充 ListBuffer[ List[Any] ] (Scala)?
【发布时间】:2020-02-16 20:50:14
【问题描述】:

我有一个这样声明的 ListBuffer:

var distances_buffer: ListBuffer[List[Any]] = ListBuffer.empty[List[Any]]

我正在尝试用这样的数据填充它:

for(current <- 0 to training_list_length - 1){
   //A and B are examples
   distances_buffer(current) ++= List[Any](A,B)
}

但是我得到以下错误:

java.lang.IndexOutOfBoundsException: 0

我错过了什么?

编辑!更多信息:

我有一个点及其类别的列表(名称:training_list)。 (x,y,类):

training_list : List[((Double, Double, String))]

我还有一个额外的点,给出了一个 x 和一个 y 值。

我的目标是计算额外点与训练列表中每个点的欧几里得距离,并创建一个如下所示的结果:

//example
List((x: Double, y: Double, class: String), distance: String)

List((4.3,3.0,Iris-setosa), 1.2529964086141665), (4.4,3.0,Iris-setosa), 1.341640786499874)...

如您所见,在列表中我想包括点的坐标(来自 training_list)、点的类别以及距离。

for(current <- 0 to training_list_length - 1){
    val dist = eDistance(test_x, test_y, training_x(current), training_y(current))
    distances_buffer += ListBuffer[Any](training_result(current),dist)
  }

创建此列表后,我想根据距离对其进行排序。也卡在这里了!

【问题讨论】:

  • 我认为你真的想要这个distances_buffer += List(A, B)。您的代码试图在位置currentListBuffer 内的内部List 中添加元素。而我相信您只想将内部 Lists 附加到缓冲区。 - PS:可变性和Any 都是 Scala 上的代码异味,你确定你的建模最适合你的问题吗?
  • 好吧,这成功了!所以要明确一点,这就是++=+= 之间的区别?另外,你能否解释一下 P.S.部分?
  • xs ++= ys 表示将ys 的所有元素(必须是A的某种集合)xs (有成为某种A 集合的构建者)。而xs += x 表示将x (必须是A 的实例) 附加到xs (建造者)。另外问题是您试图通过索引访问空构建器的元素,这不仅是错误的,而且不是预期的用例。 - 我建议您与我们分享您更广泛的问题以及您如何尝试解决它,我很确定有更多惯用的方法来解决它。
  • 您好,请检查编辑!

标签: scala list append indexoutofboundsexception listbuffer


【解决方案1】:

正如 Luis 所建议的,Anyvar 应尽可能避免使用,因此这里有一个示例可能会促使您考虑不同的方法

case class Point(x: Double, y: Double, `class`: String)

def distance(a: Point, b: Point): Double =
  math.hypot(a.x - b.x, a.y - b.y)

val targetPoint = Point(1,2,"Extra-point")
val training_list : List[((Double, Double, String))] = List((4.3,3.0,"Iris-setosa"), (4.4,3.0,"Iris-setosa"))
val points = training_list.map(Point.tupled)
val unsortedPoints: List[(Point, Double)] = points.map(point => (point, distance(point, targetPoint)))
unsortedPoints.sortBy(_._2)

哪个输出

res0: List[(Point, Double)] = List((Point(4.3,3.0,Iris-setosa),3.4481879299133333), (Point(4.4,3.0,Iris-setosa),3.5440090293338704))

我从Xavier 复制了距离计算。

【讨论】:

  • B 是每次在 for 循环中创建的 Double !我想保留 for 结构!如果我没记错的话,如果我将你的代码包含在 for 循环中,我会每次声明一个新的 ListBuffer!
  • 这真的很适合我!我现在完全明白了!非常感谢!
【解决方案2】:

就您命名事物的方式而言,您似乎来自 Python 背景。

我建议您先学习一点 Scala,因为我们的情况非常不同。
不仅在样式方面(例如驼峰式与破折号式),而且在更基本的方面,例如:

  • 一个强大的静态类型系统。因此,Any 之类的内容通常是代码异味,在 99.99% 的情况下完全没有必要。
  • OOP 和 FP 之间的混合。因此,您不必成为 FP 专家,但即使在 Scala 的 OOP 方面,也有一些惯用的东西,例如 不可变性 和常见操作 (高阶函数) 比如mapflatMapfilter & reduce
  • 我们的 ListPython 非常不同,通过索引访问元素是 O(n)(在 Python 中将是O(1))Python 列表更像是一个可以调整大小的 Array
  • 此外,我们实际上并没有for 循环。我们有一个叫for comprehension 的东西,只不过是syntactic sugar for calls to map, flatMap, filter & in some cases foreach

这个列表可以继续,但我认为我的观点很明确。
Scala 不仅是新语法,还是一种不同的编程方式。


无论如何,这是解决您的问题的惯用方法。
(不一定这是最好的方法,可以做很多变化)

// First lets create some custom types / classes to represent your data.
final case class Point(x: Double, y: Double)
final case class ClassifiedPoint(point: Point, clazz: String)

// Lets define the Euclidean distance function.
def euclideanDistance(p1: Point, p2: Point): Double =
  math.sqrt(
    math.pow((p1.x - p2.x), 2) +
    math.pow((p1.y - p2.y), 2)
  )
}

// This is what you need.
// Note that I made it somewhat more generic that is has to be.
// For example, instead of using the euclidean distance function directly on the body,
// we receive the distance function to use.
// Also, I use a technique called currying to split the arguments in different lists,
// This allows the caller to partially apply them.
def computeDistance(distanceFun: (Point, Point) => Double)
                   (trainingList: List[ClassifiedPoint])
                   (referencePoint: Point): List[(ClassifiedPoint, Double)] =
  trainingList.map { classifiedPoint =>
    val distance = distanceFun(classifiedPoint.point, referencePoint)
    classifiedPoint -> distance
  }

你可以这样使用。

val trainingList = List(
  ClassifiedPoint(Point(x = 4.3d, y = 3.0d), clazz = "Iris-setosa"),
  ClassifiedPoint(Point(x = 4.4d, y = 3.0d), clazz = "Iris-setosa")
)

// Partial application to create a new function.
val computeEuclideanDistance = computeDistance(euclideanDistance) _

computeEuclideanDistance(trainingList, Point(x = 3.0d, y = 0.0d))
// res: List[(ClassifiedPoint, Double)] =
//   List(
//     (ClassifiedPoint(Point(4.3, 3.0), "Iris-setosa"), 3.269556544854363),
//     (ClassifiedPoint(Point(4.4, 3.0), "Iris-setosa"), 3.3105890714493698)
//   )

【讨论】:

  • 非常感谢您的详细回答。确实,我需要习惯 Scala 的风格,因为我是新手。给我一些时间来评估和理解你的方式!我会让你知道结果!
猜你喜欢
  • 2019-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-20
  • 2011-09-28
  • 1970-01-01
  • 2018-07-02
相关资源
最近更新 更多