mirror of
https://github.com/golang/go
synced 2024-11-23 01:30:02 -07:00
Ring ADT.
- Provides analogous functionality to a doubly-linked list implementation. - Completely symmetric set of operations. - Operations on the ADT do not lead to results that are outside the domain of the ADT (closed interface). - Alternative to container/list. R=rsc DELTA=489 (489 added, 0 deleted, 0 changed) OCL=32284 CL=32323
This commit is contained in:
parent
e50d3ba7e4
commit
6d3d25de21
61
src/pkg/container/ring/Makefile
Normal file
61
src/pkg/container/ring/Makefile
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style
|
||||||
|
# license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
|
# DO NOT EDIT. Automatically generated by gobuild.
|
||||||
|
# gobuild -m >Makefile
|
||||||
|
|
||||||
|
D=/container/
|
||||||
|
|
||||||
|
include $(GOROOT)/src/Make.$(GOARCH)
|
||||||
|
AR=gopack
|
||||||
|
|
||||||
|
default: packages
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.[$(OS)] *.a [$(OS)].out _obj
|
||||||
|
|
||||||
|
test: packages
|
||||||
|
gotest
|
||||||
|
|
||||||
|
coverage: packages
|
||||||
|
gotest
|
||||||
|
6cov -g $$(pwd) | grep -v '_test\.go:'
|
||||||
|
|
||||||
|
%.$O: %.go
|
||||||
|
$(GC) -I_obj $*.go
|
||||||
|
|
||||||
|
%.$O: %.c
|
||||||
|
$(CC) $*.c
|
||||||
|
|
||||||
|
%.$O: %.s
|
||||||
|
$(AS) $*.s
|
||||||
|
|
||||||
|
O1=\
|
||||||
|
ring.$O\
|
||||||
|
|
||||||
|
|
||||||
|
phases: a1
|
||||||
|
_obj$D/ring.a: phases
|
||||||
|
|
||||||
|
a1: $(O1)
|
||||||
|
$(AR) grc _obj$D/ring.a ring.$O
|
||||||
|
rm -f $(O1)
|
||||||
|
|
||||||
|
|
||||||
|
newpkg: clean
|
||||||
|
mkdir -p _obj$D
|
||||||
|
$(AR) grc _obj$D/ring.a
|
||||||
|
|
||||||
|
$(O1): newpkg
|
||||||
|
$(O2): a1
|
||||||
|
|
||||||
|
nuke: clean
|
||||||
|
rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ring.a
|
||||||
|
|
||||||
|
packages: _obj$D/ring.a
|
||||||
|
|
||||||
|
install: packages
|
||||||
|
test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D
|
||||||
|
cp _obj$D/ring.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ring.a
|
174
src/pkg/container/ring/ring.go
Normal file
174
src/pkg/container/ring/ring.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// The ring package implements operations on circular lists.
|
||||||
|
package ring
|
||||||
|
|
||||||
|
// A Ring is an element of a circular list, or ring.
|
||||||
|
// Rings do not have a beginning or end; a pointer to any ring element
|
||||||
|
// serves as reference to the entire ring. Empty rings are represented
|
||||||
|
// as nil Ring pointers. The zero value for a Ring is a one-element
|
||||||
|
// ring with a nil Value.
|
||||||
|
//
|
||||||
|
type Ring struct {
|
||||||
|
next, prev *Ring;
|
||||||
|
Value interface{}; // for use by client; untouched by this library
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (r *Ring) init() *Ring {
|
||||||
|
r.next = r;
|
||||||
|
r.prev = r;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Next returns the next ring element. r must not be empty.
|
||||||
|
func (r *Ring) Next() *Ring {
|
||||||
|
if r.next == nil {
|
||||||
|
return r.init();
|
||||||
|
}
|
||||||
|
return r.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Prev returns the previous ring element. r must not be empty.
|
||||||
|
func (r *Ring) Prev() *Ring {
|
||||||
|
if r.next == nil {
|
||||||
|
return r.init();
|
||||||
|
}
|
||||||
|
return r.prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Move moves n % r.Len() elements backward (n < 0) or forward (n > 0)
|
||||||
|
// in the ring and returns that ring element. r must not be empty.
|
||||||
|
//
|
||||||
|
func (r *Ring) Move(n int) *Ring {
|
||||||
|
if r.next == nil {
|
||||||
|
return r.init();
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case n < 0:
|
||||||
|
for ; n < 0; n++ {
|
||||||
|
r = r.prev;
|
||||||
|
}
|
||||||
|
case n > 0:
|
||||||
|
for ; n > 0; n-- {
|
||||||
|
r = r.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// New creates a ring of n elements.
|
||||||
|
func New(n int) *Ring {
|
||||||
|
if n <= 0 {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
r := new(Ring);
|
||||||
|
p := r;
|
||||||
|
for i := 1; i < n; i++ {
|
||||||
|
p.next = &Ring{prev: p};
|
||||||
|
p = p.next;
|
||||||
|
}
|
||||||
|
p.next = r;
|
||||||
|
r.prev = p;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Link connects ring r with with ring s such that r.Next(1)
|
||||||
|
// becomes s and returns the original value for r.Next(1).
|
||||||
|
// r must not be empty.
|
||||||
|
//
|
||||||
|
// If r and s point to the same ring, linking
|
||||||
|
// them removes the elements between r and s from the ring.
|
||||||
|
// The removed elements form a subring and the result is a
|
||||||
|
// reference to that subring (if no elements were removed,
|
||||||
|
// the result is still the original value for r.Next(1),
|
||||||
|
// and not nil).
|
||||||
|
//
|
||||||
|
// If r and s point to different rings, linking
|
||||||
|
// them creates a single ring with the elements of s inserted
|
||||||
|
// after r. The result points to the element following the
|
||||||
|
// last element of s after insertion.
|
||||||
|
//
|
||||||
|
func (r *Ring) Link(s *Ring) *Ring {
|
||||||
|
n := r.Next();
|
||||||
|
if s != nil {
|
||||||
|
p := s.Prev();
|
||||||
|
// Note: Cannot use multiple assignment because
|
||||||
|
// evaluation order of LHS is not specified.
|
||||||
|
r.next = s;
|
||||||
|
s.prev = r;
|
||||||
|
n.prev = p;
|
||||||
|
p.next = n;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Unlink removes n % r.Len() elements from the ring r, starting
|
||||||
|
// at r.Next(). If n % r.Len() == 0, r remains unchanged.
|
||||||
|
// The result is the removed subring. r must not be empty.
|
||||||
|
//
|
||||||
|
func (r *Ring) Unlink(n int) *Ring {
|
||||||
|
if n <= 0 {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return r.Link(r.Move(n + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Len computes the number of elements in ring r.
|
||||||
|
// It executes in time proportional to the number of elements.
|
||||||
|
//
|
||||||
|
func (r *Ring) Len() int {
|
||||||
|
n := 0;
|
||||||
|
if r != nil {
|
||||||
|
n = 1;
|
||||||
|
for p := r.Next(); p != r; p = p.next {
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Forward returns a channel for forward iteration through a ring.
|
||||||
|
// Iteration is undefined if the ring is changed during iteration.
|
||||||
|
//
|
||||||
|
func (r *Ring) Forward() <-chan *Ring {
|
||||||
|
c := make(chan *Ring);
|
||||||
|
go func() {
|
||||||
|
if r != nil {
|
||||||
|
c <- r;
|
||||||
|
for p := r.Next(); p != r; p = p.next {
|
||||||
|
c <- p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(c);
|
||||||
|
}();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Backward returns a channel for backward iteration through a ring.
|
||||||
|
// Iteration is undefined if the ring is changed during iteration.
|
||||||
|
//
|
||||||
|
func (r *Ring) Backward() <-chan *Ring {
|
||||||
|
c := make(chan *Ring);
|
||||||
|
go func() {
|
||||||
|
if r != nil {
|
||||||
|
c <- r;
|
||||||
|
for p := r.Prev(); p != r; p = p.prev {
|
||||||
|
c <- p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(c);
|
||||||
|
}();
|
||||||
|
return c;
|
||||||
|
}
|
260
src/pkg/container/ring/ring_test.go
Normal file
260
src/pkg/container/ring/ring_test.go
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/ring";
|
||||||
|
"fmt";
|
||||||
|
"testing";
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// For debugging - keep around.
|
||||||
|
func dump(r *Ring) {
|
||||||
|
if r == nil {
|
||||||
|
fmt.Println("empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i, n := 0, r.Len();
|
||||||
|
for p := r; i < n; p = p.next {
|
||||||
|
fmt.Printf("%4d: %p = {<- %p | %p ->}\n", i, p, p.prev, p.next);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
fmt.Println();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func verify(t *testing.T, r *Ring, N int, sum int) {
|
||||||
|
// Len
|
||||||
|
n := r.Len();
|
||||||
|
if n != N {
|
||||||
|
t.Errorf("r.Len() == %d; expected %d", n, N);
|
||||||
|
}
|
||||||
|
|
||||||
|
// forward iteration
|
||||||
|
n = 0;
|
||||||
|
s := 0;
|
||||||
|
for p := range r.Forward() {
|
||||||
|
n++;
|
||||||
|
if p.Value != nil {
|
||||||
|
s += p.Value.(int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != N {
|
||||||
|
t.Errorf("number of forward iterations == %d; expected %d", n, N);
|
||||||
|
}
|
||||||
|
if sum >= 0 && s != sum {
|
||||||
|
t.Errorf("forward ring sum = %d; expected %d", s, sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
// backward iteration
|
||||||
|
n = 0;
|
||||||
|
s = 0;
|
||||||
|
for p := range r.Backward() {
|
||||||
|
n++;
|
||||||
|
if p.Value != nil {
|
||||||
|
s += p.Value.(int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != N {
|
||||||
|
t.Errorf("number of backward iterations == %d; expected %d", n, N);
|
||||||
|
}
|
||||||
|
if sum >= 0 && s != sum {
|
||||||
|
t.Errorf("backward ring sum = %d; expected %d", s, sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// connections
|
||||||
|
if r.next != nil {
|
||||||
|
var p *Ring; // previous element
|
||||||
|
for q := r; p == nil || q != r; q = q.next {
|
||||||
|
if p != nil && p != q.prev {
|
||||||
|
t.Errorf("prev = %p, expected q.prev = %p\n", p, q.prev);
|
||||||
|
}
|
||||||
|
p = q;
|
||||||
|
}
|
||||||
|
if p != r.prev {
|
||||||
|
t.Errorf("prev = %p, expected r.prev = %p\n", p, r.prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, Prev
|
||||||
|
if r.Next() != r.next {
|
||||||
|
t.Errorf("r.Next() != r.next");
|
||||||
|
}
|
||||||
|
if r.Prev() != r.prev {
|
||||||
|
t.Errorf("r.Prev() != r.prev");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move
|
||||||
|
if r.Move(0) != r {
|
||||||
|
t.Errorf("r.Move(0) != r");
|
||||||
|
}
|
||||||
|
if r.Move(N) != r {
|
||||||
|
t.Errorf("r.Move(%d) != r", N);
|
||||||
|
}
|
||||||
|
if r.Move(-N) != r {
|
||||||
|
t.Errorf("r.Move(%d) != r", -N);
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
ni := N + i;
|
||||||
|
mi := ni % N;
|
||||||
|
if r.Move(ni) != r.Move(mi) {
|
||||||
|
t.Errorf("r.Move(%d) != r.Move(%d)", ni, mi);
|
||||||
|
}
|
||||||
|
if r.Move(-ni) != r.Move(-mi) {
|
||||||
|
t.Errorf("r.Move(%d) != r.Move(%d)", -ni, -mi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestCornerCases(t *testing.T) {
|
||||||
|
var (
|
||||||
|
r0 *Ring;
|
||||||
|
r1 Ring;
|
||||||
|
)
|
||||||
|
// Basics
|
||||||
|
verify(t, r0, 0, 0);
|
||||||
|
verify(t, &r1, 1, 0);
|
||||||
|
// Insert
|
||||||
|
r1.Link(r0);
|
||||||
|
verify(t, r0, 0, 0);
|
||||||
|
verify(t, &r1, 1, 0);
|
||||||
|
// Insert
|
||||||
|
r1.Link(r0);
|
||||||
|
verify(t, r0, 0, 0);
|
||||||
|
verify(t, &r1, 1, 0);
|
||||||
|
// Unlink
|
||||||
|
r1.Unlink(0);
|
||||||
|
verify(t, &r1, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func makeN(n int) *Ring {
|
||||||
|
r := New(n);
|
||||||
|
for i := 1; i <= n; i++ {
|
||||||
|
r.Value = i;
|
||||||
|
r = r.Next();
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func sum(r *Ring) int {
|
||||||
|
s := 0;
|
||||||
|
for p := range r.Forward() {
|
||||||
|
s += p.Value.(int);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func sumN(n int) int {
|
||||||
|
return (n*n + n)/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
r := New(i);
|
||||||
|
verify(t, r, i, -1);
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
r := makeN(i);
|
||||||
|
verify(t, r, i, sumN(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestLink1(t *testing.T) {
|
||||||
|
r1a := makeN(1);
|
||||||
|
var r1b Ring;
|
||||||
|
r2a := r1a.Link(&r1b);
|
||||||
|
verify(t, r2a, 2, 1);
|
||||||
|
if r2a != r1a {
|
||||||
|
t.Errorf("a) 2-element link failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
r2b := r2a.Link(r2a.Next());
|
||||||
|
verify(t, r2b, 2, 1);
|
||||||
|
if r2b != r2a.Next() {
|
||||||
|
t.Errorf("b) 2-element link failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
r1c := r2b.Link(r2b);
|
||||||
|
verify(t, r1c, 1, 1);
|
||||||
|
verify(t, r2b, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestLink2(t *testing.T) {
|
||||||
|
var r0 *Ring;
|
||||||
|
r1a := &Ring{Value: 42};
|
||||||
|
r1b := &Ring{Value: 77};
|
||||||
|
r10 := makeN(10);
|
||||||
|
|
||||||
|
r1a.Link(r0);
|
||||||
|
verify(t, r1a, 1, 42);
|
||||||
|
|
||||||
|
r1a.Link(r1b);
|
||||||
|
verify(t, r1a, 2, 42 + 77);
|
||||||
|
|
||||||
|
r10.Link(r0);
|
||||||
|
verify(t, r10, 10, sumN(10));
|
||||||
|
|
||||||
|
r10.Link(r1a);
|
||||||
|
verify(t, r10, 12, sumN(10) + 42 + 77);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestLink3(t *testing.T) {
|
||||||
|
var r Ring;
|
||||||
|
n := 1;
|
||||||
|
for i := 1; i < 100; i++ {
|
||||||
|
n += i;
|
||||||
|
verify(t, r.Link(New(i)), n, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestUnlink(t *testing.T) {
|
||||||
|
r10 := makeN(10);
|
||||||
|
s10 := r10.Move(6);
|
||||||
|
|
||||||
|
sum10 := sumN(10);
|
||||||
|
sum6 := sumN(6);
|
||||||
|
|
||||||
|
verify(t, r10, 10, sum10);
|
||||||
|
verify(t, s10, 10, sum10);
|
||||||
|
|
||||||
|
r0 := r10.Unlink(0);
|
||||||
|
verify(t, r0, 0, 0);
|
||||||
|
|
||||||
|
r1 := r10.Unlink(1);
|
||||||
|
verify(t, r1, 1, 2);
|
||||||
|
verify(t, r10, 9, sum10 - 2);
|
||||||
|
|
||||||
|
r9 := r10.Unlink(9);
|
||||||
|
verify(t, r9, 9, sum10 - 2);
|
||||||
|
verify(t, r10, 9, sum10 - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestLinkUnlink(t *testing.T) {
|
||||||
|
for i := 1; i < 4; i++ {
|
||||||
|
ri := New(i);
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
rj := ri.Unlink(j);
|
||||||
|
verify(t, rj, j, -1);
|
||||||
|
verify(t, ri, i-j, -1);
|
||||||
|
ri.Link(rj);
|
||||||
|
verify(t, ri, i, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user