0

私は Scala で遊んでコツをつかもうとしているので、このコード サンプルは単なる学術的なものです。

変更可能なリストを関数に渡して、その関数に作業を実行させようとしています。関数呼び出しの後、更新されたリストを使用できます。

var data: List[Int] = List()

// Call to function to fill a list
data = initList(data)

// Output only 0-100
data.foreach( num => if (num < 100) { println(num) })

def initList(var data: List[Int]) : List[Int] = {

    for ( i <- 0 to 1000 )
    {
        data = i :: data
    }

    data = data.reverse
    data
}

コンパイルされない上記の唯一のコードはvarindef initList()であり、 data が であるためval、関数内で I に変更を加えることはできません。

まず、Scala では、ミューテーターは通常嫌われていることを知っていると言っておきましょう。そのため、質問に対する直接的な回答だけでなく、それを完全に行うためのより良い方法にもオープンです。プロジェクトでは、データの一部があちこちに移動して更新されることがありますが、データを関数に渡して変更できない場合は、どのような代替手段がありますか?

私はチュートリアルを読み、これについてグーグルで検索しましたが、Scalaでは通常このように行われないため、それについて多くを見つけることができないと思います.

4

4 に答える 4

4

最初に認識しておくべき重要なことdataは、はvarですが、リスト自体は不変であるということです。新しいリストをに割り当てることはできdataますが、実際のリスト自体を変更することはできません。これは、リストを関数に渡してリストを変更できない理由でもあります。したがって、実際には、forループは反復ごとに新しいリストを作成しています。

幸いなことに、Scalaを使用すると、リストやその他の不変のデータ構造を作成するための関数型コードを非常に簡単に作成できます。これがあなたが望むことをするための一般的な「機能的な」方法です:

def initList(data: List[Int]) = {
  def initLoop(num: Int, buildList: List[Int]): List[Int] = num match {
    case 0 => 0 :: buildList
    case n => initLoop(n - 1, n :: buildList)
  }
  initLoop(1000, data)
}

基本的に、ここで起こっていることは、forループを末尾再帰関数に置き換えたことです。呼び出しごとに、内部関数は、現在のリストを取得し、0に到達して完成したリストを返すまで、次の番号を付加することによって新しいリストを作成します。関数は1000から始まり、0に戻るため、リストを逆にする必要はありません。

これがどのように機能するかを理解するために、各再帰呼び出しでの内部関数のパラメーターの値を次に示します(ただし、1000ではなく3から始めましょう)。

initLoop(3, data)
initLoop(2, 3 :: data)
initLoop(1, 2 :: 3 :: data)
initLoop(0, 1 :: 2 :: 3 :: data)

したがって、最終的に0になると、戻ります(データが空であると想定)List(0, 1, 2, 3)

この方法は、最後にリストを逆にする必要がないため、実際にはforループを使用するよりも高速です。Scalaは末尾再帰関数を最適化できるため、スタックオーバーフローやメモリ不足エラーについて心配する必要はありません。

リストを作成および変換する方法は他にもたくさんありますが、これも可能です。

val data: List[Int] = List()
(0 to 1000).foldRight(data){ (num, buildList) => num :: buildList}

またはこれだけでも:

(0 to 1000).toList
于 2012-01-21T04:28:12.737 に答える
4

他の回答で提案されているように、機能的/不変のソリューションを優先する必要があります。ただし、この機能が本当に必要な場合 - 参照によって値を渡すには、ML スタイルの変更可能な参照セルを使用できます。

これはあなたがそれらを宣言する方法です:

val cell: Ref[SomeClass] = Ref(value)

これらの値にアクセスする方法は次のとおりです。

!cell

そして、これはあなたがそれらの値を変更する方法です:

cell := !cell + 1
// OR
cell.modify(_ + 1)

簡単な参照セルの実装:

final class Ref[A] private(private var a: A) {
  def :=(newValue: A): Unit = {
    a = newValue
  }

  def unary_! : A = a

  def modify(f: A => A): Unit = {
    a = f(a)
  }
}

object Ref {
  def apply[A](value: A) = new Ref(value)
}

これに多くの便利なメソッドを追加できます。たとえば、整数値のインクリメント、デクリメント。

これは、参照セルを使用して書き直されたコードです (期待どおりに動作します)。

def initList(data: Ref[List[Int]]): Unit = {
  for(i <- 0 to 1000)
    data := i :: !data
  data := (!data).reverse
}

val data = Ref(List.empty[Int])
initList(data)    
for(num <- !data; if num < 100)
  println(num)
于 2012-01-23T12:54:49.263 に答える
2

より良いコードは何ですか

val data = 1000 to 0 by -1

(imo)ではるかに速く読みやすくなります。

于 2012-01-21T04:19:46.980 に答える
2

このようなものはどうですか:

def initList(inData: List[Int]) : List[Int] = {
    var data = inData  // <----The one added line
    for ( i <- 0 to 1000 )
    {
        data = i :: data
    }
    data.reverse
}
于 2012-01-21T04:02:51.180 に答える