- けい
- 常連さん
- 会議室デビュー日: 2001/09/12
- 投稿数: 48
|
投稿日時: 2003-08-06 00:42
XMLで与えられる、
(5,1,4,6,1,5,7,5)
と言うデータを、同じくXMLの
((1,2),(2,0),(3,0),(4,1),(5,3),(6,1),(7,1)) //1が2個、2が0個...
と言うデータに変換したいのですが、よい方法がありませんでしょうか?
#min,maxはここのログから得られたのですが...
|
- ocean
- ベテラン
- 会議室デビュー日: 2003/07/06
- 投稿数: 65
|
投稿日時: 2003-08-06 13:38
あまりXSLTには詳しくないので、もっとエレガントな方法があるかもしれませんが・・・
純正のXSLTだと、再帰を使う方法しか思いつきませんでした。count()で線形探索をしているので、項目が増えるとかなり遅くなります。できればスクリプトなどで処理した方が速いと思います。
///////////////////////////////////
// XML
コード: |
|
<?xml version="1.0" encoding="Shift_JIS"?>
<root>
<item value="1"/>
<item value="4"/>
<item value="6"/>
<item value="1"/>
<item value="5"/>
<item value="7"/>
<item value="5"/>
</root>
|
/////////////////////////////////////
// XSLT
コード: |
|
<?xml version="1.0" encoding="Shift_JIS"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<root>
<xsl:apply-templates select="root"/>
</root>
</xsl:template>
<xsl:template match="root">
<xsl:if test="count(item)">
<xsl:call-template name="func1">
<xsl:with-param name="min" select="item[1]/@value"/>
<xsl:with-param name="max" select="item[1]/@value"/>
<xsl:with-param name="pos" select="1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- 最大値、最小値を取得して、それを引数に func2 を呼ぶ -->
<xsl:template name="func1">
<xsl:param name="min"/>
<xsl:param name="max"/>
<xsl:param name="pos"/>
<xsl:choose>
<xsl:when test="$pos <= count(item)">
<xsl:variable name="val" select="item[$pos]/@value"/>
<xsl:variable name="new_min">
<xsl:choose>
<xsl:when test="$min < $val">
<xsl:value-of select="$min"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$val"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="new_max">
<xsl:choose>
<xsl:when test="$max > $val">
<xsl:value-of select="$max"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$val"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="func1">
<xsl:with-param name="min" select="$new_min"/>
<xsl:with-param name="max" select="$new_max"/>
<xsl:with-param name="pos" select="$pos + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="func2">
<xsl:with-param name="val" select="$min"/>
<xsl:with-param name="max" select="$max"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- 渡した値$valの個数を表示し、$val+1を引数に func2 を呼ぶ -->
<!-- $val > $max のとき終了する -->
<!-- 線形探索のため、非常に遅い -->
<xsl:template name="func2">
<xsl:param name="val"/>
<xsl:param name="max"/>
<xsl:if test="$val <= $max">
<item value="{$val}" count="{count(item[@value=$val])}"/>
<xsl:call-template name="func2">
<xsl:with-param name="val" select="$val + 1"/>
<xsl:with-param name="max" select="$max"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
|
[ メッセージ編集済み 編集者: ocean 編集日時 2003-08-06 15:33 ]
|
- MMX
- ぬし
- 会議室デビュー日: 2001/10/26
- 投稿数: 861
|
投稿日時: 2003-08-06 14:01
数列の発生に、ダミーの定数を使う例です
<xsl:variable name="OneToSeven">
<n/><n/><n/><n/><n/><n/><n/>
</xsl:variable>
<xsl:for-each select="$OneToSeven">
<xsl:variable name="Num" select="position()" />
<xsl:value-of select="concat('(',$Num,',',count(itemNo[.=$Num]),')')" />
雰囲気はこんな感じです、XSLTによっては node-set関数を使ったり
N要素をM要素でくくらないと動きません。
XSLT2.0 が使える場合はもっと簡単に書けるとおもいます。
処理速度は全然考えていません。
「仕分け集計」とか一般データ処理はXSLT初期の設計目標に入っていませんから。
XSLT1.0 には配列もハッシュもありません、DOM木があるだけですし
内容テキストを切り刻む、便利な文字列処理もありません。
本格的データ処理を含んだものは XQuery などの方向と思います。
XSLT Performance in .NET
http://www.oreillynet.com/pub/a/dotnet/2003/07/14/xsltperf.html
改造版のXSLTでの性能表
http://www.xml.com/pub/a/2003/08/06/exslt.html?page=3
ネットワークゲートウェイがXML処理の新分野を切り開く
http://www.atmarkit.co.jp/news/200306/12/xml.html
XSLT最適化の道はこれからまだまだです。
[ メッセージ編集済み 編集者: MMX 編集日時 2003-08-08 09:43 ]
[ メッセージ編集済み 編集者: MMX 編集日時 2003-08-08 10:04 ]
|
- ocean
- ベテラン
- 会議室デビュー日: 2003/07/06
- 投稿数: 65
|
投稿日時: 2003-08-07 15:27
面白いですね、これ。
引用: |
|
XSLTによっては node-set関数を使ったり
N要素をM要素でくくらないと動きません。
|
環境依存が許されるならと、MSXML4を前提に書き直してみました。
msxsl:node-set()を使います。前に投稿したコードよりはかなり速いですが、
やはりエレガントさとはほど遠いです。XSLT2.0に期待です。
コード: |
|
<?xml version="1.0" encoding="Shift_JIS"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="/">
<root>
<xsl:apply-templates select="root"/>
</root>
</xsl:template>
<xsl:template match="root">
<xsl:call-template name="count">
<xsl:with-param name="list" select="item/@value"/>
</xsl:call-template>
</xsl:template>
<!--
///////////////////////////////////////////////////
// パブリック
-->
<!--
集計する
-->
<xsl:template name="count">
<xsl:param name="list"/>
<xsl:variable name="sorted-list-tree">
<xsl:for-each select="msxsl:node-set($list)">
<xsl:sort select="." data-type="number" order="ascending"/>
<dummy><xsl:value-of select="."/></dummy>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="sorted-list" select="msxsl:node-set($sorted-list-tree)/dummy"/>
<xsl:variable name="max-position" select="count(msxsl:node-set($sorted-list))"/>
<xsl:if test="$max-position">
<xsl:call-template name="count-impl">
<xsl:with-param name="sorted-list" select="$sorted-list"/>
<xsl:with-param name="cur-position" select="1"/>
<xsl:with-param name="max-position" select="$max-position"/>
<xsl:with-param name="cur-value" select="msxsl:node-set($sorted-list)[1]"/>
<xsl:with-param name="max-value" select="msxsl:node-set($sorted-list)[$max-position]"/>
<xsl:with-param name="count" select="0"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!--
集計結果を表示する
-->
<xsl:template name="print">
<xsl:param name="value"/>
<xsl:param name="count"/>
<item value="{$value}" count="{$count}"/>
</xsl:template>
<!--
///////////////////////////////////////////////////
// プライベート
-->
<!--
集計の下請け
-->
<xsl:template name="count-impl">
<xsl:param name="sorted-list"/>
<xsl:param name="cur-position"/>
<xsl:param name="max-position"/>
<xsl:param name="cur-value"/>
<xsl:param name="max-value"/>
<xsl:param name="count"/>
<xsl:if test="$cur-value <= $max-value">
<xsl:choose>
<xsl:when test="$max-position < $cur-position">
<xsl:call-template name="print">
<xsl:with-param name="value" select="$cur-value"/>
<xsl:with-param name="count" select="$count"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="msxsl:node-set($sorted-list)[$cur-position] = $cur-value">
<xsl:call-template name="count-impl">
<xsl:with-param name="sorted-list" select="$sorted-list"/>
<xsl:with-param name="cur-position" select="$cur-position + 1"/>
<xsl:with-param name="max-position" select="$max-position"/>
<xsl:with-param name="cur-value" select="$cur-value"/>
<xsl:with-param name="max-value" select="$max-value"/>
<xsl:with-param name="count" select="$count + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="print">
<xsl:with-param name="value" select="$cur-value"/>
<xsl:with-param name="count" select="$count"/>
</xsl:call-template>
<xsl:call-template name="count-impl">
<xsl:with-param name="sorted-list" select="$sorted-list"/>
<xsl:with-param name="cur-position" select="$cur-position"/>
<xsl:with-param name="max-position" select="$max-position"/>
<xsl:with-param name="cur-value" select="$cur-value + 1"/>
<xsl:with-param name="max-value" select="$max-value"/>
<xsl:with-param name="count" select="0"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
|
|
- ほむら
- ぬし
- 会議室デビュー日: 2003/02/28
- 投稿数: 583
- お住まい・勤務地: 東京都
|
投稿日時: 2003-08-07 17:06
ども、ほむらです。
誰も使用していないのでもしかしたら
環境依存なのかもしれませんが。。。。
---------------------
XML文書はocean氏の物を使用しています
コード: |
|
<?xml version="1.0" encoding="shift-jis"?>
<xsl:stylesheet version="1.0"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xml:lang="ja">
<xsl:template match="/root">
全ノード数は <xsl:value-of select="count(child::item)" /> です
<xsl:for-each select="item">
<xsl:sort data-type="number" select="@value"/>
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="item">
<xsl:variable name="v" select="@value" />
<!-- 自分よりも前に同じノードがあった場合は集計済みなので無視 -->
<xsl:if test="count(preceding-sibling::item[@value=$v]) = 0">
<!-- 自分よりも後ろにあるノードの集計 -->
(<xsl:value-of select="@value"/>, <xsl:value-of select="count(following-sibling::item[@value=$v])+1"/>),
</xsl:if>
</xsl:template>
</xsl:stylesheet>
|
出力結果:
全ノード数は 7 です (1, 2), (4, 1), (5, 2), (6, 1), (7, 1),
テストはWindows98SE MSXML4 SP1です
|
- ocean
- ベテラン
- 会議室デビュー日: 2003/07/06
- 投稿数: 65
|
投稿日時: 2003-08-07 18:34
こんにちは、ほむらさん。
このコードは自分で組んだ後で知りましたが、0個も出力する仕様のようだったので、見なかったことにしました。
試しに1792個のノードで計測したところ、
最初の投稿:7.5sec
最後の投稿:1.4sec
ほむらさんのコード:3.7sec
でした。思ったほど速度差もないので、もし0個を出力しなくて良いのなら、私のコードは忘れてください。
|
- ほむら
- ぬし
- 会議室デビュー日: 2003/02/28
- 投稿数: 583
- お住まい・勤務地: 東京都
|
投稿日時: 2003-08-07 21:47
ども、ほらむです。
さすがに数がいっぱいあると遅すぎですね。
手抜きはよくないです(笑
引用: |
|
試しに1792個のノードで計測したところ、
最初の投稿:7.5sec
最後の投稿:1.4sec
ほむらさんのコード:3.7sec
|
というわけで修正です。
コード: |
|
<xsl:if test="count(preceding-sibling::item[@value=$v]) = 0">
という1行を
<xsl:if test="name(preceding-sibling::item[@value=$v]) = ''">
に変更すると倍くらい速くなります
|
|
- ocean
- ベテラン
- 会議室デビュー日: 2003/07/06
- 投稿数: 65
|
投稿日時: 2003-08-07 22:48
おお、ほむらさんの新コードだと0.7secでした。すごい。
<xsl:if test="name(preceding-sibling::item[@value=$v]) = ''">
のところって、キャッシュとか最適化とか働いてるんでしょうか。線形探索に見えたので、ここまで速くなるとは思いませんでした。
色々いじっていて気づいたのですが、
<xsl:sort data-type="number" select="@value"/>
はなくても速度が変わりませんでした。なので、ソートが速度に関係しているわけでもなさそうです。面白い・・・
単純な上に高速とあっては・・・私のコードは完全に忘れてください。
[ メッセージ編集済み 編集者: ocean 編集日時 2003-08-07 22:56 ]
|