【问题标题】:Using fn:random-number-generator to produce random numbers more than once使用 fn:random-number-generator 多次生成随机数
【发布时间】:2019-09-13 21:51:24
【问题描述】:

我尝试编写一个简单的函数,每次调用它时都会为我提供一个随机字母,但我很难将我的想法与函数式编程方法的概念结合起来。一路上的一些帮助将不胜感激! 我的代码如下:

<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>

【问题讨论】:

    标签: xslt xslt-3.0


    【解决方案1】:

    您的问题概括了问题:

    我尝试编写一个简单的函数来为我提供一个随机字母 每次我调用它

    但是在不同的调用(具有相同的参数)上产生不同结果的函数不是真正的(“纯”)函数。

    解决这个问题的一种方法是利用 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>
    

    唯一的问题是,您正处于规范中明确定义的边界,而优化器可能不会让您摆脱这些技巧。如果可以的话,最好找到某种方式在每次调用函数时将不同的参数传递给函数:例如,应用到当前正在处理的输入节点的序列号或 generate-id()。

    【讨论】:

    【解决方案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"/>
    

    【讨论】:

      【解决方案3】:

      问题在于fn:random-number-generator function 是确定性的。规格本身解释说:

      函数的两种形式都是·确定性·:调用函数 在一个·执行范围· 内使用相同的参数两次, 产生相同的结果。

      您需要正确使用调用random-number-generator 函数的结果映射中包含的键next 下的函数。正如规范所说:

      键为“next”的条目是一个可以调用的零参数函数 返回另一个随机数生成器。

      【讨论】:

      • 谢谢亚历杭德罗,我已阅读并理解。但是,我可以使用一些帮助来正确使用下一个功能。一个小例子会很有帮助。
      • @ErikZander,您基本上必须编写一个函数,将随机数生成器作为其参数,并使用 $rng?next() 值递归调用自身,就像示例函数 r:random-sequence 中的规范一样Michael Kay 在对stackoverflow.com/a/41586171/252228 的评论中使用fold-leftfold-left(1 to 10, random-number-generator(), function($z, $i){ head($z)?next(), tail($z), head($z)?number}) 通过使用该调用的head() 或使用tail() 停止它来保持进一步处理。
      【解决方案4】:

      为了完整起见,我想出了这个解决方案,但由于递归的深度,它只适用于小段文本。

      在旁注中,我意识到我的解决方案是浪费时间,因为我使用了在其 XSLT 实现中不包含随机数生成器的存在数据库。

      <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>
      

      【讨论】:

      • 看来您应该能够使用 Michael Kay 建议的 fold-left 模式,例如fold-left(functx:chars($text), random-number-generator(), function($z, $c){ head($z)?next(), tail($z), gdpr:randomLetter2(head($z), $c)}) =&gt; tail() =&gt; string-join().
      • 是的,这很可能会奏效,我刚刚完成了我完成的工作,所以没有时间检查你。但是您的解决方案确实看起来很漂亮,我感谢您!如果我必须重新审视这个,必须尝试一下。 @MartinHonnen
      猜你喜欢
      • 2013-08-05
      • 2017-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-11
      • 1970-01-01
      • 2021-05-16
      • 1970-01-01
      相关资源
      最近更新 更多