我怎样才能在 Scala 中按习惯地"remove"列表中的单个元素并缩小差距?



列表在Scala中是不可变的,所以我想知道如何"移除"——实际上,创建一个新的集合——这个元素,然后缩小列表中创建的差距。在我看来,这将是一个使用地图的好地方,但我不知道如何在这种情况下开始。

课程是一个字符串列表。我需要这个循环,因为我实际上有几个列表,需要从中删除该索引处的元素(我使用多个列表来存储跨列表关联的数据,我只是通过确保索引始终在列表之间对应来实现这一点)。

for (i <- 0 until courses.length){
if (input == courses(i) {
//I need a map call on each list here to remove that element
//this element is not guaranteed to be at the front or the end of the list
}
}
}

让我补充一下这个问题的细节。我有四个列表,它们通过索引相互关联;一个列表存储课程名称,一个列表以简单的int格式存储课程开始的时间(即130),一个存储"am"或"pm",一个按int存储课程的天数(因此"MWF"evals为1,"TR"evals至2,等等)。我不知道拥有多个——这是解决这个问题的最好还是"正确"的方法,但这些都是我拥有的工具(一年级的compsci学生,从16岁起就没有认真编程)。我正在编写一个函数,从每个列表中删除相应的元素,我只知道1)索引对应,2)用户输入课程名称。如何使用filterNot从每个列表中删除相应的元素?我认为我对每个列表的了解不够,无法在它们上使用更高阶的函数。

这是filter:的用例

scala> List(1,2,3,4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)
scala> res0.filter(_ != 2)
res1: List[Int] = List(1, 3, 4, 5)

在转换列表的所有元素时,您希望使用贴图。

要直接回答您的问题,我认为您正在寻找patch,例如删除索引为2("c")的元素:

List("a","b","c","d").patch(2, Nil, 1)      // List(a, b, d)

其中Nil是我们要替换它的内容,1是要替换的字符数。

但是,如果你这样做:

我有四个列表,它们通过索引相互关联;一列表存储课程名称,其中一个存储开始上课的时间一个简单的int格式(即130),一个存储"am"或"pm",一个按int 存储类的天数

你会过得很糟糕。我建议你使用case class:

case class Course(name: String, time: Int, ampm: String, day: Int)

然后将它们存储在CCD_ 6中。(将时间和天数存储为Ints也不是一个好主意——请查看java.util.Calendar。)

首先要注意几个方面:

  1. List不是一个基于索引的结构。对它的所有面向索引的操作都需要线性时间。对于面向索引的算法,CCD_ 10是更好的候选者。事实上,如果您的算法需要索引,那么这无疑表明您并没有公开Scala的功能。

  2. CCD_ 11用于将项目集合";A";到相同的项目集合";B";使用来自单个"0"的传入变换器函数;A";到单个";B";。它无法更改生成的元素的数量。可能您已将mapfoldreduce混淆。

回答您更新的问题

好吧,这里有一个功能性的解决方案,它可以有效地处理列表:

val (resultCourses, resultTimeList, resultAmOrPmList, resultDateList)
= (courses, timeList, amOrPmList, dateList)
.zipped
.filterNot(_._1 == input)
.unzip4

但有一个陷阱。事实上,我非常惊讶地发现,这个解决方案中使用的函数,对于函数式语言来说是如此基础,却没有出现在标准的Scala库中。Scala对2和3元元组都有它们,但对其他元组没有。

要解决这个问题,您需要导入以下隐式扩展。

implicit class Tuple4Zipped 
[ A, B, C, D ] 
( val t : (Iterable[A], Iterable[B], Iterable[C], Iterable[D]) ) 
extends AnyVal 
{
def zipped 
= t._1.toStream
.zip(t._2).zip(t._3).zip(t._4)
.map{ case (((a, b), c), d) => (a, b, c, d) }
}
implicit class IterableUnzip4
[ A, B, C, D ]
( val ts : Iterable[(A, B, C, D)] )
extends AnyVal
{
def unzip4
= ts.foldRight((List[A](), List[B](), List[C](), List[D]()))(
(a, z) => (a._1 +: z._1, a._2 +: z._2, a._3 +: z._3, a._4 +: z._4)
)
}

这个实现需要Scala2.10,因为它利用了新的有效的Value Classes特性来提升现有的类型。

实际上,我已经将它们包含在一个名为SExt的小型扩展库中,根据您的项目,只需添加一个import sext._语句就可以拥有它们。

当然,如果您愿意,您可以直接将这些函数组合到解决方案中:

val (resultCourses, resultTimeList, resultAmOrPmList, resultDateList)
= courses.toStream
.zip(timeList).zip(amOrPmList).zip(dateList)
.map{ case (((a, b), c), d) => (a, b, c, d) }
.filterNot(_._1 == input)
.foldRight((List[A](), List[B](), List[C](), List[D]()))(
(a, z) => (a._1 +: z._1, a._2 +: z._2, a._3 +: z._3, a._4 +: z._4)
)

删除和筛选列表元素

在Scala中,您可以过滤列表以删除元素。

