“依赖注入”是指向代码传递某种指针的面向对象概念。在 FP 中,这可以使用高阶函数来完成。
在 xslt 2+ 中似乎有这些替代方案
(还有其他替代方案可以使用 include/import 静态注入代码,其中包含样式表中的模板覆盖或与包含的样式表交互,但这更类似于“模板方法”,即静态组合)
XSLT 社区中有这方面的惯用/模式吗?
(如果能够以某种方式“捆绑”注入,就像接口或类型类所做的那样,这似乎可以使用伪模式技术,通过将“模式”作为元素或值嵌入到另一个元素中来实现,或者“模式”是一个“捆绑包”,但直接使用函数并不明显)
我没有给出明确的例子,如果您需要它们来明确我的意思,请告诉我。
实际上还有第三种选择,你返回 DSL,并“解释它”,不同的上下文以不同的方式解释,这可能类似于“管道”技术,虽然我非常喜欢它,但它有点矫枉过正当你只想注入一点细微差别时。
示例:
(都使用这个 xml - 这是不相关的
<root>
<foo/>
</root>
)
#1“高阶函数”(我认为这可能是从 Martin Honnen 之前关于 HOF 的回答中窃取的)
函数“apply-function”允许“注入”函数然后执行该函数,显然任何具有兼容签名的函数都可以注入。这是默认的 FP 技术。
<xsl:stylesheet version="3.0"
xmlns:my="my"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Define a function that accepts a function parameter -->
<xsl:function name="my:apply-function" as="xs:integer">
<xsl:param name="input" as="xs:integer"/>
<xsl:param name="function" as="function(xs:integer) as xs:integer"/>
<xsl:sequence select="$function($input)"/>
</xsl:function>
<!-- Define a sample function to be passed as a parameter -->
<xsl:function name="my:double" as="xs:integer">
<xsl:param name="value" as="xs:integer"/>
<xsl:sequence select="$value * 2"/>
</xsl:function>
<!-- Main template -->
<xsl:template match="/">
<result>
<!-- Call my:apply-function and pass my:double as a parameter -->
<xsl:variable name="result" select="my:apply-function(5, my:double#1)"/>
<doubled-value>
<xsl:value-of select="$result"/>
</doubled-value>
</result>
</xsl:template>
</xsl:stylesheet>
#2“伪模式”
通过这种方式,您可以在某种结构中明确编码您的“选择”,然后对其进行匹配。
即“函数指针”被编码为
<Mode value='double'/>
我怀疑这种技术有一个更好的标签,但对我来说,它感觉像是一种模拟运行时“模式”的机制。
<xsl:stylesheet version="3.0"
xmlns:my="my"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template mode="inject" match="Mode[@value = 'double']">
<xsl:param name="value" as="xs:integer"/>
<xsl:sequence select="$value * 2"/>
</xsl:template>
<xsl:template mode="inject" match="Mode[@value = 'treble']">
<xsl:param name="value" as="xs:integer"/>
<xsl:sequence select="$value * 3"/>
</xsl:template>
<!-- Main template -->
<xsl:template match="/">
<result>
<doubled-value>
<xsl:variable name="mode">
<Mode value='double'/>
</xsl:variable>
<xsl:apply-templates mode="inject" select="$mode">
<xsl:with-param name="value" select="5"/>
</xsl:apply-templates>
</doubled-value>
</result>
</xsl:template>
</xsl:stylesheet>
#3 DSL/解释器
这里的“被调用者”代码返回一些由 my:makeDSL 编码为 xml 的特定于域的语言,然后由客户端特定的解释器进行解释。
这里的依赖关系并不是真正注入的,而是外部化的,然后以被调用者想要的方式解释,但结果是相同的。
(“nop”指令仅用于说明)
<xsl:stylesheet version="3.0"
xmlns:my="my"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template mode="inject" match="Mode[@value = 'double']">
<xsl:param name="value" as="xs:integer"/>
<xsl:sequence select="$value * 2"/>
</xsl:template>
<xsl:template mode="inject" match="Mode[@value = 'treble']">
<xsl:param name="value" as="xs:integer"/>
<xsl:sequence select="$value * 3"/>
</xsl:template>
<xsl:template mode="double" match="nop"/>
<xsl:template mode="double" match="inject">
<xsl:sequence select="2 * @value"/>
</xsl:template>
<xsl:template mode="treble" match="nop"/>
<xsl:template mode="treble" match="inject">
<xsl:sequence select="3 * @value"/>
</xsl:template>
<!-- Main template -->
<xsl:template match="/">
<result>
<doubled-value>
<xsl:apply-templates mode="double" select="my:makeDSL(5)"/>
</doubled-value>
</result>
</xsl:template>
<xsl:function name="my:makeDSL">
<xsl:param name="value"/>
<nop/>
<inject value="{$value}"/>
<nop/>
</xsl:function>
</xsl:stylesheet>
目前,答案似乎是否定的(没有惯用的方法),至少不是有意识的。
我怀疑管道模式是惯用的,但在更一般的意义上,该语言鼓励采用类似于 OO 模板方法(即静态注入)的方法,其中包含/导入样式表并通过模式覆盖模板。
这并不是说它无法实现。
问题中的技术都是适用的,并且可以通过在Maps中嵌入函数来使用类似于“字典传递”的技术。
这是一个使用“monoid”的简单示例,即可以相加的事物。
我们有 2 个幺半群“字典”编码为映射,一个用于整数,一个用于字符串,我们将每个字典传递(注入)到一些代码中,“添加”一些值并返回答案。
输入的xml是这样的
<root>
<foo value1="123" value2="456"/>
</root>
样式表首先将 foo 元素处理为整数,然后处理为字符串
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:kooks="kookerella.com"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="#all"
version="3.1">
<xsl:function name="kooks:stringAdd" as="xs:string">
<xsl:param name="s1" as="xs:string"/>
<xsl:param name="s2" as="xs:string"/>
<xsl:sequence select="concat($s1,$s2)"/>
</xsl:function>
<xsl:variable name="stringMonoid" as="map(*)">
<xsl:map>
<xsl:map-entry key="'zero'" select="''" as="xs:integer"/>
<xsl:map-entry key="'add'">
<xsl:sequence select="kooks:stringAdd#2"/>
</xsl:map-entry>
</xsl:map>
</xsl:variable>
<xsl:function name="kooks:integerAdd" as="xs:integer">
<xsl:param name="i1" as="xs:integer"/>
<xsl:param name="i2" as="xs:integer"/>
<xsl:sequence select="$i1 + $i2"/>
</xsl:function>
<xsl:variable name="integerMonoid" as="map(*)">
<xsl:map>
<xsl:map-entry key="'zero'" select="0" as="xs:integer"/>
<xsl:map-entry key="'add'">
<xsl:sequence select="kooks:integerAdd#2"/>
</xsl:map-entry>
</xsl:map>
</xsl:variable>
<xsl:template match="/">
<root>
<integer>
<xsl:apply-templates select="root/foo">
<xsl:with-param name="monoid" select="$integerMonoid"/>
</xsl:apply-templates>
</integer>
<string>
<xsl:apply-templates select="root/foo">
<xsl:with-param name="monoid" select="$stringMonoid"/>
</xsl:apply-templates>
</string>
</root>
</xsl:template>
<xsl:template match="foo">
<xsl:param name="monoid"/>
<result
value1="{@value1}"
value2="{@value2}"
zero="{map:get($monoid,'zero')}"
sum="{map:get($monoid,'add')(@value1,@value2)}"/>
</xsl:template>
</xsl:stylesheet>
导致这样的结果
<root>
<integer>
<result value1="123" value2="456" zero="0" sum="579"/>
</integer>
<string>
<result value1="123" value2="456" zero="" sum="123456"/>
</string>
</root>