No description, website, or topics provided.
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
config
lib
test
README.md
mix.exs

README.md

Информация

Задачата е за промяна на точките от домашни в курса по Elixir, проведен през лятната сесия 2017/2018 Тя носи общо 15 точки:

  • 10 за имплементация
  • 5 за тестове

Освен имплементацията на задачата, вие ще трябва да напишете и смислени тестове към нея.

По време на изпитването ще Ви бъдат задавани въпроси, свързани с имплементацията и познаването на езика. Неразбиране на написания от Вас код ще доведе до отнемане на точки.

Задачата няма краен срок - тя ще бъде оценена на място при провеждането на поправителния изпит на 25.08.2018г от 13:00 в зала 305.

Sequence

Задачата е да направите структура, която да имплементира генератор на редица.

Структурата трябва да е дефинирана в модул с името Sequence (вече имате създаден такъв във файла lib/sequence.ex). Всички функции за работа с нея трябва да се намират в същия модул.

Как трябва да изглежда вашата имплементация

Структурата трябва да има точно две полета, които да са задължитени:

  • :state e състоянието на редицата
  • :generator е функция която приема "състояние" и връща наредана двойка от генериран елемент и ново състояние

Например така бихме дефинирали генератор на естествените числа:

%Sequence{state: 0, generator: &{&1, &1 + 1}}

Какви функции трябва да имa в модула Sequence

Всички публични функции са описани в следващите параграфи. Вие можете да добавяте колкото искате не-експортирани функции (defp). Всяка публична функция от модула трябва да приема, като първо аргумент инстанция на Sequence.

Sequence.generate(seq)

Функцията връща наредена двойка, първия елемент от която е генерираният елемент, а вторият - нова редица със същия "генератор", но променено състояние.

iex> func = &{&1, &1 + 1}
iex> %Sequence{state: 0, generator: func} |> Sequence.generate
{0, %Sequence{state: 1, generator: func}}

За да можем да генерираме крайни редици ще приемем, че ако състоянието ни е nil, то вместо да върнем наредена двойка ще върнем nil и ще приемаме, че е редицата е приключила.

iex> %Sequence{state: nil, generator: fn(_) -> 1 end} |> Sequence.generate
nil

Sequence.generate_value(seq)

Прави същото като Sequence.generate/1, но връща само генерирания елемент без новата редица.

iex> %Sequence{state: 0, generator: &{&1, &1 + 1}} |> Sequence.generate_value
0

Ако редицата е приключила се случва грешка:

iex> %Sequence{state: nil, generator: fn(_) -> 1 end} |> Sequence.generate_value
** (FunctionClauseError) no function clause matching in ...

Sequence.generate_next(seq)

Прави същото като Sequence.generate/1, но връща само новата редица без генерирания елемент.

iex> func = &{&1, &1 + 1}
iex> %Sequence{state: 0, generator: func} |> Sequence.generate_next
%Sequence{state: 1, generator: func}

Ако редицата е приключила се връща nil

iex> %Sequence{state: nil, generator: fn(_) -> 1 end} |> Sequence.generate_next
nil

Sequence.advance(seq, n)

Функцията приема редица seq и цяло неотрицателно число n и връща нова редица, която е резултат от n кратното прилагане на Sequence.generate_next/1 върху редицата seq.

iex> func = &{&1, &1 + 1}
iex> seq = %Sequence{state: 0, generator: func}
iex> seq |> Sequence.advance 5
%Sequence{state: 5, generator: func}
iex> seq |> Sequence.advance 0
%Sequence{state: 0, generator: func}
iex> seq |> Sequence.advance -1
** (FunctionClauseError) no function clause matching in ...

Ако в някой момент от прилагането на Sequence.generate_next/1 бива върнат nil, то резултатът от Sequence.advance/2 също да е nil.

iex> %Sequence{state: nil, generator: fn(_) -> 1 end} |> Sequence.advance(5)
nil

Sequence.limit(seq, n)

Функцията приема редица seq и цяло неотрицателно число n и връща нова редица, която може да произведе не повече от n елемента.

Пример за лимитиране на безкрайна редица:

iex> seq = %Sequence{state: 0, generator: &{&1, &1 + 1}} |> Sequence.limit 2
iex> {n, seq} = seq |> Sequence.generate; n
0
iex> {n, seq} = seq |> Sequence.generate; n
1
iex> seq |> Sequence.generate
nil

Пример за лимитиране на крайна редица с по-малко елементи от лимита:

iex> seq = %Sequence{state: 0, generator: &{&1, nil}} |> Sequence.limit 2
iex> {n, seq} = seq |> Sequence.generate; n
0
iex> seq |> Sequence.generate
nil

Sequence.cycle(seq)

Функцията приема редица seq и връща нова. Ако дадената редица е крайна, новата редица трява да прдставлява първата с разликата, че всеки път когато достигне края си тя се връща в началното си състояние.

Например, ако seq генерира 1, 2, 3 то Sequence.cycle(seq) трябва да генерира 1, 2, 3, 1, 2, 3...

Ако редицата не е крайна, то новата редица генерира същите елементи, като старата (това не означава, че е непроменена).

Sequence.repeat(seq, n)

Функцията приема редица seq и цяло неотрицателно число n и връща нова. Ако дадената редица е крайна, новата редица, трябва да повтаря дадената n пъти.

Например, ако seq генерира 1, 2, 3 то Sequence.repeat(seq, 3) трябва да генерира 1, 2, 3, 1, 2, 3, 1, 2, 3.

Ако n не е цяло чило или е по-малко от 0, то се случва грешка. Ако редицата не е крайна, то новата редица генерира същите елементи, като старата (това не означава, че е непроменена).

Sequence.concatenate(seq_one, seq_two)

Функцията приема две редици seq_one и seq_two и връща нова. Ако seq_one е крайна върнатата редица се генерират първо елементите от seq_one, а после и тези от seq_two

Например, ако seq_one генерира 1, 2, 3, а seq_two генерира "one", "two", "three", то Sequence.concatenate(seq_one, seq_two) трябва да генерира 1, 2, 3, "one", "two", "three"

Имплементация на протоколи

За структурата трябва да имплементирате протокола Enumerable. За целта прочетете внимателно документацията на протокола.

Протоколът Enumerable трябва да ви позволи да подавате структурата, като първи аргумент на всички функции от модула Enum:

iex> seq = %Sequence{state: 0, &{&1, &1 + 1}}
iex> seq |> Enum.take 2
[0, 1]
iex> seq |> Enum.empty?
false
iex> seq |> Enum.any?(&(&1 > 5))
true
iex> seq |> Enum.at(5)
5

Някои от тях обаче няма да работят за безкрайни редици:

iex> seq = %Sequence{state: 0, &{&1, &1 + 1}}
iex> seq |> Enum.map(&(&1*&1))
безкрайна рекурсия
iex> seq |> Enum.any?(&(&1 < -2))
безкрайна рекурсия
iex> seq |> Enum.take_random()
първо тряябва да знае колко елементи има и чак тогава избира произволен от тях

За крайни редици обаче всички функции трябва да работят.