unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Mutable Graph Guile module
@ 2016-01-09 13:49 Amirouche Boubekki
  0 siblings, 0 replies; only message in thread
From: Amirouche Boubekki @ 2016-01-09 13:49 UTC (permalink / raw)
  To: Guile User

[-- Attachment #1: Type: text/plain, Size: 211 bytes --]

Héllo,


I happy to share with all of you a small graph mutable graph data 
structure built with hashtables. It include tests and Breath First 
Search algorithm.



-- 
Amirouche ~ amz3 ~ http://www.hyperdev.fr

[-- Attachment #2: graph.scm --]
[-- Type: text/plain, Size: 10865 bytes --]

;;;; graph.scm --- minimal graph module for Guile
;;;;
;;;; Copyright (C) 2016 Amirouche Boubekki <amirouche@hypermove.net>
;;;;
;;;; This library is free software; you can redistribute it and/or
;;;; modify it under the terms of the GNU Lesser General Public
;;;; License as published by the Free Software Foundation; either
;;;; version 3 of the License, or (at your option) any later version.
;;;;
;;;; This library is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;;; Lesser General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU Lesser General Public
;;;; License along with this library; if not, write to the Free Software
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
(define-module (graph))

(use-modules (srfi srfi-9))
(use-modules (srfi srfi-26))
(use-modules (srfi srfi-69))

(use-modules (ice-9 q))

;;; generate-uid helper

(define-public (generate-uid exists?)
  "Generate a random string made up alphanumeric ascii chars that doesn't exists
   according to `exists?`"
  (define (random-id)
    (define CHARS "0123456789AZERTYUIOPQSDFGHJKLMWXCVBN")
    ;; append 8 alphanumeric chars from `CHARS`
    (let loop ((count 8)
               (id ""))
      (if (eq? count 0)
          id
          (loop (1- count) (format #f "~a~a" id (string-ref CHARS (random 36)))))))

  (let loop ()
    ;; generate a random uid until it find an id that doesn't already exists?
    (let ((id (random-id)))
      (if (exists? id) (loop) id))))


;;; graph

(define-record-type <graph>
  (%make-graph name properties vertices edges)
  graph?
  (name graph-name)
  (properties graph-properties)
  (vertices graph-vertices)
  (edges graph-edges))


(define-public (make-graph name)
  (%make-graph name (make-hash-table) (make-hash-table) (make-hash-table)))

(define-public (graph-ref graph key)
  (hash-table-ref (graph-properties graph) key))

(define-public (graph-set! graph key value)
  (hash-table-set! (graph-properties graph) key value))

(define-public (graph-vertex-ref graph uid)
  (hash-table-ref (graph-vertices graph) uid (lambda () #false)))

(define-public (graph-edge-ref graph uid)
  (hash-table-ref (graph-edges graph) uid (lambda () #false)))

(define-public (graph-for-each-vertex graph proc)
  (for-each proc (hash-table-values (graph-vertices graph))))


;;; vertex

(define-record-type <vertex>
  (make-vertex graph uid incomings properties outgoings)
  vertex?
  (graph vertex-graph)
  (uid vertex-uid)
  (incomings vertex-incomings vertex-incomings!)
  (properties vertex-properties)
  (outgoings vertex-outgoings vertex-outgoings!))

(define-public (graph-make-vertex graph)
  (let* ((uid (generate-uid (cut graph-vertex-ref graph <>)))
         (vertex (make-vertex graph uid '() (make-hash-table) '())))
    (hash-table-set! (graph-vertices graph) uid vertex)
    vertex))

(define-public (vertex-ref vertex key)
  (hash-table-ref (vertex-properties vertex) key))

(define-public (vertex-set! vertex key value)
  (hash-table-set! (vertex-properties vertex) key value))


;;; edge

(define-record-type <edge>
  (make-edge graph uid start properties end)
  edge?
  (graph edge-graph)
  (uid edge-uid)
  (start edge-start edge-start!)
  (properties edge-properties)
  (end edge-end edge-end!))


(define-public (graph-make-edge graph start end)
  (let* ((uid (generate-uid (cut graph-edge-ref graph <>)))
         (edge (make-edge graph uid (vertex-uid start) (make-hash-table) (vertex-uid end))))
    ;; add to graph
    (hash-table-set! (graph-edges graph) uid edge)
    ;; update start vertex
    (vertex-outgoings! start (cons uid (vertex-outgoings start)))
    ;; update end vertex
    (vertex-incomings! end (cons uid (vertex-incomings end)))
    edge))

(define-public (edge-ref edge key)
  (hash-table-ref (edge-properties edge) key))

(define-public (edge-set! edge key value)
  (hash-table-set! (edge-properties edge) key value))

(define-public (edge-delete! edge)
  (let ((graph (edge-graph edge)))
    (hash-table-delete! (graph-edges graph) (edge-uid edge)))
  (let* ((uid (edge-start edge))
         (start (graph-vertex-ref (edge-graph edge) uid)))
    (vertex-outgoings! start (delete (edge-uid edge) (vertex-outgoings start))))
  (let* ((uid (edge-end edge))
         (end (graph-vertex-ref (edge-graph edge) uid)))
    (vertex-incomings! end (delete (edge-uid edge) (vertex-incomings end)))))


;;; helpers

(define-public (vertex-adjacents vertex)
  (let* ((ref-edge (cut graph-edge-ref (vertex-graph vertex) <>)))
    (append (map (compose edge-end ref-edge) (vertex-outgoings vertex))
            (map (compose edge-start ref-edge) (vertex-incomings vertex)))))


;;; algorithms

;; sanitize names of queue procedure
(define make-queue make-q)
(define enqueue! enq!)
(define dequeue! deq!)
(define queue-empty? q-empty?)


(define-public (bfs proc graph root)
  (let ((queue (make-queue))
        (visited (make-hash-table)))

    (hash-table-set! visited root #true)
    (enqueue! queue root)

    (proc root)

    (while (not (queue-empty? queue))
      (let ((current (dequeue! queue)))
        (let next ((adjacents (vertex-adjacents current)))
          (unless (null? adjacents)
            (let ((adjacent (graph-vertex-ref graph (car adjacents))))
              (unless (hash-table-ref visited adjacent (lambda () #false))
                (proc adjacent)
                (hash-table-set! visited adjacent #true)
                (enqueue! queue adjacent)
                (next (cdr adjacents))))))))))



;;; test-check

(define-syntax test-check
  (syntax-rules ()
    ((_ title tested-expression expected-result)
     (begin
       (format #true "* Checking ~s\n" title)
       (let* ((expected expected-result)
              (produced tested-expression))
         (unless (equal? expected produced)
             (format #true "Expected: ~s\n" expected)
             (format #true "Computed: ~s\n" produced)))))))

(when (getenv "CHECK")
  (test-check "null test"
              #true
              #true)

  (test-check "graph get/set properties"
              (let ((graph (make-graph "test")))
                (graph-set! graph "key" "value")
                (graph-ref graph "key"))
              "value")

  (test-check "graph-make-vertex"
              (let* ((graph (make-graph "test"))
                     (vertex (graph-make-vertex graph)))
                (vertex-uid vertex))
              "I5QBE2X7")

  (test-check "graph-vertex-ref"
              (let* ((graph (make-graph "test"))
                     (vertex (graph-make-vertex graph)))
                (vertex-uid (graph-vertex-ref graph (vertex-uid vertex))))
              "ACNEG5QH")

  (test-check "vertex get/set properties"
              (let* ((graph (make-graph "test"))
                     (vertex (graph-make-vertex graph)))
                (vertex-set! vertex "key" "value")
                (vertex-ref vertex "key"))
              "value")

  (test-check "graph-make-edge"
              (let* ((graph (make-graph "test"))
                     (start (graph-make-vertex graph))
                     (end (graph-make-vertex graph))
                     (edge (graph-make-edge graph start end)))
                (edge-uid edge))
              "FJC4FXVX")

  (test-check "graph-edge-ref"
              (let* ((graph (make-graph "test"))
                     (start (graph-make-vertex graph))
                     (end (graph-make-vertex graph))
                     (edge (graph-make-edge graph start end)))
                (edge-uid (graph-edge-ref graph (edge-uid edge))))
              "6D8CT84K")

  (test-check "edge get/set"
              (let* ((graph (make-graph "test"))
                     (start (graph-make-vertex graph))
                     (end (graph-make-vertex graph))
                     (edge (graph-make-edge graph start end)))
                (edge-set! edge 'key 'value)
                (edge-ref edge 'key))
              'value)

  (test-check "make edge and check start outgoings"
              (let* ((graph (make-graph "test"))
                     (start (graph-make-vertex graph))
                     (end (graph-make-vertex graph))
                     (edge (graph-make-edge graph start end)))
                (vertex-outgoings start))
              '("CGUE4JQX"))

  (test-check "make edge and check end incomings"
              (let* ((graph (make-graph "test"))
                     (start (graph-make-vertex graph))
                     (end (graph-make-vertex graph))
                     (edge (graph-make-edge graph start end)))
                (vertex-incomings end))
              '("FJUX0J70"))

  (test-check "make edge, delete and check start outgoings"
              (let* ((graph (make-graph "test"))
                     (start (graph-make-vertex graph))
                     (end (graph-make-vertex graph))
                     (edge (graph-make-edge graph start end)))
                (edge-delete! edge)
                (map edge-uid (vertex-outgoings start)))
              '())

  (test-check "make edge, delete and check end incomings"
              (let* ((graph (make-graph "test"))
                     (start (graph-make-vertex graph))
                     (end (graph-make-vertex graph))
                     (edge (graph-make-edge graph start end)))
                (edge-delete! edge)
                (map edge-uid (vertex-incomings end)))
              '())

  (test-check "bfs 1"
              (let* ((graph (make-graph "test bfs"))
                     (start (graph-make-vertex graph))
                     (end (graph-make-vertex graph))
                     (edge (graph-make-edge graph start end))
                     (out '()))
                (bfs (lambda (vertex)
                       (set! out (cons (vertex-uid vertex) out)))
                     graph
                     start)
                out)
              '("BFGONU27" "3U0FOZ3S"))

  (test-check "bfs 2"
              (let* ((graph (make-graph "test bfs"))
                     (one (graph-make-vertex graph))
                     (two (graph-make-vertex graph))
                     (three (graph-make-vertex graph))
                     (four (graph-make-vertex graph))
                     (_ (graph-make-edge graph one two))
                     (_ (graph-make-edge graph two three))
                     (_ (graph-make-edge graph three one))
                     (_ (graph-make-edge graph three four))
                     (out '()))
                (bfs (lambda (vertex)
                       (set! out (cons (vertex-uid vertex) out)))
                     graph
                     one)
                out)
              '("DFE8JJFG" "BVD4NBDB" "KBBT34AA" "PTUMTLV4"))
  )

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2016-01-09 13:49 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-01-09 13:49 Mutable Graph Guile module Amirouche Boubekki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).