Tree datastructures

You are encouraged to You are encouraged to solve this task according to the task description, using any language you may know.

The following shows a tree of data with nesting denoted by visual levels of indentation:

RosettaCode rocks code comparison wiki mocks trolling

A common datastructure for trees is to define node structures having a name and a, (possibly empty), list of child nodes. The nesting of nodes captures the indentation of the tree. Lets call this the nest form.

# E.g. if child nodes are surrounded by brackets # and separated by commas then: RosettaCode(rocks(code, ...), ...) # But only an _example_

Another datastructure for trees is to construct from the root an ordered list of the nodes level of indentation and the name of that node. The indentation for the root node is zero; node 'rocks is indented by one level from the left, and so on. Lets call this the indent form.

0 RosettaCode 1 rocks 2 code ...

Task

Create/use a nest datastructure format and textual representation for arbitrary trees. Create/use an indent datastructure format and textual representation for arbitrary trees. Create methods/classes/proceedures/routines etc to: Change from a nest tree datastructure to an indent one. Change from an indent tree datastructure to a nest one Use the above to encode the example at the start into the nest format, and show it. transform the initial nest format to indent format and show it. transform the indent format to final nest format and show it. Compare initial and final nest formats which should be the same.

Note

It's all about showing aspects of the contrasting datastructures as they hold the tree.

Comparing nested datastructures is secondary - saving formatted output as a string then a string compare would suffice for this task, if its easier.





Show all output on this page.

#include <iomanip>

#include <iostream>

#include <string>

#include <vector>

#include <utility>

#include <vector>



class nest_tree ;



bool operator == ( const nest_tree & , const nest_tree & ) ;



class nest_tree {

public :

explicit nest_tree ( const std :: string & name ) : name_ ( name ) { }

nest_tree & add_child ( const std :: string & name ) {

children_. emplace_back ( name ) ;

return children_. back ( ) ;

}

void print ( std :: ostream & out ) const {

print ( out, 0 ) ;

}

const std :: string & name ( ) const {

return name_ ;

}

const std :: vector < nest_tree > & children ( ) const {

return children_ ;

}

bool equals ( const nest_tree & n ) const {

return name_ == n. name_ && children_ == n. children_ ;

}

private :

void print ( std :: ostream & out, int level ) const {

std :: string indent ( level * 4 , ' ' ) ;

out << indent << name_ << '

' ;

for ( const nest_tree & child : children_ )

child. print ( out, level + 1 ) ;

}

std :: string name_ ;

std :: vector < nest_tree > children_ ;

} ;



bool operator == ( const nest_tree & a, const nest_tree & b ) {

return a. equals ( b ) ;

}



class indent_tree {

public :

explicit indent_tree ( const nest_tree & n ) {

items_. emplace_back ( 0 , n. name ( ) ) ;

from_nest ( n, 0 ) ;

}

void print ( std :: ostream & out ) const {

for ( const auto & item : items_ )

std :: cout << item. first << ' ' << item. second << '

' ;

}

nest_tree to_nest ( ) const {

nest_tree n ( items_ [ 0 ] . second ) ;

to_nest_ ( n, 1 , 0 ) ;

return n ;

}

private :

void from_nest ( const nest_tree & n, int level ) {

for ( const nest_tree & child : n. children ( ) ) {

items_. emplace_back ( level + 1 , child. name ( ) ) ;

from_nest ( child, level + 1 ) ;

}

}

size_t to_nest_ ( nest_tree & n, size_t pos, int level ) const {

while ( pos < items_. size ( ) && items_ [ pos ] . first == level + 1 ) {

nest_tree & child = n. add_child ( items_ [ pos ] . second ) ;

pos = to_nest_ ( child, pos + 1 , level + 1 ) ;

}

return pos ;

}

std :: vector < std :: pair < int , std :: string >> items_ ;

} ;



