Created
August 25, 2015 13:21
-
-
Save sanmiguel/95c9ede7fb5255b5942f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -module(stung_op). | |
| -type weight() :: non_neg_integer(). %% Non-normalised weight | |
| -type nweight() :: float(). %% Internal use only - normalised weight | |
| %% Inputs | |
| %% Op :: {Name, Args :: list(any())}. | |
| %% Choice :: {Op, Weight, NextChoices}. | |
| %% | |
| -type choice(T) :: {Op :: T, nweight() | weight(), list(choice(T))}. | |
| -type choices(T) :: list(choice(T)). | |
| %% Outputs - these should be opaque and used internally only | |
| -type chain(T) :: zipper_forests:zlist(T). | |
| -type forest(_) :: zipper_forests:zipper_forest(). | |
| -export_type([ | |
| choice/1 | |
| , choices/1 | |
| , chain/1 | |
| ]). | |
| -export_type([weight/0]). | |
| -export_type([nweight/0]). | |
| -export([ | |
| forest/1 | |
| , new/0 | |
| , normalise/1 | |
| , add_choices/2 | |
| , subforest/2 | |
| , meander/1 | |
| ]). | |
| -spec forest(choices(T)) -> forest(T). | |
| forest(Choices) -> | |
| add_choices(Choices, new()). | |
| -spec new() -> forest(_). | |
| new() -> | |
| zipper_forests:delete(zipper_forests:root(root)). | |
| -spec normalise(choices(_)) -> list(nweight()). | |
| normalise(Choices) -> | |
| Sum = lists:foldl(fun ({_, W, _}, S) -> W + S end, 0, Choices), | |
| [ { Op, X / Sum, C} || {Op, X, C} <- Choices ]. | |
| -spec add_choices(choices(T), forest(T)) -> forest(T). | |
| add_choices(Choices, Starter) -> | |
| lists:foldl(fun subforest/2, Starter, normalise(Choices)). | |
| -spec subforest(choice(T), forest(T)) -> forest(T). | |
| subforest({Op, Weight, []}, Forest) -> | |
| zipper_forests:insert({Op, Weight}, Forest); | |
| subforest({Op, Weight, Children}, Forest) -> | |
| %% Normalise weights: | |
| F0 = zipper_forests:insert({Op, Weight}, Forest), | |
| %% Descend | |
| F1 = zipper_forests:children(F0), | |
| %% Add children | |
| F2 = lists:foldl(fun subforest/2, F1, normalise(Children)), | |
| %% Return to parent | |
| zipper_forests:parent(F2). | |
| -spec meander(forest(T)) -> list(T). | |
| meander(Forest) -> | |
| %% TODO: Wwe might need to ensure that the Forest gets reset | |
| %% or we could leave that as an exercise for the reader... | |
| meander(pick(), [], Forest). | |
| meander(Pick, Path, Forest) -> | |
| case zipper_forests:value(Forest) of | |
| undefined -> | |
| lists:reverse(Path); | |
| {ok, {Op, Weight}} -> | |
| Next = Pick - Weight, | |
| if | |
| Next =< 0 -> | |
| %% Bingo! | |
| meander(pick(), [Op | Path], zipper_forests:children(Forest)); | |
| Next > 0 -> | |
| %% Better luck next time | |
| meander(Next, Path, zipper_forests:next(Forest)) | |
| end | |
| end. | |
| pick() -> | |
| crypto:rand_uniform(1, 1000) / 1000.0. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment