【问题标题】:XQuery get Random Text from a ListXQuery 从列表中获取随机文本
【发布时间】:2017-07-04 03:45:33
【问题描述】:

假设我有一个 100 个字符串 元素的列表,我想随机返回 50 个这些随机文本字符串

我尝试这样做:

let $list := ("a","b",..."element number 100")
return xdmp:random(100)

这个查询返回一个字符串,我想返回 50 个彼此不同的字符串。

【问题讨论】:

    标签: list xquery marklogic


    【解决方案1】:

    xdmp:random()(以及xdmp:elapsed-time())在每次调用时返回不同的值。如果不这样做,那将是相当不切实际的。这与 fn:current-dateTime() 相对,后者在一次执行运行中给出相同的值。

    Ghislain 的第一次尝试很好,但正如 BenW 所指出的,尽管 xdmp:random() 每次确实返回不同的结果,但并不是说它们在一次执行运行中是唯一的。最大 64 位规模的冲突很少见(尽管仍有可能),但在像 10 或 100 的小规模上,可能会有一些意外重复。明智的做法是从列表中删除选定的文本。

    BenW 打败了我,发布了 Ghislain 的替代品,它看起来很相似,但使用的线条更少。无论如何发布它,希望有人觉得它有用:

    declare function local:getRandomTexts($list, $count) {
      if ($count > 0 and exists($list)) then
        let $random := xdmp:random(count($list) - 1) + 1
        let $text := $list[$random]
        return ($text, local:getRandomTexts($list[. != $text], $count - 1))
      else ()
    };
    
    let $list :=
      for $i in (1 to 26)
      return fn:codepoints-to-string(64 + $i)
    for $t in local:getRandomTexts($list, 100)
    order by $t
    return $t
    

    HTH!

    【讨论】:

    【解决方案2】:

    假设它在每次调用时返回不同的结果(但我无法从 xdmp:random 的文档中判断是否是这种情况),以下代码从随机选择的列表中返回 50 个字符串(但不一定不同):

    let $list := ("a","b",..."element number 100")
    for $i in 1 to 50
    let $position = 1 + xdmp:random(99)
    return $list[$position]
    

    但是,xdmp:random 的确切行为,即它是否在调用之间返回相同的结果,取决于 MarkLogic 引擎如何支持或处理非确定性行为,这超出了 XQuery 规范的范围。严格遵守规范实际上会返回与上述查询相同的结果的 50 倍。

    XQuery 3.1 提供了一个random number generator,您可以使用它来控制种子。这允许您通过链接调用生成任意数量的数字,同时仅使用可互操作的行为并保持在完全确定的范围内。

    编辑:这是一个查询(仍然假设每次都调用xdmp:random),它应该确保在 grtjn 的评论之后从列表中获取 50 个不同的字符串。它使用 group by 子句并依赖惰性求值来获取前 50 个。

    let $list := ("a","b",..."element number 100")
    let $positions := (
        for $i in 1 to 100000 (: can be adjusted to make sure we get 50 distinct :)
        group by $position = 1 + xdmp:random(count($list) - 1)
        return $position
      )[position() le 50]
    return $list[position() = $positions]
    

    我认为 Hunterhacker 提出的计算 $positions 的建议更好。

    【讨论】:

    • xdmp:random(N) 返回 0 到 N,而位置是 1-indexed。你需要类似xdmp:random(99) + 1
    • 啊,是的,这是一个很好的观点!在 XQuery 中以 0 开头有点不寻常。我已经相应地更正了。
    【解决方案3】:

    xdmp:random() 最容易订购,仅限前 50 个:

    (for $x in (1 to 100)
    order by xdmp:random()
    return $x
    )[1 to 50]
    

    【讨论】:

    • 这是一个不错的方法。
    【解决方案4】:

    如果你说这 50 个字符串必须彼此不同,例如,不允许重复,那么即使 xdmp:random() 在同一个查询中重复调用时返回不同的值,也会得到 50 个随机位置相同的列表是不够的,因为可能会有重复。您需要从 50 个不同的列表中获取随机位置。

    declare function local:pickSomeFromList($some as xs:integer, $listIn as xs:string*, $listOut as xs:string*) as xs:string* {
      if($some = 0 or not($listIn)) then $listOut
      else 
        let $random := xdmp:random(count($listIn) - 1) + 1
        return local:pickSomeFromList(
          $some - 1, 
          ($listIn[fn:position() lt $random],$listIn[fn:position() gt $random]), 
          ($listOut, $listIn[$random])
        )
    };
    let $list := ("a","b","c","d","e","f","g","h","i","element number 10")
    return local:pickSomeFromList(5, $list, ())
    

    【讨论】:

      猜你喜欢
      • 2023-03-11
      • 2015-09-22
      • 2015-01-06
      • 2013-07-06
      • 1970-01-01
      • 1970-01-01
      • 2017-08-30
      • 1970-01-01
      相关资源
      最近更新 更多