Slowsort w Elixirze i testująca dokumentacja
Za trzy dni będzie premiera Javy 9. Dlatego właśnie czas napisać coś o Elixirze. Dziś na tapecie ląduje jeden z ciekawszych elementów języka, czyli wykonywalna dokumentacja.
Chwila teorii
Gdy dokumentujemy jakiś fragment kodu, to często aż prosi się dołączyć jakiś przykład. Coś w rodzaju dla takich danych otrzymasz taki rezultat.
Wszystko fajnie, bo taki przykładowy happy path pozwala na zrozumienie kodu. Z drugiej strony przykład zazwyczaj kopiujemy z testów, bo tam jest
gotowe, a później, jak się testy zmienią, to już niekoniecznie aktualizujemy dokumentację.
A gdyby tak w dokumentacji zaszyć testy i niech narzędzia martwią się jak to uruchomić?
Chwila praktyki
Na tapetę wziąłem algorytm Slowsort W. Z kilku powodów. Po pierwsze jestem w trakcie pisania kursu ze struktur danych na potrzeby naszego centrum szkoleniowego i implementacja slowsorta jest całkiem fajnym ćwiczeniem. Proste, a jednocześnie nietypowe. Po drugie chcę wykorzystać ten kod w czasie mojej prezentacji na JDD. Po trzecie, jak już implementować coś głupiego, to niech to będzie naprawdę głupie 😀
Kod wygląda tak:
Listing 1. implementacja Slowsort w Elixirze
defmodule Slowsort do
@moduledoc """
Documentation for Slowsort.
"""
@doc """
Example implementation of Slowsort algorithm.
## Examples
iex> Slowsort.sort([0])
[0]
iex> Slowsort.sort([1,0])
[0, 1]
iex> Slowsort.sort([3, 11, 1, 0, 22, 2])
[0, 1, 2, 3, 11, 22]
"""
def sort(numbers) do
l = length numbers
slowsort(numbers, 0, l - 1) # we need maximum index that is length - 1
end
defp slowsort(numbers, i, j) when i>=j, do: numbers
defp slowsort(numbers, i, j) do
m = Integer.floor_div((i + j), 2)
slowsort(numbers, i, m)
|> slowsort(m + 1, j)
|> swap(j, m)
|> slowsort(i, j - 1)
end
defp swap(numbers, j, m) do
max_left = Swap.nth(numbers, m);
max_rigth = Swap.nth(numbers, j);
swap(numbers, j, m, max_rigth, max_left)
end
defp swap(numbers, j, m, mr, ml) when mr = ml, do: numbers
end
Do tego oczywiście są jeszcze testy:
Listing 2. Zestaw testów
defmodule SlowsortTest do
use ExUnit.Case
@moduletag timeout: 1000
doctest Slowsort
test "the empty array" do
sorted = Slowsort.sort []
assert sorted === []
end
test "the simple array of two elements" do
sorted = Slowsort.sort [1, 0]
assert sorted === [0, 1]
end
test "the array of many elements" do
sorted = Slowsort.sort [3, 11, 1, 0, 22, 2]
assert sorted === [0, 1, 2, 3, 11, 22]
end
end
Technicznie są tu dwa zestawy testów. Zwróćcie uwagę na dokumentację funkcji sort
. Zdefiniowana jest tam sekcja Examples
, która wygląda jak
fragment kodu w markdownie. I rzeczywiście, jeżeli wygenerujemy dokumentację, to otrzymamy jakiś nagłówek. Znacznie ciekawiej jest, jeżeli uruchomimy
testy za pomocą mix test
.
Listing 4. Zrzut z testów
Compiling 1 file (.ex)
......
Finished in 0.1 seconds
6 tests, 0 failures
Uruchomiono sześć testów. Trzy zdefiniowanie w slowsort_test
i trzy zdefiniowane w dokumentacji.
Podsumowanie
Możliwość zdefiniowania prostych testów bezpośrednio w dokumentacji jest bardzo fajnym rozwiązaniem. Pozwala to na jednoczesną aktualizację testów, jak i dokumentacji. Szkoda, że w Javie nie ma takiego narzędzia. Oj szkoda…
Kod dostępny jest tutaj.