Решение на Piece table от Пламен Стоев

Обратно към всички решения

Към профила на Пламен Стоев

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 5 успешни тест(а)
  • 3 неуспешни тест(а)

Код

package main
import (
"strings"
)
type Editor interface {
// Insert text starting from given position.
Insert(position uint, text string) Editor
// Delete length items from offset.
Delete(offset, length uint) Editor
// Undo reverts latest change.
Undo() Editor
// Redo re-applies latest undone change.
Redo() Editor
// String returns complete representation of what a file looks
// like after all manipulations.
String() string
}
type chunk struct {
buffer []byte
offset int
length int
}
type editor struct {
previous Editor
next Editor
origin []byte
add []byte
chunks []chunk
}
func (e editor) Insert(position uint, text string) Editor {
textBytes := []byte(text)
addOffset := len(e.add)
e.add = append(e.add, textBytes...)
newChunk := chunk{
buffer: e.add,
offset: addOffset,
length: len(textBytes),
}
var nextChunks []chunk
currentLength := 0
intPosition := int(position)
newChunkInserted := false
for _, currentChunk := range e.chunks {
if intPosition > currentLength &&
intPosition < currentLength+currentChunk.length {
// Split currentChunk in two and tuck the newChunk between:
splitPoint := intPosition - currentLength
nextChunks = append(nextChunks,
chunk{
buffer: currentChunk.buffer,
offset: currentChunk.offset,
length: splitPoint,
},
newChunk,
chunk{
buffer: currentChunk.buffer,
offset: currentChunk.offset + splitPoint,
length: currentChunk.length - splitPoint,
})
currentLength += currentChunk.length + newChunk.length
newChunkInserted = true
} else if intPosition == currentLength {
// take both currentChunk and newChunk, newChunk first:
nextChunks = append(nextChunks, newChunk, currentChunk)
currentLength += currentChunk.length + newChunk.length
newChunkInserted = true
} else if intPosition+newChunk.length == currentLength+currentChunk.length {
// take both currentChunk and newChunk, currentChunk first:
nextChunks = append(nextChunks, currentChunk, newChunk)
currentLength += currentChunk.length + newChunk.length
} else {
// Take as-is:
nextChunks = append(nextChunks, currentChunk)
currentLength += currentChunk.length
}
}
if !newChunkInserted {
nextChunks = append(nextChunks, newChunk)
}
newEditor := editor{
origin: e.origin,
add: e.add,
chunks: nextChunks,
}
previousEditor := editor{
origin: e.origin,
add: e.add,
chunks: e.chunks,
previous: e.previous,
next: newEditor,
}
newEditor.previous = previousEditor
return newEditor
}
func (e editor) Delete(offset, length uint) Editor {
intOffset := int(offset)
intLength := int(length)
var nextChunks []chunk
currentLength := 0
for _, currentChunk := range e.chunks {
if currentLength+currentChunk.length < intOffset ||
currentLength > intOffset+intLength {
// Take "as-is":
nextChunks = append(nextChunks, currentChunk)
} else if currentLength < intOffset &&
currentLength+currentChunk.length < intOffset+intLength {
// take the first part:
splitPoint := intOffset - currentLength
nextChunks = append(nextChunks,
chunk{
buffer: currentChunk.buffer,
offset: currentChunk.offset,
length: splitPoint,
})
} else if currentLength > intOffset &&
currentLength+currentChunk.length > intOffset+intLength {
// take the second part:
splitPoint := intOffset + intLength - currentLength
nextChunks = append(nextChunks,
chunk{
buffer: currentChunk.buffer,
offset: currentChunk.offset + splitPoint,
length: currentChunk.length - splitPoint,
})
} else if currentLength <= intOffset &&
currentLength+currentChunk.length >= intOffset+intLength {
if currentLength < intOffset {
nextChunks = append(nextChunks,
chunk{
buffer: currentChunk.buffer,
offset: currentChunk.offset,
length: intOffset - currentChunk.offset,
})
}
nextChunks = append(nextChunks,
chunk{
buffer: currentChunk.buffer,
offset: intOffset + intLength,
length: currentLength + currentChunk.length - (intOffset + intLength),
})
} // else skip the chunk entirely
currentLength += currentChunk.length
}
newEditor := editor{
origin: e.origin,
add: e.add,
chunks: nextChunks,
}
previousEditor := editor{
origin: e.origin,
add: e.add,
chunks: e.chunks,
previous: e.previous,
next: newEditor,
}
newEditor.previous = previousEditor
return newEditor
}
func (e editor) Undo() Editor {
if e.previous != nil {
return e.previous
}
return e
}
func (e editor) Redo() Editor {
if e.next != nil {
return e.next
}
return e
}
func (e editor) String() string {
var builder strings.Builder
for _, chunk := range e.chunks {
builder.Write(chunk.buffer[chunk.offset : chunk.offset+chunk.length])
}
return builder.String()
}
// NewEditor returns a new editor
func NewEditor(origin string) Editor {
originBytes := []byte(origin)
return editor{
origin: originBytes,
chunks: []chunk{
chunk{
buffer: originBytes,
offset: 0,
length: len(originBytes),
},
},
}
}

