first commit

This commit is contained in:
didier amyot committed 2015-03-09 20:58:37 -07:00
commit 57d1dee00b
7 files changed
+465

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
## The questions I screwed
Questions (and their answers) I screwed up during interviews[^1].
+4
View File
@@ -0,0 +1,4 @@
## Question
Implement a *Least Recently Used* (lru) cache
+60
View File
@@ -0,0 +1,60 @@
package lrucache
import (
"container/list"
)
type Creator func(key string) interface{}
type cacheKey struct {
Value interface{}
DoubleLinkedListNode *list.Element
}
type LRUCache struct {
cache map[string]*cacheKey
ddl *list.List
creator Creator
length int
}
func New(creator Creator, length int) *LRUCache {
return &LRUCache{
cache: make(map[string]*cacheKey),
creator: creator,
ddl: new(list.List).Init(),
length: length,
}
}
func (lru *LRUCache) add(key string, val interface{}) interface{} {
lru.ddl.PushFront(key)
lru.cache[key] = &cacheKey{val, lru.ddl.Front()}
return val
}
func (lru *LRUCache) Size() int {
return len(lru.cache)
}
func (lru *LRUCache) removeExtraKeys() {
if lru.ddl.Len() < lru.length {
return
}
for e := lru.ddl.Back(); lru.ddl.Len() > lru.length; {
delete(lru.cache, e.Value.(string))
be := e.Prev()
lru.ddl.Remove(e)
e = be
}
}
func (lru *LRUCache) Get(key string) interface{} {
if val, ok := lru.cache[key]; ok {
lru.ddl.MoveToFront(val.DoubleLinkedListNode)
return val.Value
}
ret := lru.add(key, lru.creator(key))
lru.removeExtraKeys()
return ret
}
+118
View File
@@ -0,0 +1,118 @@
// Example of an lru implementation using double linked lists (not thread safe)
package lrucache
import (
"container/list"
"errors"
"strconv"
"testing"
)
func mockCreator(key string) interface{} {
return key
}
func doubleLinkedListContains(l *list.List, elm string) bool {
for node := l.Front(); node != nil; node = node.Next() {
if node.Value.(string) == elm {
return true
}
}
return false
}
func toString(a interface{}) string {
if i, ok := a.(string); ok {
return i
}
panic("cannot convert in string")
}
func TestAdd(t *testing.T) {
lru := New(mockCreator, 10)
if lru.add("key", "val") != "val" {
t.Error("must returns the value passed")
}
if toString(lru.ddl.Front().Value) != "key" {
t.Error("key must be ther first item after adding it")
}
if val, ok := lru.cache["key"]; !ok {
t.Error("key not in cache")
} else if toString(val.Value) != "val" {
t.Error("bad value associated to key")
}
}
func TestRemoveExtraKeys(t *testing.T) {
lru := New(mockCreator, 4)
for i := 0; i < 10; i++ {
lru.add(strconv.Itoa(i), strconv.Itoa(i))
}
if lru.Size() != 10 {
t.Error("bad initial length")
}
lru.removeExtraKeys()
if lru.Size() != 4 {
t.Error("bad final length. should be 4 but have ", lru.Size())
}
if lru.ddl.Len() != 4 {
t.Error("double linked list has incorrect length")
}
}
func checkLRUCacheState(lru *LRUCache, expected ...string) error {
expectedSize := len(expected)
if len(lru.cache) != expectedSize {
return errors.New("cache of wrong size")
}
if lru.ddl.Len() != expectedSize {
return errors.New("double linked list of wrong size")
}
for _, e := range expected {
if _, ok := lru.cache[e]; !ok {
return errors.New("key " + e + " not in cache")
}
if !doubleLinkedListContains(lru.ddl, e) {
return errors.New("key " + e + " not in double linked list")
}
}
return nil
}
func TestGet(t *testing.T) {
lru := New(mockCreator, 2)
if v := lru.Get("0"); v != "0" {
t.Error()
}
if err := checkLRUCacheState(lru, "0"); err != nil {
t.Error(err)
return
}
lru.Get("1")
if err := checkLRUCacheState(lru, "1", "0"); err != nil {
t.Error(err)
return
}
lru.Get("2")
if err := checkLRUCacheState(lru, "2", "1"); err != nil {
t.Error(err)
return
}
lru.Get("1")
if err := checkLRUCacheState(lru, "1", "2"); err != nil {
t.Error(err)
return
}
lru.Get("3")
if err := checkLRUCacheState(lru, "3", "1"); err != nil {
t.Error(err)
return
}
}
+4
View File
@@ -0,0 +1,4 @@
## Question
You have *n* streams of sorted object, create a function that will merge them in a
sorted steam.
+133
View File
@@ -0,0 +1,133 @@
package sorting
import "fmt"
var _ = fmt.Print
type IsGreaterer interface {
// return true iff "this" is strictly greater than e
IsGreater(e IsGreaterer) bool
}
type alwaysBigger struct{}
func (a *alwaysBigger) IsGreater(e IsGreaterer) bool {
return e != nil
}
type Stream chan IsGreaterer
type streamOutput struct {
Val IsGreaterer
Idx int
}
func readFirstElementOfAllTheStreams(streams []Stream) []IsGreaterer {
values := make([]IsGreaterer, len(streams))
ch := make(chan *streamOutput)
for i, _ := range values {
go func(idx int) {
v := <-streams[idx]
ch <- &streamOutput{v, idx}
}(i)
}
i := 0
for streamOutput := range ch {
values[streamOutput.Idx] = streamOutput.Val
i++
if i == len(streams) {
return values
}
}
return values
}
func allStreamsButOne(streams []Stream, idx int) []Stream {
ret := make([]Stream, len(streams)-1)
for i, s := range streams {
if i < idx {
ret[i] = s
} else if i > idx {
ret[i-1] = s
}
}
return ret
}
func prependToStream(streams Stream, toPrepend IsGreaterer) Stream {
ret := make(Stream)
go func() {
ret <- toPrepend
for e := range streams {
ret <- e
}
close(ret)
}()
return ret
}
func prependValueToEachStream(streams []Stream, values []IsGreaterer) []Stream {
ret := make([]Stream, len(streams))
for i := 0; i < len(streams); i++ {
ret[i] = prependToStream(streams[i], values[i])
}
return ret
}
func getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams []Stream) (Stream, IsGreaterer, []Stream) {
firstElements := readFirstElementOfAllTheStreams(streams)
streams = prependValueToEachStream(streams, firstElements)
var smallestStreamIndex int = -1
var smallestItem IsGreaterer = &alwaysBigger{}
var secondSmallestItem IsGreaterer = &alwaysBigger{}
for i, e := range firstElements {
if smallestItem.IsGreater(e) {
smallestItem = e
smallestStreamIndex = i
} else if secondSmallestItem.IsGreater(e) {
secondSmallestItem = e
}
}
if _, ok := secondSmallestItem.(*alwaysBigger); ok {
return streams[smallestStreamIndex], nil, []Stream{}
}
return streams[smallestStreamIndex], secondSmallestItem, allStreamsButOne(streams, smallestStreamIndex)
}
func sortStreamInChannel(o Stream, streams []Stream) {
if len(streams) == 1 {
for e := range streams[0] {
o <- e
}
close(o)
return
}
smallestStream, secondSmallestItem, greaterStreams := getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams)
if secondSmallestItem == nil {
sortStreamInChannel(o, append(greaterStreams, smallestStream))
return
}
for elm := range smallestStream {
if !elm.IsGreater(secondSmallestItem) {
o <- elm
} else {
smallestStream = prependToStream(smallestStream, elm)
sortStreamInChannel(o, append(greaterStreams, smallestStream))
return
}
}
sortStreamInChannel(o, greaterStreams)
}
// Takes sorted streams as input and return the merged result stream
func SortStreams(streams ...Stream) Stream {
ret := make(Stream)
go sortStreamInChannel(ret, streams)
return ret
}
+140
View File
@@ -0,0 +1,140 @@
package sorting
import (
"reflect"
"testing"
)
type MockIsGreaterer int
func (i MockIsGreaterer) IsGreater(e IsGreaterer) bool {
return i > e.(MockIsGreaterer)
}
func streamEqual(stream Stream, expected ...int) bool {
i := 0
for v := range stream {
if i >= len(expected) {
return false
}
if MockIsGreaterer(expected[i]) != v {
return false
}
i++
}
return i == len(expected)
}
func CreateStream(values ...int) Stream {
s := make(Stream)
go func() {
for _, val := range values {
s <- MockIsGreaterer(val)
}
close(s)
}()
return s
}
func TestReadFirstElementOfAllTheStreams(t *testing.T) {
s1 := CreateStream(0, 1)
arr := readFirstElementOfAllTheStreams([]Stream{s1})
if !reflect.DeepEqual(arr, []IsGreaterer{MockIsGreaterer(0)}) {
t.Error(arr)
}
if next := <-s1; next != MockIsGreaterer(1) {
t.Error("read too many element in the list")
return
}
s1 = CreateStream(0, 1, 2)
s2 := CreateStream(5, 6, 7)
s3 := CreateStream(90, 91, 92)
arr = readFirstElementOfAllTheStreams([]Stream{s1, s2, s3})
if !reflect.DeepEqual(arr, []IsGreaterer{MockIsGreaterer(0), MockIsGreaterer(5), MockIsGreaterer(90)}) {
t.Error(arr)
}
}
func TestAllStreamsButOne(t *testing.T) {
streams := []Stream{CreateStream(0, 10, 1), CreateStream(1, 10, 1), CreateStream(2, 10, 1)}
res := allStreamsButOne(streams, 1)
if len(res) != 2 {
t.Error()
}
if e := <-res[0]; e != MockIsGreaterer(0) {
t.Error()
}
if e := <-res[1]; e != MockIsGreaterer(2) {
t.Error()
}
}
func TestGetSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(t *testing.T) {
streams := []Stream{CreateStream(0, 1), CreateStream(3, 4), CreateStream(7, 8)}
stream, e, streams := getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams)
var _ = stream
_ = streams
if e != MockIsGreaterer(3) {
t.Error("getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams does not return the second smallest value", e)
}
if v := <-stream; v != MockIsGreaterer(0) {
t.Error("return streams is not the smalest", v)
}
if len(streams) != 2 {
t.Error("bad number of returned streams")
}
if len(streams) != 2 {
t.Error("bad number of returned streams")
}
}
func TestSortStreams(t *testing.T) {
if s := SortStreams(CreateStream(0, 1, 2)); !streamEqual(s, 0, 1, 2) {
t.Error()
}
if s := SortStreams(CreateStream(0, 1), CreateStream(2, 3)); !streamEqual(s, 0, 1, 2, 3) {
t.Error()
}
if s := SortStreams(CreateStream(0), CreateStream(2)); !streamEqual(s, 0, 2) {
t.Error()
}
if s := SortStreams(CreateStream(0, 5), CreateStream(2)); !streamEqual(s, 0, 2, 5) {
t.Error()
}
if s := SortStreams(CreateStream(0, 5), CreateStream(2, 7)); !streamEqual(s, 0, 2, 5, 7) {
t.Error()
}
if s := SortStreams(CreateStream(0, 5), CreateStream(2, 7, 9, 10, 12)); !streamEqual(s, 0, 2, 5, 7, 9, 10, 12) {
t.Error()
}
if s := SortStreams(CreateStream(), CreateStream(2, 7)); !streamEqual(s, 2, 7) {
t.Error()
}
if s := SortStreams(CreateStream(), CreateStream(2, 7), CreateStream(3, 8)); !streamEqual(s, 2, 3, 7, 8) {
t.Error()
}
if s := SortStreams(CreateStream()); !streamEqual(s) {
t.Error()
}
if s := SortStreams(CreateStream(1, 2), CreateStream(2, 3)); !streamEqual(s, 1, 2, 2, 3) {
t.Error()
}
}