Add 5 more exercises

This commit is contained in:
Ivan Puzyrevskiy 2020-02-24 22:54:48 +03:00
parent 3ef713d277
commit 8173ecdd8c
17 changed files with 520 additions and 0 deletions

25
hogwarts/README.md Normal file
View file

@ -0,0 +1,25 @@
# hogwarts
Учебному офису школы чародейства и волшебства "Хогвартс".
В связи с назначением меня -- Долорес Амбридж -- директором вашей (ха-ха!) школы
прошу подготовить учебный план для начинающих чародеев первого года. Высылаю
список курсов и их пререквизитов.
Надеюсь, вы меня не подведете. Иначе будете объясняться будете с самим Министерством!
## Технические детали
Реализуйте функцию `GetCourseList`.
На вход дан словарь пререквезитов для каждого курса. На выходе нужно вернуть список курсов
в порядке прохождения с учётом пререквизитов.
Если есть циклическая зависимость между курсами -- паникуйте!
### Примеры
Как запустить все тесты:
```
go test -v ./hogwarts/...
```

7
hogwarts/courselist.go Normal file
View file

@ -0,0 +1,7 @@
// +build !solution
package hogwarts
func GetCourseList(prereqs map[string][]string) []string {
return []string{}
}

View file

@ -0,0 +1,89 @@
package hogwarts
import (
"testing"
"github.com/stretchr/testify/require"
)
func impl(t *testing.T, prereqs map[string][]string, courseList []string) {
learned := make(map[string]bool)
for index, course := range courseList {
for _, prereq := range prereqs[course] {
if !learned[prereq] {
t.Errorf("course %v (index %v) depends on course %v which is not taken yet", course, index, prereq)
}
}
learned[course] = true
}
for course := range prereqs {
if !learned[course] {
t.Errorf("course %v is missing on the list", course)
}
}
}
func TestGetCourseList_computerScience(t *testing.T) {
var computerScience = map[string][]string{
"algorithms": {"data structures"},
"calculus": {"linear algebra"},
"compilers": {
"data structures",
"formal languages",
"computer organization",
},
"data structures": {"discrete math"},
"databases": {"data structures"},
"discrete math": {"intro to programming"},
"formal languages": {"discrete math"},
"networks": {"operating systems"},
"operating systems": {"data structures", "computer organization"},
"programming languages": {"data structures", "computer organization"},
}
impl(t, computerScience, GetCourseList(computerScience))
}
func TestGetCourseList_linearScience(t *testing.T) {
var linearScience = map[string][]string{
"1": {"0"},
"2": {"1"},
"3": {"2"},
"4": {"3"},
"5": {"4"},
"6": {"5"},
"7": {"6"},
"8": {"7"},
"9": {"8"},
}
impl(t, linearScience, GetCourseList(linearScience))
}
func TestGetCourseList_naiveScience(t *testing.T) {
var naiveScience = map[string][]string{
"здравый смысл": {},
"русский язык": {"здравый смысл"},
"литература": {"здравый смысл"},
"иностранный язык": {"здравый смысл"},
"алгебра": {"здравый смысл"},
"геометрия": {"здравый смысл"},
"информатика": {"здравый смысл"},
"история": {"здравый смысл"},
"обществознание": {"здравый смысл"},
"география": {"здравый смысл"},
"биология": {"здравый смысл"},
"физика": {"здравый смысл"},
"химия": {"здравый смысл"},
"музыка": {"здравый смысл"},
}
impl(t, naiveScience, GetCourseList(naiveScience))
}
func TestGetCourseList_weirdScience(t *testing.T) {
var weirdScience = map[string][]string{
"купи": {"продай"},
"продай": {"купи"},
}
require.Panics(t, func() {
impl(t, weirdScience, GetCourseList(weirdScience))
})
}

41
hotelbusiness/README.md Normal file
View file

