Как я могу идиоматически "удалить" один элемент из списка в Scala и закрыть разрыв?
списки неизменяемы в 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" evals to 1, "TR" evals to 2 и т. д.). Я не знаю, есть ли это лучший или" правильный " способ решить эту проблему, но это все инструменты, которые у меня есть (студент первого курса comp sci, который не программировал серьезно, так как мне было 16). Я пишу функцию для удаления соответствующего элемента из каждого списка, и все, что я знаю, это то, что 1) индексы соответствуют и 2) пользователь вводит имя курса. Как удалить соответствующий элемент из каждого списка с помощью filterNot? Я не думаю, что знаю достаточно о каждом списке, чтобы использовать функции более высокого порядка их.
6 ответов
чтобы ответить на ваш вопрос прямо, я думаю, что вы ищете patch
, например, для удаления элемента с индексом 2 ("c"):
List("a","b","c","d").patch(2, Nil, 1) // List(a, b, d)
здесь Nil
это то, на что мы его заменяем, и 1
- количество символов для замены.
но, если вы это сделаете:
у меня есть четыре списка, которые связаны друг с другом по индексу; один в списке хранятся имена курсов, в одном - время начала занятий простой формат int (ie 130), один хранит или " am " или "pm" , и одно сохраняет дни занятий по int
у вас будет плохое время. Я предлагаю вам использовать case class
:
case class Course(name: String, time: Int, ampm: String, day: Int)
и затем хранить их в Set[Course]
. (Хранение времени и дней как Int
s тоже не отличная идея - посмотрите на .)
Это вариант использования 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 при преобразовании всех элементов списка.
сначала несколько примечаний:
List
не является индексной структурой. Все индексно-ориентированные операции на нем занимают линейное время. Для индексно-ориентированных алгоритмовVector
гораздо лучший кандидат. Фактически, если ваш алгоритм требует индексов, это верный признак того, что вы действительно не подвергаете функциональные возможности Scala.map
служит для преобразования коллекции элементов "A" в ту же коллекцию элементов " B " с помощью a передано в функции трансформатора от одного " A "до одного"B". Он не может изменить количество результирующих элементов. Вероятно, вы перепуталиmap
Сfold
или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)
)
удаление и фильтрация элементов списка
в 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, но он устарел в пользу фильтра или 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?
подобно обновлению Никиты, вы должны "объединить" элементы каждого списка. Таким образом, курсы, меридиемы, дни и время должны быть помещены в кортеж или класс для хранения связанных элементов. Затем вы можете отфильтровать элемент кортежа или поле класса.
объединение соответствующих элементов в Кортеж выглядит следующим образом с этими данными образца:
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)