Re: Maps in exslt2?

John Snelson <[email protected]> Wed, 17 Mar 2010 12:57:14 +0000
Newsgroups gmane.text.xml.xslt.extensions
Message-ID <[email protected]>
This is a multi-part message in MIME format.
--------------010705080306050302090202
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Vladimir Nesterovsky wrote:
>> I think it's really important to get some good data structures into 
>> XQuery and XSLT 2.0. I've already written a read/black tree 
>> implementation in pure XQuery 1.1 (using 1st class function closures) 
>> which I was planning to release via EXPath when appropriate.
> 
> That's good! Can I look at implementation?

Sure - attached.

> Technically, neither RB nor AVL trees need to know of key/value separation 
> (they work with items). Key appears during search operation only.
> 
> I've prefered to implement AVL tree as it allows to build a map with access 
> by index, and by key at the same time.

I've never needed to access a sorted map by index. I don't see what 
you'd need that for except iterating the members of the map - and that's 
definitely better done in a different way.

John

-- 
John Snelson, Oracle Corporation               http://twitter.com/jpcs
Berkeley DB XML:            http://oracle.com/database/berkeley-db/xml
XQilla:                                  http://xqilla.sourceforge.net

--------------010705080306050302090202
Content-Type: text/plain;
 name="rbtree.xq"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="rbtree.xq"

(: Copyright (c) 2010 John Snelson :)

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

module namespace rbtree = "http://snelson.org.uk/functions/rbtree";
declare default function namespace "http://snelson.org.uk/functions/rbtree";

(: declare type rbtree() as function() as (item(), rbtree(), rbtree(), xs:boolean)?; :)

declare function create() as function() as item()*
{
  function() { () }
};

declare private function create(
  $x as item(),
  $a as function() as item()*,
  $b as function() as item()*,
  $isred as xs:boolean
) as function() as item()+
{
  function() { $x, $a, $b, $isred }
};

declare function empty($tree as function() as item()*) as xs:boolean
{
  fn:empty($tree())
};

declare private function value($tree as function() as item()*) as item()
{
  $tree()[1]
};

declare private function left($tree as function() as item()*) as function() as item()*
{
  $tree()[2]
};

declare private function right($tree as function() as item()*) as function() as item()*
{
  $tree()[3]
};

declare private function isred($tree as function() as item()*) as xs:boolean
{
  $tree()[4]
};

declare function contains(
  $lessthan as function(item(), item()) as xs:boolean,
  $tree as function() as item()*,
  $x as item()
) as xs:boolean
{
  fn:exists(get($lessthan, $tree, $x))
};

declare function get(
  $lessthan as function(item(), item()) as xs:boolean,
  $tree as function() as item()*,
  $x as item()
) as item()?
{
  find_gte($lessthan, $tree, $x)[fn:not($lessthan($x, .))]
};

declare function find_gte(
  $lessthan as function(item(), item()) as xs:boolean,
  $tree as function() as item()*,
  $x as item()
) as item()?
{
  if(empty($tree)) then () else

  let $y := value($tree)
  return

  if($lessthan($y, $x)) then find_gte($lessthan, right($tree), $x)
  else (find_gte($lessthan, left($tree), $x), $y)[1]
};

declare function fold(
  $f as function(item()*, item()) as item()*,
  $z as item()*,
  $tree as function() as item()*
) as item()*
{
  if(empty($tree)) then $z else

  fold($f,
    $f(fold($f, $z, left($tree)), value($tree)),
    right($tree))
};

declare function insert(
  $lessthan as function(item(), item()) as xs:boolean,
  $tree as function() as item()*,
  $x as item()
) as function() as item()+
{
  let $result := insert_helper($lessthan, $tree, $x)
  return

  if(empty(tail($result))) then $result else

  let $rb := $result[1]
  return

  create(value($rb), left($rb), right($rb), fn:false())
};