scala> val courses = List("Artificial Intelligence", "Programming Languages", "Compilers", "Networks", "Databases")
courses: List[java.lang.String] = List(Artificial Intelligence, Programming Languages, Compilers, Networks, Databases)

让我们删除几个类:

courses.filterNot(p => p == "Compilers" || p == "Databases")

您也可以使用remove,但不赞成使用filter或filterNot。

如果要按索引删除,可以使用zipWithIndex将列表中的每个元素与有序索引相关联。因此,courses.zipWithIndex变为:

List[(java.lang.String, Int)] = List((Artificial Intelligence,0), (Programming Languages,1), (Compilers,2), (Networks,3), (Databases,4))

要从中删除第二个元素,您可以参考带有courses.filterNot(_._2 == 1)的元组中的索引,该索引给出了列表:

res8: List[(java.lang.String, Int)] = List((Artificial Intelligence,0), (Compilers,2), (Networks,3), (Databases,4))

最后,另一个工具是使用indexWhere来查找任意元素的索引。

courses.indexWhere(_ contains "Languages") res9: Int = 1

重新更新

我正在编写一个函数,从每个列表,我只知道1)索引对应,2)用户输入课程名称。如何删除相应的元素使用filterNot?

与Nikita的更新类似,您必须"合并"每个列表的元素。因此,课程、子午线、日期和时间需要放入元组或类中,以包含相关元素。然后,您可以对元组的元素或类的字段进行过滤。

将相应的元素组合成一个元组,如下所示:

val courses = List(Artificial Intelligence, Programming Languages, Compilers, Networks, Databases)
val meridiems = List(am, pm, am, pm, am)
val times = List(100, 1200, 0100, 0900, 0800)
val days = List(MWF, TTH, MW, MWF, MTWTHF)

将它们与zip合并:

courses zip days zip times zip meridiems

val zipped = List[(((java.lang.String, java.lang.String), java.lang.String), java.lang.String)] = List((((Artificial Intelligence,MWF),100),am), (((Programming Languages,TTH),1200),pm), (((Compilers,MW),0100),am), (((Networks,MWF),0900),pm), (((Databases,MTWTHF),0800),am))

这种可憎的东西将嵌套的元组压扁为元组。有更好的方法。

zipped.map(x => (x._1._1._1, x._1._1._2, x._1._2, x._2)).toList

一个很好的元组列表。

List[(java.lang.String, java.lang.String, java.lang.String, java.lang.String)] = List((Artificial Intelligence,MWF,100,am), (Programming Languages,TTH,1200,pm), (Compilers,MW,0100,am), (Networks,MWF,0900,pm), (Databases,MTWTHF,0800,am))

最后,我们可以使用filterNot根据课程名称进行筛选。例如filterNot(_._1 == "Networks")

List[(java.lang.String, java.lang.String, java.lang.String, java.lang.String)] = List((Artificial Intelligence,MWF,100,am), (Programming Languages,TTH,1200,pm), (Compilers,MW,0100,am), (Databases,MTWTHF,0800,am))

我要给出的答案可能超出了你迄今为止在课程中所学的内容,所以如果是这样的话,我深表歉意。

首先,你质疑是否应该有四个列表是正确的——从根本上说,听起来你需要的是一个代表课程的对象:

/**
* Represents a course.
* @param name the human-readable descriptor for the course
* @param time the time of day as an integer equivalent to 
*             12 hour time, i.e. 1130
* @param meridiem the half of the day that the time corresponds 
*                 to: either "am" or "pm"
* @param days an encoding of the days of the week the classes runs.
*/
case class Course(name : String, timeOfDay : Int, meridiem : String, days : Int)

您可以定义一个单独的课程

val cs101 = 
Course("CS101 - Introduction to Object-Functional Programming", 
1000, "am", 1)

有更好的方法来定义这种类型(更好地表示12小时的时间,更清晰地表示一周中的几天,等等),但我不会偏离你最初的问题陈述。

考虑到这一点,你会有一个单一的课程列表:

val courses = List(cs101, cs402, bio101, phil101)

如果你想找到并删除所有与给定名称匹配的课程,你可以写:

val courseToRemove = "PHIL101 - Philosophy of Beard Ownership"
courses.filterNot(course => course.name == courseToRemove)

等价地,在Scala中使用下划线语法糖来处理函数文字:

courses.filterNot(_.name == courseToRemove)

如果存在多个课程可能具有相同名称的风险(或者您使用正则表达式或前缀匹配根据某些部分标准进行筛选),并且您只想删除第一个出现的课程,那么您可以定义自己的函数来做到这一点:

def removeFirst(courses : List[Course], courseToRemove : String) : List[Course] =
courses match {
case Nil => Nil
case head :: tail if head == courseToRemove => tail
case head :: tail => head :: removeFirst(tail)
}

使用ListBuffer是一个可变列表,类似于java列表

var l =  scala.collection.mutable.ListBuffer("a","b" ,"c")
print(l) //ListBuffer(a, b, c)
l.remove(0)
print(l) //ListBuffer(b, c)

相关内容

  • 没有找到相关文章

最新更新