Skip to content

Instantly share code, notes, and snippets.

@sanmiguel
Created August 25, 2015 13:21
Show Gist options
  • Select an option

  • Save sanmiguel/95c9ede7fb5255b5942f to your computer and use it in GitHub Desktop.

Select an option

Save sanmiguel/95c9ede7fb5255b5942f to your computer and use it in GitHub Desktop.
-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