経路からツリーへの変換

30分プログラム、その614。昨日やっていたやつ(id:mzp:20090630:tree)の逆変換。一晩寝たら、すんなり解けた。

(foo)
(foo bar)
(foo bar baz)
(foo xyzzy)
(foo baz)

みたいな木の根から末端までの経路リストがあったとする。これから、木を再構築したい。

(make-tree 'foo
           (make-tree 'bar (make-tree 'baz))
           (make-tree 'xyzzy)
           (make-tree 'baz))

これで、flash.display.Spriteとかflash.text.TextFieldみたいな完全修飾名からパッケージの階層構造を再構築できるようになる。

使い方

gosh> (path-list->tree '((foo bar baz) (foo bar) (foo xyzzy)))
(foo ((bar ((baz ()))) (xyzzy ())))

ソースコード

#! /opt/local/bin/gosh
;; -*- mode:scheme; coding:utf-8 -*-
;;
;; tree.scm -
;;
;; Copyright(C) 2009 by mzp
;; Author: MIZUNO Hiroki / mzpppp at gmail dot com
;; http://howdyworld.org
;;
;; Timestamp: 2009/06/30 21:39:50
;;
;; This program is free software; you can redistribute it and/or
;; modify it under MIT Lincence.
;;
(use srfi-1)
(use gauche.collection)

(define (make-tree name . sub-trees)
  (list name sub-trees))
(define (tree-name tree)
  (car tree))
(define (sub-trees tree)
  (cadr tree))
(define (leaf? tree)
  (eq? tree '()))

(define (fold-right-1 f xs)
  (fold-right f (car xs) (cdr xs)))

(define (merge-tree x y)
  (cond [(leaf? x) y]
	[(leaf? y) x]
	[(eq? (tree-name x) (tree-name y))
	 (apply make-tree
		(tree-name x)
		(map (lambda (ts) (fold-right-1 merge-tree ts))
		     (group-collection (append (sub-trees x) (sub-trees y))
				       :key tree-name)))]
	[else (error "invalid argument")]))

(define (path->tree path)
  (fold-right (lambda (x tree) (if (null? tree)
				   (make-tree x)
				   (make-tree x tree)))
	      '()
	      path))

(define (path-list->tree path-list)
  (fold-right-1 merge-tree
		(map path->tree path-list)))

(define (tree->path-list tree)
  (cons (list (tree-name tree))
	(map (cute cons (tree-name tree) <>)  (append-map tree->path-list (sub-trees tree)))))

(define *path-list* '((foo bar baz) (foo bar) (foo xyzzy) (foo baz)))
(define *tree* (make-tree 'foo
			  (make-tree 'bar (make-tree 'baz))
			  (make-tree 'xyzzy)
			  (make-tree 'baz)))

(tree->path-list *tree*)
(path-list->tree *path-list*)

(print (eq? *tree* (path-list->tree *path-list*)))

(path-list->tree '((foo bar baz)
		   (foo bar)
		   (foo xyzzy)))