@ -0,0 +1,41 @@
# hotelbusiness
Здравствуйте, Дональд Крямп!
Ваша империя ширится, и теперь в вашем владении премиальный курорт "Koza Hutor: Resort, Spa, Golang 5*".
Поздравляем с успешным приобретением!
Правда, есть нюанс.
Ваш твиттер настолько заряжен энергей на успех и богатство, что предыдущий управляющий курорта сбежал,
едва узнав, что вы собственной персоной планируете посетить курорт!
Ваш верный comrade Эдвард Санден немного разобрался в IT-системах курорта и выяснил,
что курорт интегрирован в международный сервис "Gluking.com" и что к вам уже вот-вот нагрянут гости!
Эдварду удалось выгрузить информацио о заездах и выездах будущих гостей -- см. структуру `hotelbusiness.Guest`.
По техническим причинам все даты заменены на цифры. Эдвард говорит, что ноль -- значит сегодня.
Менеджер по работе с гостями (его зовут Валентин) утверждает, сбежавший управляющий (его имя еще не установлено)
всегда передавал ему информацию о гостях в другом виде. А именно -- см. структуру `hotelbusiness.Load` --
указание, с какой даты сколько гостей ожидается на курорте.
В таком виде Валентин может рассчитать необходмые закупки продуктов, график работы персонала и другие мелочи,
чтобы курорт функционировал в штатном режиме.
Эдвард бы рад рассчитать данные для Валентина в требуемом виде, но, говорит, интернет в Шереметьево так себе.
Давайте покажем всем завистникам вашу мощь и талант! Напишите функцию `hotelbusiness.ComputeLoad`,
покажите всему твиттеру, что еще есть порох в пороховицах!
### Технические уточнения от Валентина
* Если в один день гости и выезжают, и заезжают, то Валентину важно знать количество гостей к ужину: то есть когда все выезжающие выехали, а все заезжающие заехали.
* Для упрощения работы Валентин просит сообщать ему только о датах, когда изменяется загрузка курорта.
### Примеры
Как запустить все тесты:
```
go test -v ./hotelbusiness/...
```

17
hotelbusiness/hotels.go Normal file
View file

@ -0,0 +1,17 @@
// +build !solution
package hotelbusiness
type Guest struct {
CheckInDate int
CheckOutDate int
}
type Load struct {
StartDate int
GuestCount int
}
func ComputeLoad(guests []Guest) []Load {
return []Load{}
}

View file

@ -0,0 +1,75 @@
package hotelbusiness
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestComputeLoad_basic(t *testing.T) {
for _, tc := range []struct {
title string
guests []Guest
result []Load
}{
{
title: "empty input",
guests: []Guest{},
result: []Load{},
},
{
title: "one guest",
guests: []Guest{{1, 2}},
result: []Load{{1, 1}, {2, 0}},
},
{
title: "two guests, one-by-one, without any gaps",
guests: []Guest{{1, 2}, {2, 3}},
result: []Load{{1, 1}, {3, 0}},
},
{
title: "two guests, one-by-one, with a gap",
guests: []Guest{{1, 2}, {3, 4}},
result: []Load{{1, 1}, {2, 0}, {3, 1}, {4, 0}},
},
{
title: "two guests, together",
guests: []Guest{{1, 2}, {1, 2}},
result: []Load{{1, 2}, {2, 0}},
},
{
title: "overlapping",
guests: []Guest{{1, 3}, {3, 5}, {2, 4}},
result: []Load{{1, 1}, {2, 2}, {4, 1}, {5, 0}},
},
{
title: "stairs",
guests: []Guest{{1, 6}, {2, 5}, {3, 4}},
result: []Load{{1, 1}, {2, 2}, {3, 3}, {4, 2}, {5, 1}, {6, 0}},
},
} {
t.Run(tc.title, func(t *testing.T) {
require.Equal(t, tc.result, ComputeLoad(tc.guests))
})
}
}
func TestComputeLoad_stress1(t *testing.T) {
n := 1000000
g := make([]Guest, 0, 1000000)
for i := 0; i < n; i++ {
g = append(g, Guest{1, 2})
}
l := ComputeLoad(g)
require.Equal(t, []Load{{1, n}, {2, 0}}, l)
}
func TestComputeLoad_stress2(t *testing.T) {
n := 1000000
g := make([]Guest, 0, 1000000)
for i := 0; i < n; i++ {
g = append(g, Guest{i, i + 1})
}
l := ComputeLoad(g)
require.Equal(t, []Load{{0, 1}, {n, 0}}, l)
}

