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、リストを後でいつでも、または単に入力するfoldRightreverse、入力を線形時間にすることができます。

あなたは何をしないでくださいやるISは使いListそれにしてAPPENDを。これは、単に最後に前に行って逆転するよりもはるかに悪いパフォーマンスを与えます。


What you DON'T do is use a List and append to itだからである新しいリストが作成されますか?一方、先頭に追加する操作を使用しても、新しいリストは作成されませんか?
ケビンメレディス

2
@KevinMeredithはい。AppendはO(n)、prependはO(1)です。
ダニエルC.ソブラル

@pgoggijrそうではありません。まず、不変であるため、「変更」はどこにもありません。すべての要素をコピーする必要があるため、トラバーサルが必要です。そのため、最後の要素のコピーを、の代わりに新しい要素を指すようにすることができますNil。第二に、プリペンドにはいかなる種類のコピーもありません。既存のリストを指す要素が作成され、それだけです。
Daniel C. Sobral 2016


22

うーん..これらは私には複雑すぎるようです。提案してもいいですか

def listTestD = (0 to 3).toList

または

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

答えてくれてありがとうございます。しかし、問題は、自明ではない場合にどうするかです。私はコードにコメントを入れて、それらがすべて0〜3のtoListに相当することを説明しました。
アジャイルフォール2009

おっと、ごめんなさい!率直に言って、ListBufferは使用しません。
アレクサンダーアザロフ

5

一般に、varを排除することにより、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)

ほとんどの場合、リストに変換する必要はないでしょう:)

インデックス付きシーケンスには、必要なものがすべて含まれています。

つまり、そのIndexedSeqで作業できるようになります。

scala> list.foldLeft(0)(_+_)
res0: Int = 55

NB VectorもデフォルトのSeq実装になりました。
Connor Doyle 2013年

2

私は常にListを好み、「理解のため」の前に「fold / reduce」を使用します。ただし、ネストされた「フォールド」が必要な場合は、「理解のため」が推奨されます。「fold / reduce / for」を使用してタスクを実行できない場合、再帰は最後の手段です。

あなたの例では、私はやります:

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

私がする前に:

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

注:「_」の順序のため、ここでは「foldLeft(/ :)」の代わりに「foldRight(:\)」を使用しています。StackOverflowExceptionをスローしないバージョンの場合は、代わりに「foldLeft」を使用してください。


18
私は強く同意しません。好みのフォームはラインノイズのように見えます。
マットR

14
私は?私は1999年に最初にHaskellを学び、数年間Scalaに手を出しています。折り畳みは素晴らしいと思いますが、特定の状況で折り畳みを適用するには、句読記号の不可解な文字列を書き込む必要がある場合は、別の方法を検討します。
マットR

11
@マットR:同意する。やり過ぎるようなこともあり、これもその一つです。
ライガイ09/09/17

8
@WalterChang私はそれらすべての絵文字の外観が好きです。ちょっと待って、そのコードですか?:P
デビッドJ.

4
((0 to 3) :\ List[Int]())(_ :: _)emoticode を呼び出すことは公正ですか?
David 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
変更のほとんどは、内部での実装方法と、投影などの高度な変更です。リストの作成方法には影響しません。
マーカスダウニング

わかりました。また、collection.jclパッケージのクラスを使用した場合にも影響を受けます。
アンドレ・ラズロ

1

新しいscala開発者として、上記の推奨される方法でリスト作成時間をチェックするための小さなテストを書きました。(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.