Scala 목록을 만드는 데 선호되는 방법


117

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

답변:


108

ListBuffer일정 시간 추가 및 일정 시간 변환을 List.

List 불변이며 일정한 시간 앞에 추가되고 선형 시간 추가가 있습니다.

목록을 구성하는 방법은 목록을 사용할 알고리즘과 목록을 생성 할 요소를 가져 오는 순서에 따라 다릅니다.

예를 들어, 사용할 때와 반대 순서로 요소를 가져 오면 a를 사용하고 List앞에 추가 할 수 있습니다 . 꼬리 재귀 함수를 사용하든 foldLeft또는 다른 것을 사용하든 실제로는 관련이 없습니다.

사용하는 것과 동일한 순서로 요소를 가져 ListBuffer오면 성능이 중요하다면 a 가 가장 선호되는 선택입니다.

그러나, 당신은 중요한 경로에없는 및 입력이 충분히 낮은 경우 할 수 있습니다 항상 reverse목록 이상 또는 단지 foldRight또는 reverse선형 시간 입력.

당신은 어떻게 하지 마십시오 할 IS는 사용 List그것과 APPEND를. 이것은 당신에게 단지 앞뒤로 후진하는 것보다 훨씬 더 나쁜 성능을 줄 것입니다.


What you DON'T do is use a List and append to it새 목록 이 생성 되었기 때문 입니까? 반면, 앞에 추가 작업을 사용하면 새 목록이 생성되지 않습니까?
Kevin Meredith

2
@KevinMeredith 네. Append는 O (n), prepend는 O (1)입니다.
Daniel C. Sobral

@pgoggijr 사실이 아닙니다. 첫째, 불변이기 때문에 "변화"는 어디에도 없습니다. 모든 요소를 ​​복사해야하므로 순회가 필요하므로 마지막 요소의 복사본이 대신 새 요소를 가리 키도록 만들 수 있습니다 Nil. 둘째, prepend에는 어떤 종류의 복사본도 없습니다. 기존 목록을 가리키는 요소가 생성됩니다.
Daniel C. Sobral


22

음 .. 이건 너무 복잡해 보입니다. 제안해도 될까요

def listTestD = (0 to 3).toList

또는

def listTestE = for (i <- (0 to 3).toList) yield i

답 해주셔서 감사합니다.하지만 문제는 사소하지 않은 경우에 무엇을 하느냐입니다. 나는 그것들이 0에서 3 toList와 동등하다는 것을 설명하는 코드에 주석을 달았다.
agilefall

죄송합니다. 솔직히 저는 ListBuffer를 사용하지 않습니다.
Alexander Azarov

5

일반적으로 모든 변수를 제거하여 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

NB Vector는 이제 기본 Seq구현 이기도합니다 .
Connor Doyle 2013 년

2

저는 항상 List를 선호하고 "for comprehension"전에 "fold / reduce"를 사용합니다. 그러나 중첩 된 "폴드"가 필요한 경우 "for comprehension"이 선호됩니다. "fold / reduce / for"를 사용하여 작업을 수행 할 수없는 경우 재귀는 마지막 수단입니다.

그래서 당신의 예를 들어, 나는 할 것입니다.

((0 to 3) :\ List[Int]())(_ :: _)

내가하기 전에 :

(for (x <- 0 to 3) yield x).toList

참고 : "_"의 순서 때문에 여기에서 "foldLeft (/ :)"대신 "foldRight (: \)"를 사용합니다. StackOverflowException을 발생시키지 않는 버전의 경우 대신 "foldLeft"를 사용하십시오.


18
나는 매우 동의하지 않습니다. 선호하는 형태는 라인 노이즈처럼 보입니다.
Matt R

14
내가? 저는 1999 년에 처음으로 Haskell을 배웠고 2 년 동안 Scala를 다루었습니다. 나는 접기가 훌륭하다고 생각하지만 주어진 상황에서 접기를 적용하려면 구두점 기호의 비밀스러운 문자열을 작성해야한다면 다른 접근 방식을 고려할 것입니다.
Matt R

11
@Matt R : 동의합니다. 그것을 과장하는 것과 같은 것이 있으며 이것은 그들 중 하나입니다.
ryeguy

8
@WalterChang 나는 그 모든 이모티콘의 모양을 좋아합니다. 잠깐만 요, 그 코드인가요? : P
데이비드 J.

4
((0 to 3) :\ List[Int]())(_ :: _)emoticode 를 호출하는 것이 공정 합니까?
데이비드 J.

2

사용 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)

2

참고 :이 답변은 이전 버전의 Scala를 위해 작성되었습니다.

Scala 컬렉션 클래스는 Scala 2.8부터 재 설계 될 예정이므로 곧 목록을 만드는 방식을 변경할 준비를하십시오.

목록을 만드는 이전 버전과 호환되는 방법은 무엇입니까? 아직 2.8 문서를 읽지 않았기 때문에 전혀 모르겠습니다.

컬렉션 클래스의 제안 된 변경 사항을 설명하는 PDF 문서


2
대부분의 변경 사항은 사물이 내부적으로 구현되는 방식과 프로젝션과 같은 고급 방식에 있습니다. 목록을 만드는 방법은 영향을받지 않습니다.
Marcus Downing

좋습니다. 알아두면 좋습니다. collection.jcl 패키지의 클래스를 사용하는 경우에도 영향을받습니다.
André Laszlo

1

새로운 스칼라 개발자로서 위의 제안 된 방법으로 목록 생성 시간을 확인하기 위해 작은 테스트를 작성했습니다. (for (p <-(0 to x)) yield p) toList 가장 빠른 접근 방식과 같습니다.

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

0

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)

0

문자열 목록을 만들려면 다음을 사용하십시오.

val l = List("is", "am", "are", "if")

1
이 오래된 질문 (10 년)에 기존 답변이 너무 많음 (9)에 답변 할 때 왜 다른 답변과 다른지 설명하는 것이 좋습니다. 현재 질문을 이해하지 못한 것 같습니다.
jwvh
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.