11

列表在 Scala 中是不可变的,因此我试图弄清楚如何“删除”——实际上,创建一个新集合——该元素,然后缩小列表中创建的差距。这对我来说听起来像是一个使用地图的好地方,但我不知道如何在这种情况下开始。

Courses 是一个字符串列表。我需要这个循环,因为我实际上有几个列表,我需要从中删除该索引处的元素(我正在使用多个列表来存储跨列表关联的数据,并且我通过简单地确保索引将始终跨列表对应)。

  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”评估为 1,“TR”评估为 2,等等)。我不知道拥有多个这是否是解决这个问题的最佳或“正确”方法,但这些都是我拥有的工具(从我 16 岁起就没有认真编程的一年级 comp sci 学生)。我正在编写一个函数来从每个列表中删除相应的元素,我所知道的是 1)索引对应和 2)用户输入课程名称。如何使用 filterNot 从每个列表中删除相应的元素?我不

4

6 回答 6

13

这是用例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)

在转换列表的所有元素时,您想使用 map。

于 2012-10-12T18:10:52.620 回答
13

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

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

whereNil是我们要替换的内容,并且1是要替换的字符数。

但是,如果你这样做:

我有四个通过索引相互关联的列表;一个列表存储课程名称,一个以简单的 int 格式(即 130)存储课程开始的时间,一个存储“am”或“pm”,一个以 int 存储课程的日期

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

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

然后将它们存储在Set[Course]. (将时间和日期存储为Ints 也不是一个好主意 - 请查看java.util.Calendar。)

于 2012-10-12T20:20:45.697 回答
3

首先是一些旁注:

  1. List不是基于索引的结构。对它的所有面向索引的操作都需要线性时间。因为面向索引的算法Vector是一个更好的候选者。事实上,如果您的算法需要索引,这肯定表明您确实没有公开 Scala 的函数功能。

  2. map用于使用传入的转换器函数将项目“A”的集合转换为相同的项目“B”集合,从单个“A”到单个“B”。它不能改变结果元素的数量。可能您对maporfold感到困惑reduce

回答您更新的问题

好的,这是一个功能性解决方案,它在列表上有效:

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)
        )
  }

此实现需要 Scala 2.10,因为它利用新的有效值类功能来拉动现有类型。

实际上,我已经将它们包含在一个名为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)
      )
于 2012-10-12T18:17:27.703 回答
2

删除和过滤列表元素

在 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))

于 2012-10-12T18:11:09.477 回答
0

我即将给出的答案可能超出了您迄今为止在课程中所教的内容,所以如果是这种情况,我深表歉意。

首先,您是否应该有四个列表的问题是正确的 - 从根本上讲,听起来您需要的是一个代表课程的对象:

/**
 * 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)
  }
于 2012-10-13T13:11:03.987 回答
0

使用 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)
于 2013-05-22T14:35:05.533 回答