int main ( ) {

nest_tree n ( "RosettaCode" ) ;

auto & child1 = n. add_child ( "rocks" ) ;

child1. add_child ( "code" ) ;

child1. add_child ( "comparison" ) ;

child1. add_child ( "wiki" ) ;

auto & child2 = n. add_child ( "mocks" ) ;

child2. add_child ( "trolling" ) ;



std :: cout << "Initial nest format:

" ;

n. print ( std :: cout ) ;



indent_tree i ( n ) ;

std :: cout << "

Indent format:

" ;

i. print ( std :: cout ) ;



nest_tree n2 ( i. to_nest ( ) ) ;

std :: cout << "

Final nest format:

" ;

n2. print ( std :: cout ) ;



std :: cout << "

Are initial and final nest formats equal? "

<< std :: boolalpha << n. equals ( n2 ) << '

' ;



return 0 ;

}

Output:

Initial nest format: RosettaCode rocks code comparison wiki mocks trolling Indent format: 0 RosettaCode 1 rocks 2 code 2 comparison 2 wiki 1 mocks 2 trolling Final nest format: RosettaCode rocks code comparison wiki mocks trolling Are initial and final nest formats equal? true

package main



import (

"fmt"

"io"

"os"

"strings"

)



type nNode struct {

name string

children [] nNode

}



type iNode struct {

level int

name string

}



