用于对字符串中的字符进行计数/格式化的xquery递归
我试图在xquery 1.0中找到以下问题的解决方案。我只需要单独使用递归函数。输入的字符始终仅在A-Z之间用于对字符串中的字符进行计数/格式化的xquery递归,xquery,Xquery,我试图在xquery 1.0中找到以下问题的解决方案。我只需要单独使用递归函数。输入的字符始终仅在A-Z之间 1. AAABBCCD --> A1A2A3-B1B2-C1C2-D1 2. ABBAB --> A1-B1B2-A2-B3 谢谢。到目前为止,我所做的尝试没有任何结果。首先,您可能希望将字符串拆分为单个字符,这可以通过多种方式完成(例如,使用fn:substring(…)),我将使用fn:string-to-codepoints($str): 示例:local:t
1. AAABBCCD --> A1A2A3-B1B2-C1C2-D1
2. ABBAB --> A1-B1B2-A2-B3
谢谢。到目前为止,我所做的尝试没有任何结果。首先,您可能希望将字符串拆分为单个字符,这可以通过多种方式完成(例如,使用
fn:substring(…)
),我将使用fn:string-to-codepoints($str)
:
示例:local:to chars('ABC')
生成序列('A','B','C')
对于格式化方案,您必须跟踪
- 一个接一个地检查每个字符
- 获取并更新其在地图中的计数
- 决定是否在输出字符串中的破折号之前添加破折号
- 然后再发生
declare function local:format($chars, $counts, $last, $out) {
if(empty($chars)) then $out
else (
let $c := head($chars), (: current char :)
$new-chars := tail($chars), (: rest of the chars :)
$count := ($counts($c), 1)[1], (: old count if present, `1` otherwise :)
$cc := $c || $count, (: char with count :)
$new-out := if($c eq $last) then $out || $cc (: updated output string :)
else $out || '-' || $cc,
$new-counts := map:put($counts, $c, $count + 1) (: updated map :)
return local:format($new-chars, $new-counts, $c, $new-out)
)
};
我们使用空映射调用它,并将序列中的第一个字符称为“previous”字符,以避免在输出的开头出现破折号
declare function local:format-string($str) {
let $chars := local:to-chars($str)
return local:format($chars, map{}, head($chars), '')
};
这适用于任意字符:local:format string('HELLO')
产生'H1-E1-L1L2-O1'
编辑:
我一定跳过了XQuery1.0部分,我指责咖啡因的缺乏。。。您还可以将计数保留为26个整数的序列,而不是映射:
declare function local:format($chars, $counts, $last, $out) {
if(empty($chars)) then $out
else (
let $c := $chars[1],
$new-chars := subsequence($chars, 2),
$pos := string-to-codepoints($c) - 64,
$count := $counts[$pos],
$cc := $c || $count,
$new-out := if($c eq $last) then concat($out, $cc)
else concat($out, '-', $cc),
$new-counts :=
(
subsequence($counts, 1, $pos - 1),
$count + 1,
subsequence($counts, $pos + 1)
)
return local:format($new-chars, $new-counts, $c, $new-out)
)
};
declare function local:format-string($str) {
let $chars := local:to-chars($str),
$counts := for $i in 1 to 26 return 1
return local:format($chars, $counts, head($chars), '')
};
我试图了解如何在XQuery1.0中实现以下功能。我是
仅使用递归函数寻找解决方案。这个
输入将始终只有A-Z之间的字符
1. AAABBCCD --> A1A2A3-B1B2-C1C2-D1
2. ABBAB --> A1-B1B2-A2-B3
这里有一个XPath 2.0表达式:
string-join(
(for $s in .,
$indS in 1 to string-length($s),
$cp in string-to-codepoints($s)[$indS]
return
(
'-'[$indS gt 1 and $cp ne string-to-codepoints($s)[$indS -1]]
,
concat(codepoints-to-string($cp),
for $i in 1 to count(index-of(string-to-codepoints($s), $cp))
return
$i[index-of(string-to-codepoints($s), $cp)[$i] eq $indS]
)
)
),
''
)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="t">
<xsl:sequence select=
"string-join(
(for $s in .,
$indS in 1 to string-length($s),
$cp in string-to-codepoints($s)[$indS]
return
(
'-'[$indS gt 1 and $cp ne string-to-codepoints($s)[$indS -1]]
,
concat(codepoints-to-string($cp),
for $i in 1 to count(index-of(string-to-codepoints($s), $cp))
return
$i[index-of(string-to-codepoints($s), $cp)[$i] eq $indS]
)
)
),
''
)
"/>
</xsl:template>
</xsl:stylesheet>
<z>
<t>AAABBCCD</t>
<t>ABBAB</t>
<t>aBcBaBcc</t>
</z>
A1A2A3-B1B2-C1C2-D1
A1-B1B2-A2-B3
a1-B1-c1-B2-a2-B3-c2c3
这当然是非递归的,我认为这是一个优势——我们保证不会发生调用堆栈溢出:)
->>>>>还要注意,输入字符可以有任何值,而不仅仅是[A-Z]。使用简单的序列将XQuery 3.0、基于地图的解决方案很好地转换为某种XQuery 1.0方式。但是我认为,XQuery1中没有
head
和tail
,因此需要添加更多的子序列
或谓词。使用concat
而不是| |
。Leo和@MartinHonnen,只是为了记录,存在一个XPath 2.0表达式/解决方案-当然是非递归的,因为这是XPath 2.0 Hanks Martin,我希望现在是正确的XQ1.0。感谢您的回复。我的任务是学习xquery中的递归,并了解如何在递归调用之间管理变量。你的帖子向我展示了在xquery中解决问题的那些部分。我没有为头/尾/管的小问题烦恼。@LeoWörteler,这是否只适用于拉丁字母表中的26个字符?也许一个更普遍的解决方案对读者更有用?好问题。作为记录,存在一个XPath2.0解决方案——当然是非递归的,因为这是XPath2.0。当然,这也是一个XQuery 1.0解决方案,因为XQuery 1.0是Xpath 2.0Slkrasnodar的超集,我的答案对解决您的问题有用吗?如果您对此有任何疑问/问题,请在评论中说明。如果一切都好的话,请考虑接受一个回答。@ DimitreNovatchev,谢谢你的回答。我仍在阅读Leo的第一个答案,并将其移植到OracleEnv。仅供参考,我对两个答案都投了赞成票。请给我一些时间,如果我有问题,我可以回来。斯克拉斯诺达尔,没问题。新年快乐学到了一些东西。正如你所说,你的答案可能更有效,但我可能已经用我的标题和问题的措辞影响了利奥,让他在回复中发表自己的观点。因此,我选择了这一点作为我接受的答案。谢谢。@Slkrasnodar,很高兴这个回答有帮助。无论您接受哪种答案,我都强烈建议在实际生产工作中使用非递归解决方案,以避免意外的崩溃。至少在BaseX中,这两个问题(调用堆栈和每个递归调用的新序列)实际上并不相关。由于函数是尾部递归的,因此调用堆栈不会任意增长。这是大多数XQuery处理器都应该实现的众所周知的优化。序列在内部表示为Finger Trees(B树的变体),这意味着在中间插入值会导致与原始结构共享大部分结构的序列。函数式语言的良好实现不应阻碍递归算法。很好的评论,@LeoWörteler,是的,并非所有的XQuery处理器都是平等的。感谢您在BaseX中实现手指树数据结构!我看到一位权威人士的声明,W3C工作组没有强制要求尾部递归优化,因为他们不知道如何严格定义“尾部递归”。。。