2016-07-03 5 views
1

私はScalaで左折の仕組みを理解するのが難しいです。倍の操作が残っていますか?

次のコードは、charsのリスト内のそれぞれの一意の文字に対して、 の回数を計算します。たとえば、呼び出し

times(List('a', 'b', 'a')) 

は(結果のリストの順序は重要ではありません)以下を返す必要があります:

List(('a', 2), ('b', 1)) 


def times(chars: List[Char]): List[(Char, Int)] = { 
    def incr(acc: Map[Char,Int], c: Char) = { 
    val count = (acc get c).getOrElse(0) + 1 
    acc + ((c, count)); 
    } 
    val map = Map[Char, Int]() 
    (map /: chars)(incr).iterator.toList 
} 

私は、この関数の最後の行が実際にあるものにと同じくらい混乱していますやっている? 助けを借りて助けてください。おかげさまで

+1

'foldLeft'、' /: 'について、あるいはその両方についてご質問ですか? – pedrofurla

答えて

1

私は、より詳細な方法であなたの関数を書き直し:

(Map(), 'a') => Map() ++ Map(`a` -> 1) 

2番目の呼び出し::

(Map(`a` -> 1), `b`) => Map('a' -> 1) ++ Map('b' ->1) 

def times(chars: List[Char]): List[(Char, Int)] = { 
    chars 
    .foldLeft(Map[Char, Int]()){ (acc, c) => 
     acc + ((c, acc.getOrElse(c, 0) + 1)) 
    } 
    .toList 
} 

はのはtimes("aba".toList)

最初の呼び出しで最初のステップを見てみましょう

第三呼び出し:

(Map('a' -> 1, 'b' ->1), 'a') => 
     Map('a' -> 1, 'b' ->1) ++ Map('a' -> 2) => 
     Map('a' -> 2, 'b' ->1) 
+1

折り畳みに 'case'を使う必要はありません。パターンマッチはありません。私は少しそれが誤解を招くことがわかります。 – pedrofurla

+0

@pedrofurlaタプルを解体し、 'a._1、a._2'を書かないことです。 Scalaの将来のバージョンでは、 'case'は実際には必要ありません。 – ipoteka

+0

'acc'はMap [Chat、Int]であり、cはCharです。ここでタプルを解体しているわけではありません。ケースなしで試してみると、それだけで動作することがわかります。 – pedrofurla

1

Scalaのコードベースでの実際の実装は非常に簡潔である:

def foldLeft[B](initialValue: B)(f: (B, A) => B): B = { 
    //Notice that both accumulator and collectionCopy are `var`s! They are reassigned each time in the loop. 
    var accumulator = initialValue 
    //create a copy of the collection 
    var collectionCopy = this //the function is inside a collection class, so **this** is the collection 
    while (!collectionCopy.isEmpty) { 
     accumulator = f(accumulator , collection.head) 
     collectionCopy = these.tail 
    } 
    accumulator 
    } 

編集コメントの後:

def foldLeft[B](z: B)(f: (B, A) => B): B = { 
    var acc = z 
    var these = this 
    while (!these.isEmpty) { 
     acc = f(acc, these.head) 
     these = these.tail 
    } 
    acc 
    } 

は、私は明確にするためのものの名前を変更してみましょう

OPの機能を再訪して、それを命令的な方法で書き直してみましょう。 e。どうやら混乱の源である、非機能): (map /: chars)(incr)は命令的のように書き換えることができchars.foldLeft(map)(incr)、とまったく同じことです:

def foldLeft(initialValue: Map[Char,Int])(incrFunction: (Map[Char,Int], Char) => Map[Char,Int]): Map[Char,Int] = { 
     //Notice that both accumulator and charList are `var`s! They are reassigned each time in the loop. 
     var accumulator = initialValue 
     //create a copy of the collection 
     var charList: List[Char] = this //the function is inside a collection class, so **this** is the collection 
     while (!charList.isEmpty) { 
      accumulator = incrFunction(accumulator , collection.head) 
      charList = these.tail 
     } 
     accumulator 
     } 

