1

以下の Scala クラスは、JDOM を使用してファイルを解析し、ファイルの値を Scala の不変マップに取り込みます。Map は常にゼロであるため、Map で演算子を使用+しても何の効果もないようです。

import java.io.File
import org.jsoup.nodes.Document
import org.jsoup.Jsoup
import org.jsoup.select.Elements
import org.jsoup.nodes.Element
import scala.collection.immutable.TreeMap

class JdkElementDetail() {

  var fileLocation: String = _

  def this(fileLocation: String) = {
      this()
      this.fileLocation = fileLocation;
    }


  def parseFile : Map[String , String] = {

    val jdkElementsMap: Map[String, String] = new TreeMap[String , String];
    val input: File = new File(fileLocation);
    val doc: Document = Jsoup.parse(input, "UTF-8", "http://example.com/");
    val e: Elements = doc.getElementsByAttribute("href");

    val href: java.util.Iterator[Element] = e.iterator();
    while (href.hasNext()) {
      var objectName = href.next();
      var hrefValue = objectName.attr("href");
      var name = objectName.text();

      jdkElementsMap + name -> hrefValue
            println("size is "+jdkElementsMap.size)
    }

    jdkElementsMap
  }

}

println("size is "+jdkElementsMap.size) always prints "size is 0"

マップに正しく追加していないのに、なぜサイズが常にゼロなのですか?

jdkElementsMapこれを aに変換してvarから次を使用する唯一の修正はありますか?

jdkElementsMap += name -> hrefValue

ここでwhileループを削除すると、更新されたオブジェクトになります:

package com.parse

import java.io.File
import org.jsoup.nodes.Document
import org.jsoup.Jsoup
import org.jsoup.select.Elements
import org.jsoup.nodes.Element
import scala.collection.immutable.TreeMap
import scala.collection.JavaConverters._

class JdkElementDetail() {

  var fileLocation: String = _

  def this(fileLocation: String) = {
      this()
      this.fileLocation = fileLocation;
    }


  def parseFile : Map[String , String] = {

    var jdkElementsMap: Map[String, String] = new TreeMap[String , String];
    val input: File = new File(fileLocation);
    val doc: Document = Jsoup.parse(input, "UTF-8", "http://example.com/");
    val elements: Elements = doc.getElementsByAttribute("href");

    val elementsScalaIterator = elements.iterator().asScala

    elementsScalaIterator.foreach {
      keyVal => {
          var hrefValue = keyVal.attr("href");
          var name = keyVal.text();
          println("size is "+jdkElementsMap.size)
          jdkElementsMap += name -> hrefValue
       }
    }
    jdkElementsMap
  }

}
4

2 に答える 2

7

リストであろうとマップであろうと、不変のデータ構造はまさに不変です。それらを変更することはありません。古いデータ構造への変更に基づいて新しいデータ構造を作成します。

を実行するとval x = jdkElementsMap + (name -> hrefValue)、 に新しいマップが表示されますが、 はxそのままjdkElementsMapです。

に変更jdkElementsMapする場合は、または単にvarを行うことができます。後者は変更可能なマップでも機能しますjdkEleemntsMap = jdkElementsMap + (name -> hrefValue)jdkElementsMap += (name -> hrefValue)

それが唯一の方法ですか?whileいいえ、しかし同じことを達成するには、ループを手放す必要があります。これらの行を置き換えることができます:

val href: java.util.Iterator[Element] = e.iterator();
while (href.hasNext()) {
  var objectName = href.next();
  var hrefValue = objectName.attr("href");
  var name = objectName.text();

  jdkElementsMap + name -> hrefValue
        println("size is "+jdkElementsMap.size)
}

jdkElementsMap

次のような折り目があります。

import scala.collection.JavaConverters.asScalaIteratorConverter

e.iterator().asScala.foldLeft(jdkElementsMap) {
  case (accumulator, href) =>  // href here is not an iterator
    val objectName = href
    val hrefValue = objectName.attr("href")
    val name = objectName.text()

    val newAccumulator = accumulator + (name -> hrefValue)

    println("size is "+newAccumulator.size)

    newAccumulator
}

または再帰で:

def createMap(hrefIterator: java.util.Iterator[Element],
              jdkElementsMap: Map[String, String]): Map[String, String] = {
  if (hrefIterator.hasNext()) {
    val objectName = hrefIterator.next()
    val hrefValue = objectName.attr("href")
    val name = objectName.text()

    val newMap = jdkElementsMap + name -> hrefValue

    println("size is "+newMap.size)

    createMap(hrefIterator, newMap)
  } else {
     jdkElementsMap
  }
}

createMap(e.iterator(), new TreeMap[String, String])

パフォーマンスに関しては、折り畳みはかなり遅くなり、再帰はわずかに速くなるはずです。

注意してほしいのは、Scala は変更可能なマップを提供しており、それらがあると言うことができるということだけではありません。それらがあなたの問題により適している場合は、先に進んでそれらを使用してください! 不変のものの使用方法を学びたい場合は、上記の 2 つのアプローチを学ぶ必要があります。

于 2013-01-30T23:33:06.927 に答える
3

マップは不変であるため、変更すると変更されたマップが返されます。jdkElementsMap + (name -> hrefValue)新しいペアを含む新しいマップを返しますが、作成後に変更されたマップを破棄しています。

編集: Java イテラブルを Scala イテラブルに変換できるように見えるので、結果のシーケンスを折りたたんでマップを蓄積できます。

import scala.collection.JavaConverters._
val e: Elements = doc.getElementsByAttribute("href");
val jdkElementsMap = e.asScala
    .foldLeft(new TreeMap[String , String])((map, href) => map + (href.text() -> href.attr("href"))

作成するマップの種類を気にしない場合は、次を使用できますtoMap

val jdkElementsMap = e.asScala
    .map(href => (href.text(), href.attr("href")))
    .toMap
于 2013-01-30T22:52:16.260 に答える