func printNest ( n nNode , level int , w io. Writer ) {

if level == 0 {

fmt . Fprintln ( w , "

==Nest form==

" )

}

fmt . Fprintf ( w , "%s%s

" , strings . Repeat ( " " , level ), n . name )

for _ , c := range n . children {

fmt . Fprintf ( w , "%s" , strings . Repeat ( " " , level + 1 ))

printNest ( c , level + 1 , w )

}

}



func toNest ( iNodes [] iNode , start , level int , n * nNode ) {

if level == 0 {

n . name = iNodes [ 0 ] . name

}

for i := start + 1 ; i < len ( iNodes ); i ++ {

if iNodes [ i ] . level == level + 1 {

c := nNode { iNodes [ i ] . name , nil }

toNest ( iNodes , i , level + 1 , &c )

n . children = append ( n . children , c )

} else if iNodes [ i ] . level < = level {

return

}

}

}



func printIndent ( iNodes [] iNode , w io. Writer ) {

fmt . Fprintln ( w , "

==Indent form==

" )

for _ , n := range iNodes {

fmt . Fprintf ( w , "%d %s

" , n . level , n . name )

}

}



func toIndent ( n nNode , level int , iNodes * [] iNode ) {

* iNodes = append ( * iNodes , iNode { level , n . name })

for _ , c := range n . children {

toIndent ( c , level + 1 , iNodes )

}

}



func main () {

n1 := nNode { "RosettaCode" , nil }

n2 := nNode { "rocks" , [] nNode {{ "code" , nil }, { "comparison" , nil }, { "wiki" , nil }}}

n3 := nNode { "mocks" , [] nNode {{ "trolling" , nil }}}

n1 . children = append ( n1 . children , n2 , n3 )



var sb strings . Builder

printNest ( n1 , 0 , &sb )

s1 := sb . String ()

fmt . Print ( s1 )



var iNodes [] iNode

toIndent ( n1 , 0 , &iNodes )

printIndent ( iNodes , os . Stdout )



var n nNode

toNest ( iNodes , 0 , 0 , &n )

sb . Reset ()

printNest ( n , 0 , &sb )

s2 := sb . String ()

fmt . Print ( s2 )



fmt . Println ( "

Round trip test satisfied? " , s1 == s2 )

}

Output:

==Nest form== RosettaCode rocks code comparison wiki mocks trolling ==Indent form== 0 RosettaCode 1 rocks 2 code 2 comparison 2 wiki 1 mocks 2 trolling ==Nest form== RosettaCode rocks code comparison wiki mocks trolling Round trip test satisfied? true

const nesttext = """

RosettaCode

rocks

code

comparison

wiki

mocks

trolling

"""



function nesttoindent(txt)

ret = ""

windent = gcd(length.([x.match for x in eachmatch(r"\s+", txt)]) .- 1)

for lin in split(txt, "

")

ret *= isempty(lin) ? "

" : isspace(lin[1]) ?

replace(lin, r"\s+" => (s) -> "$(length(s)÷windent) ") * "

" :

"0 " * lin * "

"

end

return ret, " "^windent

end



function indenttonest(txt, indenttext)

ret = ""

for lin in filter(x -> length(x) > 1, split(txt, "

"))

(num, name) = split(lin, r"\s+", limit=2)

indentnum = parse(Int, num)

ret *= indentnum == 0 ? name * "

" : indenttext^indentnum * name * "

"

end

return ret

end



indenttext, itext = nesttoindent(nesttext)

restorednesttext = indenttonest(indenttext, itext)



println("Original:

", nesttext, "

")

println("Indent form:

", indenttext, "

")

println("Back to nest form:

", restorednesttext, "

")

println("original == restored: ", strip(nesttext) == strip(restorednesttext))



Output:

Original: RosettaCode rocks code comparison wiki mocks trolling Indent form: 0 RosettaCode 1 rocks 2 code 2 comparison 2 wiki 1 mocks 2 trolling Back to nest form: RosettaCode rocks code comparison wiki mocks trolling original == restored: true

Translation of: Raku

use strict ;

use warnings ;

use feature 'say' ;

use JSON ;

use Data :: Printer ;



my $trees = <<~ END ;

RosettaCode

encourages

code

diversity

comparison

discourages

golfing

trolling

emphasising execution speed

code - golf . io

encourages

golfing

discourages

comparison

END



my $level = ' ' ;

sub nested_to_indent { shift =~ s #^($level*)# ($1 ? length($1)/length $level : 0) . ' ' #egmr }

sub indent_to_nested { shift =~ s #^(\d+)\s*# $level x $1 #egmr }



say my $indent = nested_to_indent $trees ;

my $nest = indent_to_nested $indent ;



use Test :: More ;

is ( $trees , $nest , 'Round-trip' ) ;

done_testing ( ) ;



# Import outline paragraph into native data structure

sub import {

my ( $trees ) = @_ ;

my $level = ' ' ;

my $forest ;

my $last = - 999 ;



for my $branch ( split /

/ , $trees ) {

$branch =~ m/(($level*))*/ ;

my $this = $1 ? length ( $1 ) / length ( $level ) : 0 ;

$forest .= do {

if ( $this gt $last ) { '[' . trim_and_quote ( $branch ) }

elsif ( $this lt $last ) { ']' x ( $last - $this ) . ',' . trim_and_quote ( $branch ) }

else { trim_and_quote ( $branch ) }

} ;

$last = $this ;

}

sub trim_and_quote { shift =~ s /^ \ s * ( .* \S ) \ s * $/ "$1" ,/ r }



eval $forest . ']' x ( 1 + $last ) ;

}



my $forest = import $trees ;

say "Native data structure:

" . np $forest ;

say "

JSON:

" . encode_json ( $forest ) ;

Output:

RosettaCode 1 encourages 2 code 3 diversity 3 comparison 1 discourages 2 golfing 2 trolling 2 emphasising execution speed code-golf.io 1 encourages 2 golfing 1 discourages 2 comparison ok 1 - Round-trip 1..1 Native data structure: \ [ [0] "RosettaCode", [1] [ [0] "encourages", [1] [ [0] "code", [1] [ [0] "diversity", [1] "comparison" ] ], [2] "discourages", [3] [ [0] "golfing", [1] "trolling", [2] "emphasising execution speed" ] ], [2] "code-golf.io", [3] [ [0] "encourages", [1] [ [0] "golfing" ], [2] "discourages", [3] [ [0] "comparison" ] ] ] JSON: ["RosettaCode",["encourages",["code",["diversity","comparison"]],"discourages",["golfing","trolling","emphasising execution speed"]],"code-golf.io",["encourages",["golfing"],"discourages",["comparison"]]]

The standard Phix sequence is perfect for handling all of these kinds of structures.

function text_to_indent(string plain_text)

sequence lines = split(plain_text,"

",no_empty:=true),

parents = {}

for i=1 to length(lines) do

string line = trim_tail(lines[i]),

text = trim_head(line)

integer indent = length(line)-length(text)

-- remove any completed parents

while length(parents) and indent<=parents[$] do

parents = parents[1..$-1]

end while

-- append potential new parent

parents &= indent

integer depth = length(parents)

lines[i] = {depth,text}

end for

return lines

end function



function indent_to_nested(sequence indent, integer idx=1, level=1)

sequence res = {}

while idx<=length(indent) do

{integer lvl, string text} = indent[idx]

if lvl<level then exit end if

{sequence children,idx} = indent_to_nested(indent,idx+1,level+1)

res = append(res,{text,children})

end while

return {res,idx}

end function



function nested_to_indent(sequence nested, integer level=1)

sequence res = {}

for i=1 to length(nested) do

{string text, sequence children} = nested[i]

res = append(res,{level,text})

res &= nested_to_indent(children,level+1)

end for

return res

end function



constant text = """

RosettaCode

encourages

code

diversity

comparison

discourages

emphasising execution speed

code-golf.io

encourages

golfing

discourages

comparison"""



sequence indent = text_to_indent(text),

nested = indent_to_nested(indent)[1],

n2ichk = nested_to_indent(nested)



puts(1,"Indent form:

")

pp(indent,{pp_Nest,1})

puts(1,"

Nested form:

")

pp(nested,{pp_Nest,8})

printf(1,"

Nested to indent:%s

",{iff(n2ichk==indent?"same":"***ERROR***")})

Output:

Indent form: {{1, `RosettaCode`}, {2, `encourages`}, {3, `code`}, {4, `diversity`}, {4, `comparison`}, {2, `discourages`}, {3, `emphasising execution speed`}, {1, `code-golf.io`}, {2, `encourages`}, {3, `golfing`}, {2, `discourages`}, {3, `comparison`}} Nested form: {{`RosettaCode`, {{`encourages`, {{`code`, {{`diversity`, {}}, {`comparison`, {}}}}}}, {`discourages`, {{`emphasising execution speed`, {}}}}}}, {`code-golf.io`, {{`encourages`, {{`golfing`, {}}}}, {`discourages`, {{`comparison`, {}}}}}}} Nested to indent:same

You can also strictly enforce these structures, which is obviously useful for debugging.

Admittedly this is somewhat more tedious, but at the same time infinitely more flexible and powerful than a "plain old struct".

type indent_struct(object o)

if sequence(o) then

for i=1 to length(o) do

object oi = o[i]

if not sequence(oi)

or length(oi)!=2

or not integer(oi[1])

or not string(oi[2]) then

return false

end if

end for

return true

end if

return false

end type



type nested_struct(object o)

if sequence(o) then

for i=1 to length(o) do

object oi = o[i]

if not sequence(oi)

or length(oi)!=2

or not string(oi[1])

or not nested_struct(oi[2]) then

return false

end if

end for

return true

end if

return false

end type



-- and as above except:

function indent_to_nested(indent_struct indent, integer idx=1, level=1)

function nested_to_indent(nested_struct nested, integer level=1)

-- also make the output sequences better typed:

indent_struct indent = text_to_indent(text)

nested_struct nested = indent_to_nested(indent)[1]

indent_struct r2ichk = nested_to_indent(nested)

Just arranges the standard lists and tuples for the datastructures allowing pprint to show the different arrangement of storage.

from pprint import pprint as pp



def to_indent ( node , depth = 0 , flat = None ) :

if flat is None :

flat = [ ]

if node:

flat. append ( ( depth , node [ 0 ] ) )

for child in node [ 1 ] :

to_indent ( child , depth + 1 , flat )

return flat



def to_nest ( lst , depth = 0 , level = None ) :

if level is None :

level = [ ]

while lst:

d , name = lst [ 0 ]

if d == depth:

children = [ ]

level. append ( ( name , children ) )

lst. pop ( 0 )

elif d > depth: # down

to_nest ( lst , d , children )

elif d < depth: # up

return

return level [ 0 ] if level else None



if __name__ == '__main__' :

print ( 'Start Nest format:' )

nest = ( 'RosettaCode' , [ ( 'rocks' , [ ( 'code' , [ ] ) , ( 'comparison' , [ ] ) , ( 'wiki' , [ ] ) ] ) ,

( 'mocks' , [ ( 'trolling' , [ ] ) ] ) ] )

pp ( nest , width = 25 )



print ( '

... To Indent format:' )

as_ind = to_indent ( nest )

pp ( as_ind , width = 25 )



print ( '

... To Nest format:' )

as_nest = to_nest ( as_ind )

pp ( as_nest , width = 25 )



if nest != as_nest:

print ( "Whoops round-trip issues" )

Output:

Start Nest format: ('RosettaCode', [('rocks', [('code', []), ('comparison', []), ('wiki', [])]), ('mocks', [('trolling', [])])]) ... To Indent format: [(0, 'RosettaCode'), (1, 'rocks'), (2, 'code'), (2, 'comparison'), (2, 'wiki'), (1, 'mocks'), (2, 'trolling')] ... To Nest format: ('RosettaCode', [('rocks', [('code', []), ('comparison', []), ('wiki', [])]), ('mocks', [('trolling', [])])])

(formerly Perl 6)

Works with: Rakudo version 2020.08.1

Code golf is a entertaining passtime, even if it isn't appropriate for this site. To a large extent, I agree with Hout, I am not really on board with mocking anybody, especially espousing it as an official RosettaCode position. So, feel free to mark this incorrect.

#`(

Sort of vague as to what we are trying to accomplish here . If we are just

trying to transform from one format to another , probably easiest to just

perform string manipulations .

)



my $level = ' ' ;



my $trees = q : to / END /;

RosettaCode

encourages

code

diversity

comparison

discourages

golfing

trolling

emphasising execution speed

code - golf . io

encourages

golfing

discourages

comparison

END



sub nested - to - indent { $^str . subst : / ^^ ($($level))* / , -> $ / { "{+$0} " } , : g }

sub indent - to - nested { $^str . subst : / ^^ (\d+) \s* / , -> $ / { "{$level x +$0}" } , : g }



say $trees ;

say my $indent = $trees . &nested - to - indent ;

say my $nest = $indent . &indent - to - nested ;



use Test ;

is ( $trees , $nest , 'Round-trip equals original' ) ;



#`(

If , on the other hand , we want perform more complex transformations ; better to

load it into a native data structure which will then allow us to manipulate it

however we like .

)



# Import outline paragraph into native data structure

sub import ( Str $trees , $level = ' ' ) {

my $forest ;

my $last = - Inf ;



for $trees . lines -> $branch {

$branch ~~ / ($($level))* / ;

my $this = + $0 ;

$forest ~= do {

given $this cmp $last {

when More { " \[ '{esc $branch.trim}', " }

when Same { "'{esc $branch.trim}', " }

when Less { "{']' x $last - $this}, '{esc $branch.trim}', " }

}

}

$last = $this ;

}



sub esc { $^s . subst ( /(<['\\]>)/ , -> $ / { " \\ $0" } , : g ) }



$forest ~= ']' x 1 + $last ;

$forest . EVAL ;

}



my $forest = import $trees ;



say "

Native data structure:

" , $forest . raku ;



{

use JSON :: Fast ;

say "

JSON:

" , $forest . &to - json ;

}



{

use YAML ;

say "

YAML:

" , $forest . &dump ;

}

Output:

RosettaCode encourages code diversity comparison discourages golfing trolling emphasising execution speed code-golf.io encourages golfing discourages comparison 0 RosettaCode 1 encourages 2 code 3 diversity 3 comparison 1 discourages 2 golfing 2 trolling 2 emphasising execution speed 0 code-golf.io 1 encourages 2 golfing 1 discourages 2 comparison RosettaCode encourages code diversity comparison discourages golfing trolling emphasising execution speed code-golf.io encourages golfing discourages comparison ok 1 - Round-trip equals original Native data structure: $["RosettaCode", ["encourages", ["code", ["diversity", "comparison"]], "discourages", ["golfing", "trolling", "emphasising execution speed"]], "code-golf.io", ["encourages", ["golfing"], "discourages", ["comparison"]]] JSON: [ "RosettaCode", [ "encourages", [ "code", [ "diversity", "comparison" ] ], "discourages", [ "golfing", "trolling", "emphasising execution speed" ] ], "code-golf.io", [ "encourages", [ "golfing" ], "discourages", [ "comparison" ] ] ] YAML: --- - RosettaCode - - encourages - - code - - diversity - comparison - discourages - - golfing - trolling - emphasising execution speed - code-golf.io - - encourages - - golfing - discourages - - comparison ...

fcn nestToIndent(nestTree){

fcn(out,node,level){

out.append(List(level,node[0])); // (n,name) or ("..",name)

if(node.len()>1){ // (name children), (name, (tree))

level+=1;

foreach child in (node[1,*]){

if(String.isType(child)) out.append(List(level,child));

else self.fcn(out,child,level)

}

}

out

}(List(),nestTree,0)

}

fcn nestToString(nestTree,dot="."){

fcn(out,dot,node,level){

out.writeln(dot*level,node[0]); // (name)

if(node.len()>1){ // (name children), (name, (tree))

level+=1;

foreach child in (node[1,*]){

if(String.isType(child)) out.writeln(dot*level,child);

else self.fcn(out,dot,child,level)

}

}

out

}(Data(),dot,nestTree,0).text

}



fcn indentToNest(iTree,depth=0,nTree=List()){

while(iTree){ // (n,name)

d, name := iTree[0];

if(d==depth){

nTree.append(name);

iTree.pop(0);

}

else if(d>depth){ // assume can't skip levels down

if(nTree.len()>1 and not List.isType((nm:=nTree[-1]))){

nTree[-1]=(children:=List(nm));

indentToNest(iTree,d,children);

}else{

nTree.append(children:=List(name));

iTree.pop(0);

indentToNest(iTree,d+1,children);

}

}

else break; // d<depth

}

return(nTree)

}

fcn indentToString(indentTree){ indentTree.apply("concat"," ").concat("

") }

tree:=L("RosettaCode",

L("rocks","code","comparison","wiki"),

L("mocks","golfing") );



println("Nest tree internal format:

",tree.toString(*,*));

println("Formated:

",nestToString(tree));



indentTree:=nestToIndent(tree);

println("To indent format:

",indentToString(indentTree));



nestTree:=indentToNest(indentTree);

println("

Indent to nested format:

",nestTree);

println("Is this tree the same as what we started with? ",nestTree==tree);

Output:

Nest tree internal format: L("RosettaCode",L("rocks","code","comparison","wiki"),L("mocks","golfing")) Formated: RosettaCode .rocks ..code ..comparison ..wiki .mocks ..golfing To indent format: 0 RosettaCode 1 rocks 2 code 2 comparison 2 wiki 1 mocks 2 golfing Indent to nested format: L("RosettaCode",L("rocks","code","comparison","wiki"),L("mocks","golfing")) Is this tree the same as what we started with? True

I'm choosing to only allow one root per tree/forest so the Raku example is coded differently:

rakutrees:=L(

L("RosettaCode",

L("encourages",

L("code",

"diversity","comparison")),

L("discourages",

"golfing","trolling","emphasising execution speed"),

),

L("code-golf.io",

L("encourages","golfing"),

L("discourages","comparison"),

)

);

println(rakutrees.apply(nestToString).concat());

iTrees := rakutrees.apply(nestToIndent);

println(iTrees.apply(indentToString).concat("

"));

(iTrees.apply(indentToNest)==rakutrees).println();

Output:

RosettaCode encourages code diversity comparison discourages golfing trolling emphasising execution speed code-golf.io encourages golfing discourages comparison 0 RosettaCode 1 encourages 2 code 3 diversity 3 comparison 1 discourages 2 golfing 2 trolling 2 emphasising execution speed 0 code-golf.io 1 encourages 2 golfing 1 discourages 2 comparison True