我尝试编写一个简单的函数来在每次调用它时为我提供一个随机字母,但是我很难将我的想法与功能编程方法的概念结合起来。一路上的一些帮助将不胜感激! 我的代码如下:
<xd:doc>
<xd:desc>Provides one random letter, if the type is provided it returns a letter of thet type</xd:desc>
<xd:param name="type">The type of letter to return, one of (A,a,B,b)</xd:param>
</xd:doc>
<xsl:function name="gdpr:randomLetter" as="xs:string">
<xsl:param name="type" as="xs:string"></xsl:param>
<xsl:choose>
<xsl:when test="$type = 'A'">
<xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 7)[1]"/>
<xsl:variable name="letters" select="('A','O','U','E','I','Y','Q')"/>
<xsl:value-of select="$letters[$randomNumber]"/>
</xsl:when>
<xsl:when test="$type = 'a'">
<xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 7)[1]"/>
<xsl:variable name="letters" select="('a','o','u','e','i','y','q')"/>
<xsl:value-of select="$letters[$randomNumber]"/>
</xsl:when>
<xsl:when test="$type = 'B'">
<xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 19)[1]"/>
<xsl:variable name="letters" select="('W','R','T','P','S','D','F','G','H','J','K','L','M','N','B','V','C','X','Z')"/>
<xsl:value-of select="$letters[$randomNumber]"/>
</xsl:when>
<xsl:when test="$type = 'b'">
<xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 19)[1]"/>
<xsl:variable name="letters" select="('w','r','t','p','s','d','f','g','h','j','k','l','m','n','b','v','c','x','z')"/>
<xsl:value-of select="$letters[$randomNumber]"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="randomNumber" select="random-number-generator()['next']?permute(1 to 52)[1]"/>
<xsl:variable name="letters" select="('A','O','U','E','I','Y','Q','a','o','u','e','i','y','q','w','r','t','p','s','d','f','g','h','j','k','l','m','n','b','v','c','x','z','W','R','T','P','S','D','F','G','H','J','K','L','M','N','B','V','C','X','Z')"/>
<xsl:value-of select="$letters[$randomNumber]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
答案 0 :(得分:4)
您的问题涵盖了问题:
我尝试编写一个简单的函数为我提供一个随机字母 每次我叫它
但是在不同的调用(具有相同的参数)上产生不同结果的函数不是真正的(“ pure”)函数。
一种解决方法是利用XSLT已经具有某种“不纯”函数的事实:创建新节点的函数每次都会返回一个不同的节点,您可以使用generate-id( )。所以你可以写
<xsl:function name="my:random" as="xs:double">
<xsl:variable name="dummy"><a/></xsl:variable>
<xsl:sequence select="fn:random-number-generator(generate-id($dummy))?permute(1 to 10)"/>
</xsl:function>
唯一的问题是,您正处于规范中定义良好的界限上,而优化器可能无法让您摆脱这些s俩。如果可以的话,最好找到一种在每次调用该函数时将不同的参数传递给该函数的方法:例如,将序列号或generate-id()应用于当前正在处理的输入节点。
答案 1 :(得分:2)
在XSLT 3的上下文中,我认为为您需要的每个节点使用一个“新” random-number-generator
的一种方法是定义一个累加器:
<xsl:accumulator name="rng" as="map(xs:string, item())" initial-value="random-number-generator(current-dateTime())">
<xsl:accumulator-rule match="foo[@type]" select="$value?next()"/>
</xsl:accumulator>
这样,您就可以将功能实现为
<xsl:function name="gdpr:randomLetter" as="item()*">
<xsl:param name="type" as="xs:string"/>
<xsl:param name="rng" as="map(xs:string, item())"/>
<xsl:choose>
<xsl:when test="$type = 'A'">
<xsl:variable name="randomNumber" select="$rng?permute(1 to 7)[1]"/>
<xsl:variable name="letters" select="('A','O','U','E','I','Y','Q')"/>
<xsl:sequence select="$letters[$randomNumber]"/>
</xsl:when>
<xsl:when test="$type = 'a'">
<xsl:variable name="randomNumber" select="$rng?permute(1 to 7)[1]"/>
<xsl:variable name="letters" select="('a','o','u','e','i','y','q')"/>
<xsl:sequence select="$letters[$randomNumber]"/>
</xsl:when>
<xsl:when test="$type = 'B'">
<xsl:variable name="randomNumber" select="$rng?permute(1 to 19)[1]"/>
<xsl:variable name="letters" select="('W','R','T','P','S','D','F','G','H','J','K','L','M','N','B','V','C','X','Z')"/>
<xsl:sequence select="$letters[$randomNumber]"/>
</xsl:when>
<xsl:when test="$type = 'b'">
<xsl:variable name="randomNumber" select="$rng?permute(1 to 19)[1]"/>
<xsl:variable name="letters" select="('w','r','t','p','s','d','f','g','h','j','k','l','m','n','b','v','c','x','z')"/>
<xsl:sequence select="$letters[$randomNumber]"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="randomNumber" select="$rng?permute(1 to 52)[1]"/>
<xsl:variable name="letters" select="('A','O','U','E','I','Y','Q','a','o','u','e','i','y','q','w','r','t','p','s','d','f','g','h','j','k','l','m','n','b','v','c','x','z','W','R','T','P','S','D','F','G','H','J','K','L','M','N','B','V','C','X','Z')"/>
<xsl:sequence select="$letters[$randomNumber]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
然后用例如
进行调用<xsl:template match="foo[@type]">
<xsl:copy>
<xsl:value-of select="gdpr:randomLetter(@type, accumulator-before('rng'))"/>
</xsl:copy>
</xsl:template>
并确保您使用
<xsl:mode on-no-match="shallow-copy" use-accumulators="rng"/>
答案 2 :(得分:0)
问题在于fn:random-number-generator
function是确定性的。规范本身解释了这一点:
函数的两种形式都是·确定性·:调用函数 在单个·执行范围·中,使用相同的参数两次 产生相同的结果。
您需要通过调用next
函数来正确使用结果映射中包含的键random-number-generator
下的函数。就像规范说的那样:
键为“ next”的条目是一个零arity函数,可以调用 返回另一个随机数生成器。
答案 3 :(得分:0)
为了完整起见,我想出了这个解决方案,但是由于递归的深度,它仅适用于小的文本。
在一个旁注中,我意识到我的解决方案是浪费时间,因为我在XSLT实现中使用了不包含随机数生成器的exist-db。
<xsl:function name="gdpr:rngRecurseStart">
<xsl:param name="text"></xsl:param>
<xsl:variable name="chars" select="functx:chars($text)"/>
<xsl:copy-of select="gdpr:rngRecurse($chars,random-number-generator(current-dateTime()),'')"></xsl:copy-of>
</xsl:function>
<xsl:function name="gdpr:rngRecurse">
<xsl:param name="chars"></xsl:param>
<xsl:param name="rngGenerator"></xsl:param>
<xsl:param name="newText"></xsl:param>
<xsl:variable name="curentchar" select="$chars[1]"></xsl:variable>
<xsl:variable name="newRngGenerator" select="$rngGenerator?next()"/>
<xsl:choose>
<xsl:when test="count($chars) >1">
<xsl:variable name="transformedChar" select="gdpr:randomLetter2($newRngGenerator,$curentchar)"/>
<xsl:variable name="resultText" select="concat($newText, $transformedChar)"/>
<xsl:copy-of select="gdpr:rngRecurse(subsequence($chars,2),$newRngGenerator,$resultText)"></xsl:copy-of>
</xsl:when>
<xsl:when test="count($chars) =1">
<xsl:variable name="transformedChar" select="gdpr:randomLetter2($newRngGenerator,$curentchar)"/>
<xsl:variable name="resultText" select="concat($newText, $transformedChar)"/>
<xsl:copy-of select="$resultText"></xsl:copy-of>
</xsl:when>
<xsl:otherwise><xsl:copy-of select="$newText"></xsl:copy-of></xsl:otherwise>
</xsl:choose>
</xsl:function>