问题
In this question, the asker wants to transform documents like this:
<text>
The capitals of Bolivia are <blank/> and <blank/>.
</text>
Into this:
<text>
The capitals of Bolivia are <input name="blank.1"> and <input name="blank.2">.
</text>
As I noted in my answer there, Anti-XML's zippers provide a clean solution to this problem. The following, for example, would work for renaming the blank elements:
import com.codecommit.antixml._
val q = <text>The capitals of Bolivia are <blank/> and <blank/>.</text>.convert
(q \\ "blank").map(_.copy(name = "input")).unselect
Unfortunately the following doesn't work:
(q \\ "blank").zipWithIndex.map { case (el, i) => el.copy(
name = "input",
attrs = Attributes("name" -> "blank.%d".format(i + 1))
)}.unselect
Because of course once we've zipWithIndex
-ed the zipper we no longer have a zipper, just an IndexedSeq
—we can't have a Zipper[(Node, Int)]
because the definition is trait Zipper[+A <: Node] ...
.
Is there a clean way to use zip
or zipWithIndex
on an Anti-XML zipper, do some other operations with map
, etc., and end up with something that's still a zipper?
回答1:
I can't think of a direct way to achieve what you need, but if you're willing to go for a lower level function, you can use a fold
, e.g.:
val blanks = q \\ "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
Note that the zipper is a random access container, so efficiency shouldn't be an issue in this case.
来源:https://stackoverflow.com/questions/11143558/zipping-zippers-in-anti-xml