declare private function insert_helper(
  $lessthan as function(item(), item()) as xs:boolean,
  $tree as function() as item()*,
  $x as item()
) as item()+
{
  if(empty($tree)) then create($x, create(), create(), fn:true()) else

  let $y := value($tree)
  let $a := left($tree)
  let $b := right($tree)
  let $isred := isred($tree)
  return

  if($lessthan($x, $y)) then (
    let $ins := insert_helper($lessthan, $a, $x)
    let $a_ := $ins[1]
    return

    if(isred($a_)) then
      if(empty(tail($ins))) then
        (create($y, $a_, $b, $isred), "l")
      else if($ins[2] = "l") then
        let $gc := left($a_)
        return
        balance(value($gc), value($a_), $y,
          left($gc), right($gc), right($a_), $b)
      else
        let $gc := right($a_)
        return
        balance(value($a_), value($gc), $y,
          left($a_), left($gc), right($gc), $b)
    else
      create($y, $a_, $b, $isred)
  )
  else if($lessthan($y, $x)) then (
    let $ins := insert_helper($lessthan, $b, $x)
    let $b_ := $ins[1]
    return

    if(isred($b_)) then (
      if(empty(tail($ins))) then
        (create($y, $a, $b_, $isred), "r")
      else if($ins[2] = "l") then
        let $gc := left($b_)
        return
        balance($y, value($gc), value($b_),
          $a, left($gc), right($gc), right($b_))
      else
        let $gc := right($b_)
        return
        balance($y, value($b_), value($gc),
          $a, left($b_), left($gc), right($gc))
    ) else
      create($y, $a, $b_, $isred)
  )
  else create($x, $a, $b, $isred)
};

declare private function balance(
  $x as item(),
  $y as item(),
  $z as item(),
  $a as function() as item()*,
  $b as function() as item()*,
  $c as function() as item()*,
  $d as function() as item()*
) as function() as item()+
{
  create(
    $y,
    create($x, $a, $b, fn:false()),
    create($z, $c, $d, fn:false()),
    fn:true()
  )
};

--------------010705080306050302090202
Content-Type: text/plain;
 name="map.xq"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="map.xq"

(: Copyright (c) 2010 John Snelson :)

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

module namespace map = "http://snelson.org.uk/functions/map";
declare default function namespace "http://snelson.org.uk/functions/map";
import module namespace rbtree = "http://snelson.org.uk/functions/rbtree" at "rbtree.xq";

declare private function pair($key as item(), $value as item()*)
  as function() as item()+
{
  function() { $key, $value }
};

declare private function key($pair as function() as item()+) as item()
{
  $pair()[1]
};

declare private function value($pair as function() as item()+) as item()*
{
  fn:subsequence($pair(), 2)
};

declare private function lt($a, $b)
{
  key($a) < key($b)
};

declare function put(
  $map as function() as item()*?,
  $key as item(),
  $value as item()*
) as (function() as item()+)+
{
  let $map := if(fn:empty($map)) then rbtree:create() else $map
  let $pair := pair($key, $value)
  return
    rbtree:insert(lt#2, $map, $pair)
};

declare function get($map as function() as item()*?, $key as item())
  as item()*
{
  if(fn:empty($map)) then () else

  value(rbtree:get(lt#2, $map, pair($key, ())))
};

declare function contains($map as function() as item()*?, $key as item())
  as xs:boolean
{
  if(fn:empty($map)) then fn:false() else

  rbtree:contains(lt#2, $map, pair($key, ()))
};

declare function fold(
  $f as function(item()*, item(), item()*) as item()*,
  $z as item()*,
  $map as function() as item()*?
) as item()*
{
  rbtree:fold(
    function($result, $pair) {
      $f($result, key($pair), value($pair))
    },
    $z, $map)
};

--------------010705080306050302090202
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
exslt mailing list
[email protected]
http://www.exslt.org/list
--------------010705080306050302090202--