16
utf8reverse/README.md Normal file
View file

@ -0,0 +1,16 @@
# utf8reverse
Реализуйте функцию `utf8reverse.Reverse`.
Функция принимает на вход юникодную строку и должна возвращать строку,
состояющую из тех же юникодных рун, но записанных в обратном порядке.
Обратите внимание в тестах, что некоторые графемы распадаются при обращении строки.
Данный эффект связан с тем, что в юникоде некоторые руны умеют комбинироваться в одну графему.
### Примеры
Как запустить все тесты:
```
go test -v ./utf8reverse/...
```

7
utf8reverse/reverse.go Normal file
View file

@ -0,0 +1,7 @@
// +build !solution
package utf8reverse
func Reverse(input string) string {
return ""
}

View file

@ -0,0 +1,44 @@
package utf8reverse
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestReverse(t *testing.T) {
for i, tc := range []struct {
input string
output string
}{
{input: "", output: ""},
{input: "x", output: "x"},
{input: "Hello!", output: "!olleH"},
{input: "Привет", output: "тевирП"},
{input: "\r\n", output: "\n\r"},
{input: "\n\n", output: "\n\n"},
{input: "\t*", output: "*\t"},
// NB: Диакритика съехала!
{input: "möp", output: "p̈om"},
// NB: Иероглиф развалился!,
{input: "뢴", output: "ᆫᅬᄅ"},
{input: "Hello, 世界", output: "界世 ,olleH"},
{input: "ำ", output: "ำ"},
{input: "ำำ", output: "ำำ"},
// NB: Эмоджи распался.
{input: "👩‍❤️‍💋‍👩", output: "👩‍💋‍️❤‍👩"},
// NB: Эмоджи распался.
{input: "🏋🏽‍♀️", output: "️♀\u200d🏽🏋"},
{input: "🙂", output: "🙂"},
{input: "🙂🙂", output: "🙂🙂"},
// NB: DE != ED
{input: "🇩🇪", output: "🇪🇩"},
// NB: Флаг распался. :)
{input: "🏳️‍🌈", output: "🌈‍️🏳"},
} {
t.Run(fmt.Sprintf("#%v: %v", i, tc.input), func(t *testing.T) {
require.Equal(t, tc.output, Reverse(tc.input))
})
}
}

24
utf8reverse/solution.go Normal file
View file

@ -0,0 +1,24 @@
// +build solution
package utf8reverse
import (
"unicode/utf8"
)
func Reverse(input string) string {
rs := []rune{}
sz := 0
for len(input) > 0 {
r, n := utf8.DecodeRuneInString(input)
rs = append(rs, r)
sz += utf8.RuneLen(r)
input = input[n:]
}
bs := make([]byte, sz)
for i, j := 0, 0; i < len(rs); i++ {
n := utf8.EncodeRune(bs[j:], rs[len(rs)-i-1])
j += n
}
return string(bs)
}

View file

@ -0,0 +1,13 @@
# utf8spacecollapse
Реализуйте функцию `utf8spacecollapse.CollapseSpaces`.
Функция принимает на вход юникодную строку и должна возвращать строку,
состояющую из тех же символом, но где все подряд идущие группы пробельных символов заменены на ' ' (один обычный пробел).
### Примеры
Как запустить все тесты:
```
go test -v ./utf8spacecollapse/...
```

View file

@ -0,0 +1,7 @@
// +build !solution
package utf8spacecollapse
func CollapseSpaces(input string) string {
return ""
}

View file

@ -0,0 +1,29 @@
package utf8spacecollapse
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestCollapseSpaces(t *testing.T) {
for i, tc := range []struct {
input string
output string
}{
{input: "", output: ""},
{input: "x", output: "x"},
{input: "Hello, World!", output: "Hello, World!"},
{input: "Привет,\tМир!", output: "Привет, Мир!"},
{input: "\r\n", output: " "},
{input: "\n\n", output: " "},
{input: "\t*", output: " *"},
{input: " \t \t ", output: " "},
{input: " \tx\t ", output: " x "},
} {
t.Run(fmt.Sprintf("#%v: %v", i, tc.input), func(t *testing.T) {
require.Equal(t, tc.output, CollapseSpaces(tc.input))
})
}
}

