Skip to content

Commit

Permalink
molly: add a mix iterator
Browse files Browse the repository at this point in the history
The patch add a function that takes a random mixture of a number
generators and chooses between them uniformly.
  • Loading branch information
ligurio committed Jul 7, 2024
1 parent ea41c29 commit a6da163
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- A `mix()` iterator.
- A CAS-register generator.

### Changed
Expand Down
53 changes: 45 additions & 8 deletions molly/gen.lua
Original file line number Diff line number Diff line change
Expand Up @@ -387,19 +387,56 @@ local cycle_times = function()
end
methods.cycle_times = cycle_times

--- (TODO) A random mixture of several generators. Takes a collection of generators
-- and chooses between them uniformly.
local mix_gen

mix_gen = function(_, state)
assert(type(state) == 'table')
local len = table.getn(state)
if len == 0 then
return nil, nil
end
local nth = math.random(len)
local it = state[nth]
local gen1, param1, state1 = unwrap(it)
local state2, value = gen1(param1, state1)
if value == nil then
table.remove(state, nth)
return mix_gen(nil, state)
end
state[nth] = fun.wrap(gen1, param1, state2)
return state, value
end

--- A random mixture of a number generators. Takes a collection of
-- generators and chooses between them uniformly.
--
-- To be precise, a mix behaves like a sequence of one-time, randomly selected
-- generators from the given collection. This is efficient and prevents
-- multiple generators from competing for the next slot, making it hard to
-- control the mixture of operations.
-- @usage
-- > molly.gen.range(1, 5):mix(molly.gen.range(5, 10)):totable()
-- ---
-- - - 1
-- - 5
-- - 2
-- - 3
-- - 6
-- - 7
-- - 4
-- - 8
-- - 9
-- - 5
-- - 10
--
-- @param ... - an iterators
-- @return an iterator
-- @function mix
local mix = function()
-- TODO
local function mix(...)
local params = {...}
local state = {}
for _, it in ipairs(params) do
if tostring(it) == '<generator>' then
table.insert(state, it)
end
end
return fun.wrap(mix_gen, nil, state)
end
methods.mix = mix
exports.mix = mix
Expand Down
9 changes: 8 additions & 1 deletion test/tests.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ local utils = molly.utils
local seed = os.time()
math.randomseed(seed)

test:plan(11)
test:plan(12)

test:test('clock', function(test)
test:plan(5)
Expand Down Expand Up @@ -219,6 +219,13 @@ test:test('tests.list_append_gen', function(test)
test:is(type(op_val) == 'number' or op_val == json.NULL, true, "tests.list_append_gen(): op value")
end)

test:test('gen.mix', function(test)
test:plan(1)

local tbl = molly.gen.range(1, 5):mix(molly.gen.range(5, 10)):totable()
test:is(#tbl, 11, 'length of items generated by gen.mix')
end)

test:test('runner', function(test)
test:plan(4)

Expand Down

0 comments on commit a6da163

Please sign in to comment.