Обновление переменных в XQuery - возможно или нет? - PullRequest
5 голосов
/ 24 сентября 2010

Я немного сбит с толку относительно обновлений переменных в XQuery: в [1] он говорит:

Переменные не могут быть обновлены.Это означает, что вы не можете написать что-то вроде let $ 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 ]

6 голосов
/ 24 сентября 2010

На самом деле, обновление переменной возможно, если ваш процессор 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
5 голосов
/ 24 сентября 2010

Вы описываете неизменность, особенность функциональных языков. Это правда;как только переменная установлена ​​в значение, она не может быть установлена ​​в другое значение.

Неизменность имеет много преимуществ.В частности, параллельное программирование стало намного проще.

В случае циклов происходит то, что новая переменная создается каждый раз в цикле, заменяя исходную.Так что неизменность сохраняется.Это подробно объясняется в статье, на которую вы ссылаетесь:

Не обновляется ли переменная, когда вы пишете что-то вроде следующего?

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

(Этот запрос показываетВремя выполнения каждого видео. Сначала оно преобразует сохраненное значение из строки в целое число, а затем умножает его на одну минуту (PT1M), чтобы получить время выполнения в виде продолжительности, чтобы можно было извлечь компоненты продолжительности из часов и минут.Попробуйте.)

Здесь переменная $ x каждый раз имеет другое значение в цикле XQuery for.Это похоже на обновление.Технически, тем не менее, каждый раз вокруг цикла for вы создаете новую переменную с новым значением, а не назначаете новое значение старой переменной.

3 голосов
/ 08 января 2012

В ответ на ваш первый вопрос о переменных и второй вопрос об операторах if.

Вы не можете изменить значение переменной (глупое имя не так, учитывая, что они не меняются!).

Волшебная пыльца пикси для использования XQuery с любой сложной логикой (и что-то вроде изменяемых переменных) - это рекурсия.

Роберт Харви упомянул языковую конструкцию цикла for, в которой переменная меняет каждыйвремя, которое очень важно, но которое не всегда может решить ваши проблемы, если ваше намеченное поведение не может быть достигнуто простой итерацией списка.То, что вы просили, это изменяемые переменные.

С помощью рекурсии ваши функции вызывают себя.Это означает, что они могут передать измененное значение следующему вызову функции вместо значения, которое они уже имеют.Он более или менее дополняет изменчивую переменную, но позволяет сохранить преимущества функционального языка.

Может быть немного сложным переключиться с процедурного (последовательного выполнения шагов) способа мышленияк функциональному (одновременному оцениванию предложений) образу мышления.

Рекурсия позволяет предложениям, которые фактически оцениваются, зависеть от сложной логики, которая выглядит последовательной.На практике вы просто создаете предложения, которые требуют, чтобы другие оценивали, прежде чем они смогут оценить себя, что не совсем то же самое.

Вот глупый, совершенно неактуальный и полностью непроверенный пример, который показывает, как работает списокможет быть изменен «на лету», так как он проходит через рекурсию, что невозможно в цикле for.

Обратите внимание, что переменная $ patternremaining является строго новой переменной (рассчитанной частично на основе $ pattern).Независимо от того, какие шаблоны передаются в качестве аргумента в рекурсивный вызов, они присваиваются $ шаблонам в рамках нового вызова функции.

(: 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 (например, написание компиляторов) рекурсия является единственной моделью I 'нашел с достаточной силой.Реальные примеры имеют тенденцию выглядеть даже сложнее, чем приведенный выше.

Что касается оператора if () then () else (), потому что вполне возможно, что тот же контекст выполнения (если выпроцедурный кодер думает, что «комбинация переменных стека») встречалась ранее, и это же выражение было вычислено уже где-то еще, тогда оператор if НИКОГДА не будет вычисляться снова, потому что интерпретатор может кэшировать результат, основываясь на предыдущем вызове.Поэтому не совсем верно, что вы можете положиться на последовательность.Он может вообще не запускаться!

Это возможно, поскольку в определение языка встроено отсутствие побочных эффектов, которые могли бы изменить результат функциональной оценки (следовательно, те переменные, которые не меняются).

Эта кешируемость является центральной особенностью функционального подхода.Это создает потенциал для написания высокоэффективных интерпретаторов, но требует от вас рекурсивного мышления, если вы хотите работать с изменяемыми значениями.

0 голосов
/ 24 сентября 2010

Верьте.

XQuery в этом отношении похож на функциональный язык программирования.Вы не хотите изменять значение уже рассчитанного вычисления с функциональной / математической точки зрения.Кроме того, данные являются неизменяемыми и не могут изменять состояние, поэтому язык можно использовать для создания приложений с высоким уровнем параллелизма.Erlang - это «хороший» пример языка, созданного для высокого параллелизма.Неизменяемость также используется в некоторых императивных языках, таких как Java, для высокой степени параллелизма.

Как и в любом другом функциональном языке, вы всегда можете быть уверены, что «оценили» свой «expr», а затем внесли необходимые изменения с копией.

Обратите внимание, что я не говорю, что XQuery - хороший функциональный язык программирования.Посмотрите на erlang пример хорошего функционального языка и реализации, обеспечивающей хорошую производительность.

0 голосов
/ 24 сентября 2010

Хотя переменные не могут быть обновлены, вы можете определить вторую переменную с тем же именем, и в этом случае любые ссылки на эту переменную с областью ее привязки будут привязаны к более поздней переменной с меньшей областью действия.

Итак

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

означает

let $x_2 := $x_1 +1
return $x_2
0 голосов
/ 24 сентября 2010

Нет, вы не можете обновить переменную. Если вы не можете полагаться на порядок исполнения, как это будет иметь смысл в любом случае?

Конечно, даже без обновляемых переменных вы можете делать практически все, включая «циклы» с инкрементными переменными, используя рекурсивную функцию. Эффективно ли это или хорошая идея - другая история. Однажды я реализовал sqrt() в XSLT (который также имеет не обновляемые переменные), используя рекурсивный шаблон ...

...