Лог от изпълнението

--- FAIL: TestExampleFromReadme (0.00s)
panic: runtime error: slice bounds out of range [recovered]
	panic: runtime error: slice bounds out of range

goroutine 6 [running]:
testing.tRunner.func1(0xc0000b2100)
	/usr/local/go/src/testing/testing.go:792 +0x387
panic(0x513020, 0x61aff0)
	/usr/local/go/src/runtime/panic.go:513 +0x1b9
_/tmp/d20181107-53-zq4hk4.editor.String(0x556ea0, 0xc00009c700, 0x0, 0x0, 0xc000014260, 0x14, 0x20, 0xc0000180b8, 0x8, 0x8, ...)
	/tmp/d20181107-53-zq4hk4/solution.go:205 +0x2b0
_/tmp/d20181107-53-zq4hk4.TestExampleFromReadme(0xc0000b2100)
	/tmp/d20181107-53-zq4hk4/solution_test.go:15 +0x298
testing.tRunner(0xc0000b2100, 0x540ac8)
	/usr/local/go/src/testing/testing.go:827 +0xbf
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:878 +0x353
exit status 2
FAIL	_/tmp/d20181107-53-zq4hk4	0.004s
PASS
ok  	_/tmp/d20181107-53-zq4hk4	0.002s
PASS
ok  	_/tmp/d20181107-53-zq4hk4	0.002s
--- FAIL: TestSeveralUndos (0.00s)
panic: runtime error: slice bounds out of range [recovered]
	panic: runtime error: slice bounds out of range

goroutine 6 [running]:
testing.tRunner.func1(0xc0000b0100)
	/usr/local/go/src/testing/testing.go:792 +0x387
panic(0x513020, 0x61aff0)
	/usr/local/go/src/runtime/panic.go:513 +0x1b9
_/tmp/d20181107-53-zq4hk4.editor.String(0x556ea0, 0xc00009c700, 0x0, 0x0, 0xc000014260, 0x14, 0x20, 0xc0000180b8, 0x8, 0x8, ...)
	/tmp/d20181107-53-zq4hk4/solution.go:205 +0x2b0
_/tmp/d20181107-53-zq4hk4.TestSeveralUndos(0xc0000b0100)
	/tmp/d20181107-53-zq4hk4/solution_test.go:39 +0x1f2
testing.tRunner(0xc0000b0100, 0x540af0)
	/usr/local/go/src/testing/testing.go:827 +0xbf
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:878 +0x353
exit status 2
FAIL	_/tmp/d20181107-53-zq4hk4	0.005s
PASS
ok  	_/tmp/d20181107-53-zq4hk4	0.003s
--- FAIL: TestSeveralRedos (0.00s)
    solution_test.go:75: Expect: "A span of text"; got "A large span of English text"
FAIL
exit status 1
FAIL	_/tmp/d20181107-53-zq4hk4	0.002s
PASS
ok  	_/tmp/d20181107-53-zq4hk4	0.002s
PASS
ok  	_/tmp/d20181107-53-zq4hk4	0.002s

История (1 версия и 3 коментара)

Пламен обнови решението на 07.11.2018 16:30 (преди 9 месеца)

