Предпочтительный способ создания списка Scala
Есть несколько способов создать неизменяемый список в Scala (см. Придуманный пример кода ниже). Вы можете использовать изменяемый ListBuffer, создать var
перечислите и измените его, используйте хвостовой рекурсивный метод и, возможно, другие, о которых я не знаю.
Инстинктивно я использую ListBuffer, но у меня нет веских причин для этого. Есть ли предпочтительный или идиоматический метод для создания списка, или есть ситуации, которые являются лучшими для одного метода по сравнению с другим?
import scala.collection.mutable.ListBuffer
// THESE are all the same as: 0 to 3 toList.
def listTestA() ={
var list:List[Int] = Nil
for(i <- 0 to 3)
list = list ::: List(i)
list
}
def listTestB() ={
val list = new ListBuffer[Int]()
for (i <- 0 to 3)
list += i
list.toList
}
def listTestC() ={
def _add(l:List[Int], i:Int):List[Int] = i match {
case 3 => l ::: List(3)
case _ => _add(l ::: List(i), i +1)
}
_add(Nil, 0)
}
9 ответов
ListBuffer
изменяемый список, который имеет постоянное добавление и преобразование в постоянное время в List
,
List
является неизменным и имеет постоянное время и линейное время.
То, как вы создадите свой список, зависит от алгоритма, для которого вы будете использовать список, и от порядка, в котором вы получаете элементы для его создания.
Например, если вы получаете элементы в обратном порядке, когда они будут использоваться, то вы можете просто использовать List
и делает prepends. Будете ли вы делать это с хвостовой рекурсивной функцией, foldLeft
или что-то еще не очень актуально.
Если вы получаете элементы в том же порядке, в котором вы их используете, то ListBuffer
скорее всего предпочтительный выбор, если производительность критична.
Но если вы не находитесь на критическом пути, а вход достаточно низок, вы всегда можете reverse
список позже или просто foldRight
, или же reverse
вход, который является линейным временем.
То, что вы не делаете, это использовать List
и добавить к нему. Это даст вам гораздо худшую производительность, чем просто добавление и изменение в конце.
Хм.. это кажется мне слишком сложным. Могу ли я предложить
def listTestD = (0 to 3).toList
или же
def listTestE = for (i <- (0 to 3).toList) yield i
Вы хотите сосредоточиться на неизменяемости в Scala, вообще устраняя любые переменные. Читаемость по-прежнему важна для вашего собратья так:
Пытаться:
scala> val list = for(i <- 1 to 10) yield i
list: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Вам, вероятно, даже не нужно конвертировать в список в большинстве случаев:)
Индексированный seq будет иметь все необходимое:
То есть теперь вы можете работать с этим IndexedSeq:
scala> list.foldLeft(0)(_+_)
res0: Int = 55
Я всегда предпочитаю Список, и я использую "свернуть / уменьшить" перед "для понимания". Однако "для понимания" предпочтительнее, если требуются вложенные "складки". Рекурсия является последним средством, если я не могу выполнить задачу, используя "сворачивание / уменьшение / для".
поэтому для вашего примера я сделаю:
((0 to 3) :\ List[Int]())(_ :: _)
прежде чем я сделаю:
(for (x <- 0 to 3) yield x).toList
Примечание: здесь я использую "foldRight(:\)" вместо "foldLeft(/:)" из-за порядка "_". Для версии, которая не выдает исключение StackruException, используйте вместо этого "foldLeft".
Примечание. Этот ответ написан для старой версии Scala.
Начиная с версии Scala 2.8 классы коллекции Scala будут переработаны, так что будьте готовы изменить способ создания списков очень скоро.
Каков прямой совместимый способ создания списка? Я понятия не имею, так как я еще не прочитал 2.8 документа.
PDF-документ с описанием предлагаемых изменений классов коллекции
С помощью List.tabulate
, как это,
List.tabulate(3)( x => 2*x )
res: List(0, 2, 4)
List.tabulate(3)( _ => Math.random )
res: List(0.935455779102479, 0.6004888906328091, 0.3425278797788426)
List.tabulate(3)( _ => (Math.random*10).toInt )
res: List(8, 0, 7)
Как новый разработчик scala, я написал небольшой тест, чтобы проверить время создания списка с помощью предложенных выше методов. Похоже, что (для ( p <- (от 0 до x)) получим p), чтобы перечислить самый быстрый подход.
import java.util.Date
object Listbm {
final val listSize = 1048576
final val iterationCounts = 5
def getCurrentTime: BigInt = (new Date) getTime
def createList[T] ( f : Int => T )( size : Int ): T = f ( size )
// returns function time execution
def experiment[T] ( f : Int => T ) ( iterations: Int ) ( size :Int ) : Int = {
val start_time = getCurrentTime
for ( p <- 0 to iterations ) createList ( f ) ( size )
return (getCurrentTime - start_time) toInt
}
def printResult ( f: => Int ) : Unit = println ( "execution time " + f )
def main( args : Array[String] ) {
args(0) match {
case "for" => printResult ( experiment ( x => (for ( p <- ( 0 to x ) ) yield p) toList ) ( iterationCounts ) ( listSize ) )
case "range" => printResult ( experiment ( x => ( 0 to x ) toList ) ( iterationCounts ) ( listSize ) )
case "::" => printResult ( experiment ( x => ((0 to x) :\ List[Int]())(_ :: _) ) ( iterationCounts ) ( listSize ) )
case _ => println ( "please use: for, range or ::\n")
}
}
}
Просто пример, который использует collection.breakOut
scala> val a : List[Int] = (for( x <- 1 to 10 ) yield x * 3)(collection.breakOut)
a: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)
scala> val b : List[Int] = (1 to 10).map(_ * 3)(collection.breakOut)
b: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)
Чтобы создать список строк, используйте следующее:
val l = List("is", "am", "are", "if")