Оператор if: неполное выражение FLWOR

У меня есть следующий код XQuery в редакторе BaseX, но он содержит ошибки в предложении if, заявляя

Неполное выражение FLWOR: ожидается "возврат"

Код, который я пытаюсь использовать:

import module namespace functx = 'http://www.functx.com';
declare namespace file = "http://expath.org/ns/file";

let $root := 'E:\basex-index-testing\sonar-small\index'
let $ds := file:dir-separator()

(: get files in sonar-small database :)
for $f in db:list('sonar-small')
  (: remove *.xml from doc name :)
  let $corpus := substring($f, 1, string-length($f) - 4)
  for $alpino in db:open('sonar-small', $f)/treebank/alpino_ds
  (: make sure tree has sentence element :)
  where count($alpino/sentence) > 0
      let $sentenceId := data($alpino/@id)

      for $node in $alpino//node
      (: make sure there are less than 500 descendants, 
      less than 100 and more than 0 children :)
      where count($node//node) < 500 and count($node/node) > 0 
            and count($node/node) < 100
        let $catTop := data($node/@cat)

        (: create indexing pattern based on node's direct children :)
        let $childrenRelCat := ()
        for $child in $node/node
          let $childRel := data($child/@rel)
          (: use children's cat or pt attribute, default to '' :)
          let $childCat := data($child/(@cat, @pt, '')[1])
          (: concatenate childrenRelCat sequence (append to list) :)
          let $childrenRelCat := ($childrenRelCat, 
                                  string-join(($childRel, $childCat), '%'))

        let $bf := string-join(functx:sort($childrenRelCat), '_')
        let $sent := <tree id="{$sentenceId}">{$node}</tree>

        let $dir := concat($root, $ds, $catTop)
        (: this if-clause throws an error: missing return statement, 
        incomplete FWLOR:)
        if (file:exists($dir) and file:is-dir($dir))
          then ()
        else file:create-dir($dir)        

        (: append subtree to pattern-file :)
        file:append($dir || $bf || '-index.xml', $sent)
        (: doesn't have to return anything, but FWLOR demands it... :)
        return $f

Кажется, я упускаю что-то важное в том, как XQuery оценивает выражения или ожидает, что они будут упорядочены. Что не так в коде выше?

(Этот вопрос называется почти идентично, но приведенный там ответ не помогает, так как там была еще одна ошибка в коде этого ОП.)


person Bram Vanroy    schedule 15.04.2018    source источник
comment
За let должен следовать return. Я не уверен, чего вы хотите добиться с помощью всего выражения, как return 5 относится к созданию файла?   -  person Martin Honnen    schedule 15.04.2018
comment
@MartinHonnen Возврат был примером. Я часто оказываюсь в местах, откуда мне нечего возвращать. Если за let следует возврат, возврат выходит из функции, так как же я когда-нибудь достигну возврата? И есть ли способ сделать pythonic return None, когда вы не хотите ничего возвращать?   -  person Bram Vanroy    schedule 16.04.2018
comment
Будет ли let $dir := 'hello/world' return if (file:exists($dir) and file:is-dir($dir)) then () else file:create-dir($dir) делать то, что вы хотите, например. разрешить вам сохранить имя файла, а затем использовать его в выражении if? Я до сих пор не уверен, что вы хотите делать с return 5 (и я не проверял, возвращает ли что-то file:create-dir).   -  person Martin Honnen    schedule 16.04.2018
comment
@MartinHonnen Я заменил фиктивный код своим настоящим кодом. Я добавил комментарии, чтобы немного объяснить, что происходит. Надеюсь, это понятнее?   -  person Bram Vanroy    schedule 16.04.2018
comment
Теперь в последней строке есть return $file, хотя эта переменная никогда не объявлялась и не была привязана к значению. В общем, проблема в том, что let всегда должен быть частью w3. .org/TR/xquery-31/#doc-xquery31-FLWORExpr с выражением return и if не может быть начальным или промежуточным предложением такого FLOWRExpr, оно может быть только в предложении return или использоваться внутри из let (например, let $foo := if (...) then ... else ...) или for (например, for $bar in (if (...) then ... else ...)).   -  person Martin Honnen    schedule 16.04.2018
comment
@MartinHonnen Ой, мой плохой. Отредактировано на $f. Не странная конструкция? Я не привык к XQuery, и это меня очень сбивает с толку. Но принуждение к использованию объявлений переменных не должно быть очень эффективным с точки зрения использования памяти, верно? Как бы вы переписали мое if-оператор? Я не вижу разумного способа включить оператор в return или let-декларацию.   -  person Bram Vanroy    schedule 16.04.2018
comment
Я просто пытался объяснить на этих примерах, где выражение if может появиться, а где нет, и каковы правила построения выражения FLOWR. Что касается переписывания кода разрешенным способом, я думаю, что один из способов будет let $dir := concat($root, $ds, $catTop) return (if (file:exists($dir) and file:is-dir($dir)) then () else file:create-dir($dir), file:append($dir || $bf || '-index.xml', $sent)).   -  person Martin Honnen    schedule 16.04.2018


Ответы (1)


Я думаю, что настоящая проблема здесь в том, что вы используете выражение if

if (file:exists($dir) and file:is-dir($dir))
          then () else file:create-dir($dir) 

для достижения побочных эффектов, а не для того, чтобы вернуть результат. Хотя внешние функции, такие как file:create-dir(), могут иметь побочные эффекты, на самом деле XQuery не предназначен для работы, и поэтому вам нужно быть очень осторожным при использовании таких внешних функций. Мало того, подробные правила о том, что работает, а что нет, могут различаться от одного процессора XQuery к другому.

Кроме того, конечно, ваш запрос должен удовлетворять грамматике, а грамматика выражения FLWOR говорит, что оно состоит из последовательности предложений, каждое из которых является for, let, where, order-by, .... или оговорка о возврате. Так что ваше выражение "если" неуместно.

Я не знаю BaseX, но я думаю, что следующее будет работать:

let $dir := concat($root, $ds, $catTop)
return (
        if (file:exists($dir) and file:is-dir($dir))
          then ()
          else file:create-dir($dir),        
        file:append($dir || $bf || '-index.xml', $sent),
        $f
)  
person Michael Kay    schedule 16.04.2018
comment
Я могу подтвердить, что предложенный код будет работать в BaseX. Побочные и недетерминированные функции никогда не будут оптимизированы. В данном запросе может быть даже достаточно избавиться от выражения if и всегда вызывать file:create-dir (если каталог существует, ничего не произойдет; если это файл, а не каталог, вы все равно получите ошибку). - person Christian Grün; 17.04.2018