View file

@ -0,0 +1,30 @@
// +build solution
package utf8spacecollapse
import (
"unicode"
"unicode/utf8"
)
func CollapseSpaces(input string) string {
res := make([]byte, len(input))
pos := 0
lastWasSpace := false
for len(input) > 0 {
r, n := utf8.DecodeRuneInString(input)
input = input[n:]
if unicode.IsSpace(r) {
if lastWasSpace {
continue
}
res[pos] = ' '
pos++
lastWasSpace = true
} else {
pos += utf8.EncodeRune(res[pos:], r)
lastWasSpace = false
}
}
return string(res[:pos])
}

54
varjoin/README.md Normal file
View file

@ -0,0 +1,54 @@
# varfmt
Реализуйте функцию `varfmt.Sprintf`. Функция принимает формат строку и переменное число аргументов.
Синтаксис формат-строки похож на формат-строки питона:
- `{}` - задаёт ссылку на аргумент
- `{number}` - ссылается на аргумент с индексом `number`
- `{}` ссылается на аргумент с индексом равным позиции `{}` внутри паттерна
Например, `varfmt.Sprintf("{1} {0}", "Hello", "World)` должен вернуть строку `World Hello`.
Аргументы функции могут быть произвольными типами. Вам нужно форматировать их так же, как это
делает функция `fmt.Sprint`. Вызывать `fmt.Sprint` для форматирования отдельного аргумента
не запрещается.
Ваше решение будет сравниваться с baseline-решением на бенчмарке. Сравнение будет
проходить независимо по трем метрикам.
- `time/op` - время на одну итерацию бенчмарка
- `alloc/op` - число выделенных байт на одну итерацию бенчмарка
- `allocs/op` - число выделенных объектов на одну итерацию бенчмарка
Ваш код должен быть не более чем в два раза хуже чем baseline.
```
goos: linux
goarch: amd64
pkg: gitlab.com/slon/shad-go/varfmt
BenchmarkFormat/small_int-4 4744729 263 ns/op 64 B/op 4 allocs/op
BenchmarkFormat/small_string-4 2388128 484 ns/op 168 B/op 8 allocs/op
BenchmarkFormat/big-4 8997 127827 ns/op 194656 B/op 41 allocs/op
BenchmarkSprintf/small-4 13330094 85.7 ns/op 2 B/op 1 allocs/op
BenchmarkSprintf/small_string-4 9351295 123 ns/op 16 B/op 1 allocs/op
BenchmarkSprintf/big-4 12006 108144 ns/op 16392 B/op 1 allocs/op
PASS
```
### Примеры
Как запустить все тесты и бенчмарки:
```
go test -v -bench=. ./varfmt/...
```
Как запустить только бенчмарки:
```
go test -v -run=^a -bench=. ./varfmt/...
```
Здесь `^a` - регулярное выражение, задающее тесты для запуска,
а `.` - задаёт бенчмарки.
Как запустить только big бенчмарки:
```
go test -v -run=^a -bench=/big ./varfmt/...
```

7
varjoin/join.go Normal file
View file

@ -0,0 +1,7 @@
// +build !solution
package varjoin
func Join(args ...string) string {
return ""
}

35
varjoin/join_test.go Normal file
View file

@ -0,0 +1,35 @@
package varjoin
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFormat(t *testing.T) {
for _, tc := range []struct {
sep string
args []string
result string
}{
{
sep: "/",
args: []string{},
result: "",
},
{
sep: "/",
args: []string{"1", "2", "3"},
result: "1/2/3",
},
{
sep: "",
args: []string{"h", "e", "ll", "o"},
result: "hello",
},
} {
t.Run(tc.result, func(t *testing.T) {
require.Equal(t, tc.result, Join(tc.sep, tc.args...))
})
}
}