2016-04-28 7 views
0

XQueryУдаление последовательных номеров из последовательности в XQuery

Входной: (1,2,3,4,5,6,7,14,15,16,17,24,25,26 , 27,28)

Выход: (1,7,14,17,24,28)

Я попытался удалить последовательные номера из последовательности ввода с помощью функции XQuery, но не делать это

xquery version "1.0" encoding "utf-8"; 

    declare namespace ns1="http://www.somenamespace.org/types"; 

    declare variable $request as xs:integer* external; 

    declare function local:func($reqSequence as xs:integer*) as xs:integer* { 

    let $nonRepeatSeq := for $count in (1 to count($reqSequence)) return 
          if ($reqSequence[$count+1] - $reqSequence) then 
          remove($reqSequence,$count+1) 
          else() 
    return 
    $nonRepeatSeq 
    }; 

    local:func((1,2,3,4,5,6,7,14,15,16,17,24,25,26,27,28)) 

Просьба предложить, как это сделать в функциональном языке XQuery.

+2

Всегда обеспечивайте _specific проблемы statement_, описать, где вы застряли. «failed» не является разумным описанием проблемы, вы получаете сообщение об ошибке, вы получаете неправильный вывод, ...? –

+0

Простой застрял в функциональной части без гражданства .. :) –

ответ

3

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

Сначала просто перебирайте значения и выберите (a) первое значение, (b) любое значение, которое не больше, чем его предшественник, и (c) любое значение, которое не меньше его преемника. [OP указывает, что последнее значение также необходимо включить; оставил упражнение для читателя. Или см. Ответ Майкла Кей, в котором приведена формулировка фильтра; Закон де Моргана ударяет снова]

let $vseq := (1,2,3,4,5,6,7,14,15,16,17,24,25,26,27,28) 
for $v at $pos in $vseq 
return if ($pos eq 1 
      or $vseq[$pos - 1] ne $v - 1 
      or $vseq[$pos + 1] ne $v + 1) 
     then $v 
     else() 

Или, во-вторых, делать примерно то же самое в выражении фильтра:

let $vseq := (1,2,3,4,5,6,7,14,15,16,17,24,25,26,27,28) 
return $vseq[ 
    for $i in position() return 
     $i eq 1 
     or . ne $vseq[$i - 1] + 1 
     or . ne $vseq[$i + 1] - 1] 

Основное различие между этими двумя способами выполнения вычисления и вашего нерабочими попытка состоит в том, что они ничего не говорят об изменении или изменении последовательности; они просто задают новую последовательность. Используя выражение фильтра, вторая формулировка ясно показывает, что результатом будет подпоследовательность $ vseq; выражение for вообще не дает такой гарантии (хотя, поскольку для каждого значения оно возвращает либо пустую последовательность, либо само значение, мы можем видеть, что здесь тоже результат будет подпоследовательностью: копией $ vseq, из которой были получены некоторые значения опущено.

Многие программисты трудно перестать думать в терминах присваивания переменным или модификации структуры данных, но его стоит некоторых усилий.

[Приложение] Я может быть видом что-то, но я не см. способ выражения этого вычисления в чистом XPath 2.0, поскольку XPath 2.0, похоже, не имеет какого-либо механизма, который может связывать переменную типа $vseq с не-одиночной последовательностью значений (XPath 3.0 имеет выражения let, так что это не проблема. Вторая формулировка выше самая чистая XPath 3.0)

+0

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

+0

Однако просто добавлено последнее число в последовательность –

+0

Майкл, вы может сделать это в XPath 2.0, если входная последовательность поставляется снаружи в качестве параметра или если она фактически получена из содержимого XML-документа: см. мое решение. –

0

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

declare function local:non-consec(
    $prev as xs:integer?, 
    $rest as xs:integer* 
) as xs:integer* 
{ 
    if (empty($rest)) then() 
    else 
    let $curr := head($rest) 
    let $next := subsequence($rest, 2, 1) 
    return (
     if ($prev eq $curr - 1 and $curr eq $next - 1) 
     then() (: This number is part of a consecutive sequence :) 
     else $curr, 
     local:non-consec(head($rest), tail($rest)) 
    ) 
}; 

local:non-consec((), (1,2,3,4,5,6,7,14,15,16,17,24,25,26,27,28)) 
=> 
1 
7 
14 
17 
24 
28 
+0

Использование версии XQuery 1, где функции главы и хвоста не реализованы внутренне –

+0

@KaushikBose Для совместимости с XQuery 1 вы можете просто заменить 'head ($ rest)' на '$ rest [1] 'и' tail ($ rest) 'с' подпоследовательность ($ rest, 2) '. – wst

2

В XSLT это может быть сделано, как:.

<xsl:for-each-group select="$in" group-adjacent=". - position()"> 
    <xsl:sequence select="current-group()[1], current-group()[last()]"/> 
</xsl:for-each-group> 

В XQuery 3.0 вы можете сделать это с акробатикой окна, но я слишком ленив, чтобы работать деталь.

XPath 2.0 решение (при условии, последовательность ввода в $in) является:

for $i in 1 to count($in) 
return $in[$i][not(. eq $in[$i - 1]+1 and . eq $in[$i+1]-1)]