+package main
+
+import (
+ "strings"
+)
+
+type Editor interface {
+ // Insert text starting from given position.
+ Insert(position uint, text string) Editor
+
+ // Delete length items from offset.
+ Delete(offset, length uint) Editor
+
+ // Undo reverts latest change.
+ Undo() Editor
+
+ // Redo re-applies latest undone change.
+ Redo() Editor
+
+ // String returns complete representation of what a file looks
+ // like after all manipulations.
+ String() string
+}
+
+type chunk struct {
+ buffer []byte
+ offset int
+ length int
+}
+
+type editor struct {
+ previous Editor
+ next Editor
+ origin []byte
+ add []byte
+ chunks []chunk
+}
+
+func (e editor) Insert(position uint, text string) Editor {
+ textBytes := []byte(text)
+ addOffset := len(e.add)
+ e.add = append(e.add, textBytes...)
+
+ newChunk := chunk{
+ buffer: e.add,
+ offset: addOffset,
+ length: len(textBytes),
+ }
+
+ var nextChunks []chunk
+ currentLength := 0
+ intPosition := int(position)
+
+ newChunkInserted := false
+ for _, currentChunk := range e.chunks {
+ if intPosition > currentLength &&
+ intPosition < currentLength+currentChunk.length {
+ // Split currentChunk in two and tuck the newChunk between:
+
+ splitPoint := intPosition - currentLength
+
+ nextChunks = append(nextChunks,
+ chunk{
+ buffer: currentChunk.buffer,
+ offset: currentChunk.offset,
+ length: splitPoint,
+ },
+ newChunk,
+ chunk{
+ buffer: currentChunk.buffer,
+ offset: currentChunk.offset + splitPoint,
+ length: currentChunk.length - splitPoint,
+ })
+ currentLength += currentChunk.length + newChunk.length
+ newChunkInserted = true
+ } else if intPosition == currentLength {
+ // take both currentChunk and newChunk, newChunk first:
+ nextChunks = append(nextChunks, newChunk, currentChunk)
+ currentLength += currentChunk.length + newChunk.length
+ newChunkInserted = true
+ } else if intPosition+newChunk.length == currentLength+currentChunk.length {
+ // take both currentChunk and newChunk, currentChunk first:
+ nextChunks = append(nextChunks, currentChunk, newChunk)
+ currentLength += currentChunk.length + newChunk.length
+ } else {
+ // Take as-is:
+ nextChunks = append(nextChunks, currentChunk)
+ currentLength += currentChunk.length
+ }
+ }
+
+ if !newChunkInserted {
+ nextChunks = append(nextChunks, newChunk)
+ }
+
+ newEditor := editor{
+ origin: e.origin,
+ add: e.add,
+ chunks: nextChunks,
+ }
+ previousEditor := editor{
+ origin: e.origin,
+ add: e.add,
+ chunks: e.chunks,
+ previous: e.previous,
+ next: newEditor,
+ }
+ newEditor.previous = previousEditor
+
+ return newEditor
+}
+
+func (e editor) Delete(offset, length uint) Editor {
+ intOffset := int(offset)
+ intLength := int(length)
+
+ var nextChunks []chunk
+ currentLength := 0
+ for _, currentChunk := range e.chunks {
+ if currentLength+currentChunk.length < intOffset ||
+ currentLength > intOffset+intLength {
+ // Take "as-is":
+ nextChunks = append(nextChunks, currentChunk)
+ } else if currentLength < intOffset &&
+ currentLength+currentChunk.length < intOffset+intLength {
+ // take the first part:
+
+ splitPoint := intOffset - currentLength
+
+ nextChunks = append(nextChunks,
+ chunk{
+ buffer: currentChunk.buffer,
+ offset: currentChunk.offset,
+ length: splitPoint,
+ })
+ } else if currentLength > intOffset &&
+ currentLength+currentChunk.length > intOffset+intLength {
+ // take the second part:
+
+ splitPoint := intOffset + intLength - currentLength
+
+ nextChunks = append(nextChunks,
+ chunk{
+ buffer: currentChunk.buffer,
+ offset: currentChunk.offset + splitPoint,
+ length: currentChunk.length - splitPoint,
+ })
+ } else if currentLength <= intOffset &&
+ currentLength+currentChunk.length >= intOffset+intLength {
+ if currentLength < intOffset {
+ nextChunks = append(nextChunks,
+ chunk{
+ buffer: currentChunk.buffer,
+ offset: currentChunk.offset,
+ length: intOffset - currentChunk.offset,
+ })
+ }
+ nextChunks = append(nextChunks,
+ chunk{
+ buffer: currentChunk.buffer,
+ offset: intOffset + intLength,
+ length: currentLength + currentChunk.length - (intOffset + intLength),
+ })
+ } // else skip the chunk entirely
+ currentLength += currentChunk.length
+ }
+
+ newEditor := editor{
+ origin: e.origin,
+ add: e.add,
+ chunks: nextChunks,
+ }
+ previousEditor := editor{
+ origin: e.origin,
+ add: e.add,
+ chunks: e.chunks,
+ previous: e.previous,
+ next: newEditor,
+ }
+ newEditor.previous = previousEditor
+
+ return newEditor
+}
+
+func (e editor) Undo() Editor {
+ if e.previous != nil {
+ return e.previous
+ }
+
+ return e
+}
+
+func (e editor) Redo() Editor {
+ if e.next != nil {
+ return e.next
+ }
+
+ return e
+}
+
+func (e editor) String() string {
+ var builder strings.Builder
+
+ for _, chunk := range e.chunks {
+ builder.Write(chunk.buffer[chunk.offset : chunk.offset+chunk.length])
+ }
+
+ return builder.String()
+}
+
+// NewEditor returns a new editor
+func NewEditor(origin string) Editor {
+ originBytes := []byte(origin)
+
+ return editor{
+ origin: originBytes,
+ chunks: []chunk{
+ chunk{
+ buffer: originBytes,
+ offset: 0,
+ length: len(originBytes),
+ },
+ },
+ }
+}