4

XML の一部を置き換えようとしていますが、途中でアキュムレータが必要です。次のような XML として保存された穴埋め問題があるとします。

val q = <text>The capitals of Bolivia are <blank/> and <blank/>.</text>

ある時点で、これらの空白を HTML 入力要素に変換したいと思うでしょう。最初と 2 番目を区別してチェックできるようにする必要があります。(この場合、2 つの大文字がどちらの順序でも表示される可能性があるという事実は無視してください。これは、後で扱う頭痛の種です。)

StackOverflow に関する素敵な回答のおかげで、次のソリューションを作成しました。

import scala.xml._
import scala.xml.transform._

class BlankReplacer extends BasicTransformer {
  var i = 0

  override def transform(n: Node): NodeSeq = n match {
    case <blank/> => {
      i += 1
      <input name={ "blank.%d".format(i) }/>
    }
    case elem: Elem => elem.copy(child=elem.child.flatMap(transform _))
    case _ => n
  }
}

これはかなりうまく機能します。new BlankReplacer()再番号付けを開始するたびに を作成する必要がありますが、ほとんど機能します。

scala> new BlankReplacer()(q)
res6: scala.xml.Node = <text>The capitals of Bolivia are <input name="blank.1"></input> and <input name="blank.2"></input>.</text>

これが質問です。を置き換えるたびに行わなければならない突然変異を避ける簡単な方法はあり<blank/>ますか? 私が持っているものは恐ろしいものではありませんがBlankReplacer、質問を HTML に変換する必要があるたびにクラスの新しいインスタンスを作成していなければ、これはよりきれいになると思います。これをアキュムレータに変換する方法があると確信していますが、その方法がわかりません。

ありがとう!トッド

4

2 に答える 2

4

これはまさに、Anti-XMLのジッパーが解決するように設計されている種類の問題です。

つまり、XML ツリーから開始し、セレクターを使用してそのツリーにドリルダウンし、いくつかの変更 (この場合は新しい属性) を加えてその結果セットの新しいバージョンを派生させました。私たちが最初に持っていた木に、私たちが腸の中で行った変更を除いて。これがジッパーの目的です...

あなたの場合、次のようなことができます:

import com.codecommit.antixml._

def replaceBlanks(el: Elem) = {
  var i = 0
  (el \\ "blank").map { _ =>
    i += 1
    <input name={"blank.%d".format(i)}/>.convert
  }.unselect
}

または、この回答varのトリックの使用を避けることができます:

def replaceBlanks(el: Elem) = {
  val blanks = el \\ "blank"

  (0 until blanks.size).foldLeft(blanks) {
    case (z, i) => z.updated(i, z(i).copy(
      name = "input",
      attrs = Attributes("name" -> "blank.%d".format(i + 1)))
    )
  }.unselect
}

これで、メソッドを要素に適用できます (要素を に変換した後com.codecommit.antixml.Elem):

scala> println(replaceBlanks(q.convert))
<text>The capitals of Bolivia are <input name="blank.1"/> and <input name="blank.2"/>.</text>

トリックは、 を使用して を使用\\してツリーを掘り下げることができますが、 を使用して結果の「ノード セット」(実際にはジッパー) を変更し、それらを元のコンテキストに戻すことができるのscala.xmlとは異なります。scala.xmlunselect

于 2012-06-21T16:54:23.557 に答える
1

Scales Xmlはパスの折りたたみを提供し、ツリーを「変更」して蓄積できるようにします...

import scales.utils._
import ScalesUtils._
import scales.xml._
import ScalesXml._

// the xml example
val q = <("text") /( "The capitals of Bolivia are ", <("blank")," and ",<("blank"),".")

// which elems to fold on?
val p = top(q) \\* "blank"

val f = foldPositions(p, p.size){ // size is used as the starting point for the fold
  case (question, path) =>
(question - 1, Replace( <("input") /@ ("name" -> ("blank."+question) )) )
  }

// f is an either, so we assuming its working here, and ._1 is the accumalator, _.2 the Path
val newTree = f.left.get._2.tree

唯一の癖は、逆のドキュメント順序で蓄積されることです。非蓄積バージョンもあります。これにより、一部が破壊的な変換の組み合わせが可能になります (たとえば、サブチャイルドを変更してから別の変換でそれを削除するなど、すべてうまくいきます)。

フォールド自体への入力は、同じツリー内にある限り、パスの Iterable であり、必要に応じてクエリを組み合わせることができます。

Scales で Xml を折り畳む方法の詳細については、こちらを参照してください。

于 2012-06-23T09:34:30.583 に答える