私はこれがfoldLeftの概念が明確になります願っています。

これは、本質的に、コレクションをトラバースしてアキュムレータを更新することによっていくらかの値を累積する必須のwhileループに対する抽象化です。アキュムレータは、アキュムレータの前の値とコレクションの現在の項目を取るユーザ提供関数を使用して更新されます。

非常に説明すると、sum、maxなどのようなコレクションのあらゆる種類の集計を計算するのに役立つだろうと考えています。スカラーコレクションは実際にこれらの関数をすべて提供しますが、あなたの質問の詳細に

、私はこれは簡単にGROUPBYを使用して行うことができることを指摘してみましょう:

def times(l: List[Char]) = l.groupBy(c => c).mapValues(_.size).toList 

times(List('a','b','a')) // outputs List[(Char, Int)] = List((b,1), (a,2)) 

.groupBy(c => c)はあなたにMap[Char,List[Char]] = Map(b -> List(b), a -> List(a, a))

を与えるその後、我々は、マップの値をマッピングするために.mapValues(_.size)を使用グループ化されたサブコレクションのサイズ:Map[Char,Int] = Map(b -> 1, a -> 2)

最後に、.toListのキー値タプルのリストにマップを変換して、最終結果を取得します。

最後に、あなたが言ったように出力リストの順序を気にしない場合、出力をMap[Char,Int]として残しておくと、この決定が(リストに変換される代わりに)より良く伝わります。

+0

あなたはOPの質問に答えませんでした。「この関数の最後の行が実際に行っていることについては混乱していますか?何か助けてください」 –

+0

こんにちはPaul、編集に関するあなたの意見は? –

+0

さて、これで問題に答えようとしていますので、それは改善点です。彼らは彼らのために働く答えであると言うために営業にそれを残します –

1

ScalaではfoldLeftは次のように動作します。

あなたはRES = 55を取得します

val nums = List(2, 3, 4, 5, 6, 7, 8, 9, 10) 
val res= nums.foldLeft(0)((m: Int, n: Int) => m + n) 

、あなたが整数のリストがあるとします。

これを視覚化できます。

val res1 = nums.foldLeft(0) { (m: Int, n: Int) => println("m: " + m + " n: " + n); 
m + n } 
m: 0 n: 1 
m: 1 n: 2 
m: 3 n: 3 
m: 6 n: 4 
m: 10 n: 5 
m: 15 n: 6 
m: 21 n: 7 
m: 28 n: 8 
m: 36 n: 9 
m: 45 n: 10 

したがって、foldLeft引数に初期アキュムレータ値を渡す必要があることがわかります。累積された値は 'm'に格納され、次の値は 'n'に格納されます。 最後に、結果としてアキュムレータを取得します。

+0

質問はfoldLeftの説明を求めるだけではなく、関数の最後の行が何をしているのですか?あなたはそれに対処していません。 –

1

について、あなたは求めている「最後の行」から始めるのをしてみましょう:オペレータ/:コード(map /: chars)(incr)は折りたたみ式左chars上で、初期値とし、explainedで順番にextendsTraversableMapトレイトextendsIterableとしては、文字から整数への空のmapのピングであり、accの各中間値にincrを適用し、各要素ccharsの値に適用します。

たとえば、charsList('a', 'b', 'a', 'c')の場合、左の式(map /: chars)(incr)incr(incr(incr(incr(Map[Char, Int](), 'a'), 'b'), 'a'), 'c')になります。 incrが何のためにとして今

、:それは文字cとともに、整数への文字から中間マッピングaccを取り、1整数インクリメントは、マッピングでcに対応します。また、getOrElse(0)は、acccが存在しない場合、増分される整数は0とみなされます)

たとえば、List('a', 'b', 'a', 'c')charsとすると、最終マッピングはtoListでリストに変換したときにはになります。

関連する問題