Add 5 more exercises
This commit is contained in:
parent
3ef713d277
commit
8173ecdd8c
17 changed files with 520 additions and 0 deletions
25
hogwarts/README.md
Normal file
25
hogwarts/README.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
# hogwarts
|
||||
|
||||
Учебному офису школы чародейства и волшебства "Хогвартс".
|
||||
|
||||
В связи с назначением меня -- Долорес Амбридж -- директором вашей (ха-ха!) школы
|
||||
прошу подготовить учебный план для начинающих чародеев первого года. Высылаю
|
||||
список курсов и их пререквизитов.
|
||||
|
||||
Надеюсь, вы меня не подведете. Иначе будете объясняться будете с самим Министерством!
|
||||
|
||||
## Технические детали
|
||||
|
||||
Реализуйте функцию `GetCourseList`.
|
||||
|
||||
На вход дан словарь пререквезитов для каждого курса. На выходе нужно вернуть список курсов
|
||||
в порядке прохождения с учётом пререквизитов.
|
||||
|
||||
Если есть циклическая зависимость между курсами -- паникуйте!
|
||||
|
||||
### Примеры
|
||||
|
||||
Как запустить все тесты:
|
||||
```
|
||||
go test -v ./hogwarts/...
|
||||
```
|
7
hogwarts/courselist.go
Normal file
7
hogwarts/courselist.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !solution
|
||||
|
||||
package hogwarts
|
||||
|
||||
func GetCourseList(prereqs map[string][]string) []string {
|
||||
return []string{}
|
||||
}
|
89
hogwarts/courselist_test.go
Normal file
89
hogwarts/courselist_test.go
Normal 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
41
hotelbusiness/README.md
Normal 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
17
hotelbusiness/hotels.go
Normal 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{}
|
||||
}
|
75
hotelbusiness/hotels_test.go
Normal file
75
hotelbusiness/hotels_test.go
Normal 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
16
utf8reverse/README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# utf8reverse
|
||||
|
||||
Реализуйте функцию `utf8reverse.Reverse`.
|
||||
|
||||
Функция принимает на вход юникодную строку и должна возвращать строку,
|
||||
состояющую из тех же юникодных рун, но записанных в обратном порядке.
|
||||
|
||||
Обратите внимание в тестах, что некоторые графемы распадаются при обращении строки.
|
||||
Данный эффект связан с тем, что в юникоде некоторые руны умеют комбинироваться в одну графему.
|
||||
|
||||
### Примеры
|
||||
|
||||
Как запустить все тесты:
|
||||
```
|
||||
go test -v ./utf8reverse/...
|
||||
```
|
7
utf8reverse/reverse.go
Normal file
7
utf8reverse/reverse.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !solution
|
||||
|
||||
package utf8reverse
|
||||
|
||||
func Reverse(input string) string {
|
||||
return ""
|
||||
}
|
44
utf8reverse/reverse_test.go
Normal file
44
utf8reverse/reverse_test.go
Normal 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
24
utf8reverse/solution.go
Normal 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)
|
||||
}
|
13
utf8spacecollapse/README.md
Normal file
13
utf8spacecollapse/README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# utf8spacecollapse
|
||||
|
||||
Реализуйте функцию `utf8spacecollapse.CollapseSpaces`.
|
||||
|
||||
Функция принимает на вход юникодную строку и должна возвращать строку,
|
||||
состояющую из тех же символом, но где все подряд идущие группы пробельных символов заменены на ' ' (один обычный пробел).
|
||||
|
||||
### Примеры
|
||||
|
||||
Как запустить все тесты:
|
||||
```
|
||||
go test -v ./utf8spacecollapse/...
|
||||
```
|
7
utf8spacecollapse/collapse.go
Normal file
7
utf8spacecollapse/collapse.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !solution
|
||||
|
||||
package utf8spacecollapse
|
||||
|
||||
func CollapseSpaces(input string) string {
|
||||
return ""
|
||||
}
|
29
utf8spacecollapse/collapse_test.go
Normal file
29
utf8spacecollapse/collapse_test.go
Normal 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))
|
||||
})
|
||||
}
|
||||
}
|
30
utf8spacecollapse/solution.go
Normal file
30
utf8spacecollapse/solution.go
Normal 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
54
varjoin/README.md
Normal 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
7
varjoin/join.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !solution
|
||||
|
||||
package varjoin
|
||||
|
||||
func Join(args ...string) string {
|
||||
return ""
|
||||
}
|
35
varjoin/join_test.go
Normal file
35
varjoin/join_test.go
Normal 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...))
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue