在XQuery中更新变量 - 可能与否?

时间:2010-09-23 21:01:59

标签: xquery

我对XQuery中的变量更新有点困惑: 在[1]上它说:

  

无法更新变量。这意味着你不能写像$ x:= $ x + 1这样的东西。如果您期望XQuery的行为方式与JavaScript等过程语言相同,那么这条规则可能看起来很奇怪。但XQuery不是那种语言,它是一种声明性语言并且在更高层次上工作。没有关于执行不同表达式的顺序的规则(这意味着在Stylus Studio XQuery调试器和XSLT调试器中显示当前执行点的小黄色三角形有时会以令人惊讶的方式运行),这意味着构造的结构结果将取决于执行顺序(如变量赋值)被禁止。

我想知道是否真的没有办法可靠地更新变量?也许我只是习惯了其他语言的东西,但我无法想象/相信它; - )

[1] http://www.stylusstudio.com/xquery_flwor.html,“L代表LET”这一章的截图下面的第二段

更新 我必须向此添加一个问题:是否不可能更新if语句中的现有变量,因为在这种情况下执行顺序是否清楚?我猜你不允许在循环中使用$ x = $ x + 1这样的东西?

6 个答案:

答案 0 :(得分:6)

实际上,如果您的XQuery处理器支持 XQuery Scripting Extension 1.0

,则可以更新变量

例如,以下示例适用于Zorba Sandbox

declare namespace an = "http://zorba.io/annotations";

declare %an:sequential function local:fib(){
  variable $a as xs:integer := 0;
  variable $b as xs:integer := 1;  
  variable $c as xs:integer := $a + $b;
  variable $fibseq as xs:integer* := ($a, $b);
  while ($c < 100) { 
     $fibseq := ($fibseq, $c);
     $a := $b;
     $b := $c;
     $c := $a + $b; 
  } 
  $fibseq
};

local:fib()

顺序功能可以进行更新。 apply语句(以;结尾的每段代码)立即应用所有更新。

如果您只想在FLWOR中设置计数变量,可以使用at关键字:

for $item at $x in ("a","b","c")
return $x

返回:

<?xml version="1.0" encoding="UTF-8"?>
1 2 3

答案 1 :(得分:5)

你在描述不变性,functional languages.的一个特征这是真的;一旦变量设置为值,就不能将其设置为其他值。

不变性有很多好处。特别是,concurrent programming变得更加容易。

在循环的情况下,会发生的是每次循环创建一个新变量,替换原始变量。所以不变性仍然存在。您在链接的文章中详细解释了这一点:

  

是否有更新变量   当你写的东西像   以下

for $v in //video
let $x := xs:int($v/runtime) * xdt:dayTimeDuration("PT1M")
return concat($v/title, ": ", 
      hours-from-duration($x), " hour(s) ",
      minutes-from-duration($x), " minutes")
     

(此查询显示的运行时间   每个视频。它首先转换   从字符串到存储的值   整数,然后乘以1   分钟(PT1M)来获取运行时间   作为一个持续时间,以便它可以提取   小时和分钟的组成部分   持续时间。试试吧。)

     

这里变量$ x有不同   每次围绕XQuery的值   环。这感觉有点像更新。   从技术上讲,每次都是这样   for循环你正在创建一个新的   变量带有新值,而不是   为旧的分配新值   变量

答案 2 :(得分:2)

回答关于变量的第一个问题,以及关于if语句的第二个问题。

你不能改变一个变量的值(愚蠢的名字不是它,因为它们没有变化!)。

使用XQuery和任何复杂逻辑(以及类似可变变量之类的东西)的神奇小精灵尘埃是递归。

罗伯特哈维提到了for循环的语言结构,其中变量每次都会发生变化,这是非常相关的,但除非你的预期行为可以通过简单的列表迭代来实现,否则它不能总是解决你的问题。你要求的是可变变量。

通过递归,您的函数可以自行调用。这意味着它们可以将修改后的值传递给下一个函数调用,而不是它们已经具有的值。它或多或少地增加了一个可变变量,但允许保留功能语言的好处。

从程序性(顺序执行步骤)思维方式转变为功能性(同时评估条款)思维方式可能有点头晕。

递归允许实际评估的子句依赖于复杂逻辑,哪种看起来是顺序的。在实践中,你只是创建了一些条款,要求其他人在评估自己之前进行评估,这不完全相同。

这是一个愚蠢的,完全不相关且完全未经测试的示例,它显示了如何在递归时“动态”修改列表,这在for循环中是不可能的。

注意变量$ patternsremaining严格来说是一个新变量(部分基于$ patterns计算)。无论将哪种模式作为参数传递到递归调用中,都会在新函数调用中分配给$ patterns。

(: Here $patterns looks like <pattern match="something" replace="else" /> :)
declare function local:transform($text as text(), $patterns as element(pattern)*) {
   if(not($patterns)) then 
      $text
   else
      let $patternsremaining := $patterns[position() > 1],
          $modifiedtext := replace($text, $pattern/@match, $pattern/@replace)
      return 
         if($local:language="French" and not($patterns[@match='le'])) then (
             local:transform($modifiedtext, ($patternsremaining, <pattern match="Londres" replace="London" />))
      )
      else(
         local:transform($modifiedtext, $patternsremaining)
      )
};

对于使用XSLT和XQuery的硬核活动(例如编写编译器),递归是我发现的唯一具有足够功能的模型。然而,真实的例子看起来比上面的例子更复杂。

关于if()then()else()语句,因为之前遇到过相同的执行上下文(如果你是程序编码器认为'堆栈变量组合')是可行的,同样的表达式是已经在其他地方进行了评估,然后永远不会再次评估if语句,因为解释器可以根据先前的调用来缓存结果。因此,您可以依赖序列并不严格。可能根本没有运行!

这是可能的,因为它内置于语言定义中,可能没有副作用会改变功能评估的结果(因此那些不变的变量)。

此可缓存性是功能方法的核心功能。它创造了编写高效解释器的潜力,但如果您希望能够使用可变值进行操作,则需要您递归思考。

答案 3 :(得分:0)

不,您无法更新变量。如果你不能依赖执行的顺序,那么这样做怎么会有意义呢?

当然,即使没有可更新的变量,您也可以使用递归函数执行几乎所有操作,包括使用递增变量的“循环”。这是有效还是好主意是另一回事。我曾经使用递归模板在XSLT(也有不可更新的变量)中实现了sqrt() ...

答案 4 :(得分:0)

虽然无法更新变量,但您可以定义具有相同名称的第二个变量,在这种情况下,对具有其绑定范围的该变量的任何引用都将绑定到具有较小范围的后一个变量。

所以

let $x := $x + +1
return $x

装置

let $x_2 := $x_1 +1
return $x_2

答案 5 :(得分:0)

相信它。

在这方面,XQuery就像是一种函数式编程语言。您不希望从功能/数学角度更改已计算的计算的值。此外,数据是不可变的,不能更改状态,因此可以使用该语言来构建高并发应用程序。 Erlang是为高并发性而构建的语言的“好”示例。不可变性也用于某些命令式语言,例如Java,以实现高并发性。

就像在任何其他功能语言中一样,您可以始终确保“评估”您的“expr”,然后使用副本进行所需的更改。

请注意,我并不是说XQuery是一种优秀的函数式编程语言。看看erlang是否有一个良好的功能语言和实现的例子,可以提供良好的性能。