この広告は、90日以上更新していないブログに表示しています。
Twitterでこんなつぶやきをしました。
プログラミング言語としてのSQL(select)とか、プログラミング言語としてのXSLTとかやると、構文大事って実感持ててオススメ。オススメはしない。
— ぐるぐる系SQL (@bleis)2021年4月24日
が、ちょっと違う感じに受け取られたかな、と思ったので補足します。
つぶやきの中でいちいち「プログラミング言語としての」と書いたのは、「普通の」SQLや「普通の」XSLTとは違って、ということを明示したかったからです。
普通はSQLは問合せのための言語だし、XSLTはXMLを変換するための言語*1であって、JavaやC#などのプログラミング言語と同じようなものと見ている人はほぼいないでしょう。ただ、ちょっとしたテクニックを知っていると、これらを使って「普通の」プログラミング言語でやるような処理が書けてしまうのです。
XSLTにはループや分岐が組み込まれているので、手続き型プログラミングであればどうとでもなります。なりますが、それでは面白くないのでループを封印してみましょう。
XSLTではtemplate を関数とみなすことで、ループを使わなくても再帰呼び出しが使えます。あとは高階関数が使えればざっくり関数プログラミング的なものが出来ますね。詳しい説明はそのアイディアを形にした人が書いたドキュメント に譲るとして、そのテクニックを使うことでFizzBuzzがrange 関数とmap 関数とfizzbuzz 関数を組み合わせることで書けてしまいます。
<?xml version="1.0" encoding="UTF-8"?><!-- 最近のXSLT事情がよくわからないけど、Saxonで動作確認(ファイル名: style.xsl) --><xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:xs="http://www.w3.org/2001/XMLSchema"xmlns:fizzbuzz="fizzbuzz"version="3.0"><xsl:outputmethod="text"/><!-- fromからtoまでの数列を作る関数 --><xsl:templatename="range"as="xs:integer *"><xsl:paramname="from"as="xs:integer"/><xsl:paramname="to"as="xs:integer"/><xsl:choose><xsl:whentest="$from<= $to"><xsl:sequenceselect="$from"/><xsl:call-templatename="range"><xsl:with-paramname="from"select="$from + 1"/><xsl:with-paramname="to"select="$to"/></xsl:call-template></xsl:when><xsl:otherwise></xsl:otherwise></xsl:choose></xsl:template><!-- シーケンスのmap関数 --><xsl:templatename="map"><xsl:paramname="sequence"/><xsl:paramname="f"/><xsl:choose><xsl:whentest="empty($sequence)"></xsl:when><xsl:otherwise><xsl:variablename="v"><xsl:apply-templatesselect="$f"><xsl:with-paramname="arg0"select="$sequence[1]"/></xsl:apply-templates></xsl:variable><xsl:sequenceselect="$v"/><xsl:call-templatename="map"><xsl:with-paramname="sequence"select="$sequence[position() > 1]"/><xsl:with-paramname="f"select="$f"/></xsl:call-template></xsl:otherwise></xsl:choose></xsl:template><fizzbuzz:fizzbuzz/><!-- 数値を与えるとFizzBuzzかFizzかBuzzか数値自身を返す関数 --><xsl:templatename="fizzbuzz"match="*[namespace-uri()='fizzbuzz']"><xsl:paramname="arg0" /><xsl:variablename="n"select="number($arg0)"/><xsl:choose><xsl:whentest="$n mod 15 = 0"><xsl:value-ofselect="'FizzBuzz
'"/></xsl:when><xsl:whentest="$n mod 5 = 0"><xsl:value-ofselect="'Buzz
'"/></xsl:when><xsl:whentest="$n mod 3 = 0"><xsl:value-ofselect="'Fizz
'"/></xsl:when><xsl:otherwise><xsl:value-ofselect="$n"/><xsl:value-ofselect="'
'"/></xsl:otherwise></xsl:choose></xsl:template><!-- エントリーポイント --><xsl:templatematch="/"><xsl:variablename="from"select="input/from"/><xsl:variablename="to"select="input/to"/><xsl:variablename="xs"as="xs:integer *"><xsl:call-templatename="range"><xsl:with-paramname="from"select="$from"/><xsl:with-paramname="to"select="$to"/></xsl:call-template></xsl:variable><xsl:call-templatename="map"><xsl:with-paramname="sequence"select="$xs"/><xsl:with-paramname="f"select="document('')/*/fizzbuzz:*[1]"/></xsl:call-template></xsl:template></xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?><!-- 入力ファイル(ファイル名: input.xml) --><input><from>1</from><to>100</to></input>
# 実行方法$ saxon -s:input.xml -xsl:style.xsl -o:output.txt
長いので折り畳みましたが、これと同じようなことを F# で書くとこうなります。
letrecrange from ``to``=if from<= ``to``then from:: range(from+1) ``to``else[]letrecmap f=function|[]->[]| x::xs-> f x:: map f xsletfizzbuzz n=if n %15=0then"FizzBuzz\n"elif n %5=0then"Buzz\n"elif n %3=0then"Fizz\n"elsestring n+"\n"letxs= range1100xs|> map fizzbuzz|>List.iter(printf"%s")
大体機械的に対応は取れると思います。
SQLは再帰クエリーによって再帰が書けます。しかし、XSLTとは違い関数に対応させられるようなものが(SELECT文の範疇では)ありませんし、高階関数など夢のまた夢です。そのため、SQLでのプログラミングはXSLTよりも困難です。
ただ、再帰クエリーでも使うWITH を「入力固定の関数」のようなものとみなすことで、ある程度の構造化は可能です。
-- 最近PostgreSQLを使っているのでPostgreSQL想定WITH RECURSIVE range_ (n_)AS (SELECT1UNIONALLSELECT n_ +1FROM range_WHERE n_ <100), fizzbuzz_ (n_, result_)AS (SELECT n_ ,CASEWHEN n_ %15 =0THEN'FizzBuzz'WHEN n_ %5 =0THEN'Buzz'WHEN n_ %3 =0THEN'Fizz'ELSECAST(n_ASvarchar)ENDFROM range_)SELECT *FROM fizzbuzz_;
さらに複雑なことがしたい場合、空白区切りなどの文字列をリストと見立てて操作することでよりいろいろなことができるようになります*2。
こんな感じで、XSLTでプログラミングしようとすると大量のノイズで本来書きたい処理はタグの中に埋もれてしまいますし、SQL(select)でプログラミングしようとするとそもそも関数からしてないので考え方からして変えないといけません(そのための例としてはFizzBuzzは小さすぎたかも)。SQLで複雑なやつだと、SQL で数式を評価 (完全版 + α) - ぐるぐる~ あたりがオススメです。数式評価だけど、演算子の優先順位を設定可能な完全に頭おかしいやつです。
ということで、他の人にはあまりオススメできない、オススメの構文のありがたみが体感できる方法でした。制約された環境であれこれ考えるのが好きな人であればあるいは・・・
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。