2
votes

Utilisation de fn: random-number-generator pour produire des nombres aléatoires plus d'une fois

J'essaie d'écrire une fonction simple pour me fournir une lettre aléatoire à chaque fois que je l'appelle mais j'ai des difficultés à combiner mon idée avec le concept d'une approche de programmation fonctionnelle. Une aide en cours de route serait appréciée! Le code que j'ai ressemble à:

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

4 Réponses :


0
votes

Le problème est que fn : la fonction de générateur de nombres aléatoires est déterministe. Les spécifications elles-mêmes expliquent que:

Les deux formes de la fonction sont · déterministes · : appel de la fonction deux fois avec les mêmes arguments, dans un même · périmètre d'exécution · , produit les mêmes résultats.

Vous devez utiliser correctement la fonction sous la touche next contenue dans la carte résultante de l'appel de la fonction random-number-generator . Comme le dit la spécification:

L'entrée avec la touche "suivant" est une fonction zéro-arité qui peut être appelée pour renvoyer un autre générateur de nombres aléatoires.


2 commentaires

Merci Alejandro, que j'ai lu et compris. Cependant, je pourrais utiliser une certaine assistance pour savoir comment utiliser correctement la fonction suivante. Un petit exemple serait bénéfique.


@ErikZander, vous devez essentiellement écrire une fonction en prenant un générateur de nombres aléatoires comme argument et en s'appelant récursivement avec la valeur $ rng? Next () , comme le fait la spécification dans l'exemple de fonction r: random-sequence ou comme le fait Michael Kay dans le commentaire à stackoverflow.com/a/41586171/252228 avec fold-left : fold-left (1 à 10, random-number-generator (), function ($ z, $ i) {head ($ z)? next () , tail ($ z), head ($ z)? number}) pour que le traitement ultérieur reste possible en utilisant le head () de cet appel ou en l'arrêtant en utilisant le queue () .



2
votes

Dans le contexte de XSLT 3, je pense qu'une façon d'avoir un "nouveau" générateur de nombres aléatoires pour chaque nœud dont vous avez besoin est de définir un accumulateur:

<xsl:mode on-no-match="shallow-copy" use-accumulators="rng"/>


0 commentaires

5
votes

Votre question résume le problème:

J'essaye d'écrire une fonction simple pour me fournir une lettre aléatoire chaque fois que je l'appelle

Mais une fonction qui produit des résultats différents sur différentes invocations (avec les mêmes arguments) n'est pas une fonction vraie ("pure").

Une façon de sortir de cela est d'exploiter le fait que XSLT a déjà des fonctions "impures" d'un genre: une fonction qui crée un nouveau nœud retourne un nœud différent à chaque fois, et vous pouvez l'exposer en utilisant generate-id (). Vous pouvez donc écrire

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

Le seul problème avec ceci est que vous êtes juste sur les limites de ce qui est bien défini dans la spécification et l'optimiseur pourrait ne pas vous laisser partir avec de telles astuces. Il est préférable, si vous le pouvez, de trouver un moyen de passer un argument différent à la fonction à chaque fois qu'elle est appelée: par exemple, un numéro de séquence, ou generate-id () appliqué au nœud d'entrée que vous traitez actuellement.


1 commentaires

J'aime aussi cette réponse: si vous voulez des effets secondaires dans un environnement déclaratif, vous devrez encoder vous-même un type d'état. Lectures complémentaires stackoverflow.com/questions / 3850368 /…



0
votes

Par souci d'exhaustivité, j'ai proposé cette solution mais elle ne fonctionne que pour de petits morceaux de texte en raison de la profondeur de la récursivité.

En passant, j'ai réalisé que ma solution était une perte de temps car J'utilise exist-db qui n'inclut pas le générateur de nombres aléatoires dans son implémentation 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>


2 commentaires

Il semble que vous devriez pouvoir utiliser le modèle fold-left suggéré par Michael Kay, par exemple. fold-left (functx: chars ($ text), random-number-generator (), function ($ z, $ c) {head ($ z)? next (), tail ($ z), gdpr: randomLetter2 (head ($ z), $ c)}) => tail () => string-join () .


Oui, cela fonctionnerait très probablement, j'ai juste réussi avec ce que j'ai terminé, donc je n'ai pas eu le temps de vous vérifier. Mais votre solution a certainement l'air simple et je vous en remercie! Je dois l'essayer si je dois revoir cela. @MartinHonnen