commit a6da1637e7167d21c26dd0c5e13d0b8d49957e9a from: Sergey Bronnikov date: Sun Jul 07 08:47:17 2024 UTC molly: add a mix iterator The patch add a function that takes a random mixture of a number generators and chooses between them uniformly. commit - ea41c293ff9f6b36a7dbd2458dc4d3a2f98436ff commit + a6da1637e7167d21c26dd0c5e13d0b8d49957e9a blob - f839a057004a12b1739cc23db706c715c7416b19 blob + dda991b8c7b1c0576b388fd4d9e8691d53830306 --- CHANGELOG.md +++ CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http ### Added +- A `mix()` iterator. - A CAS-register generator. ### Changed blob - 3144c1444557b72128ccdf516302c8572a5d6e15 blob + 943ec8deed4dcc025103d3629bc10204ce072b28 --- molly/gen.lua +++ molly/gen.lua @@ -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) == '' then + table.insert(state, it) + end + end + return fun.wrap(mix_gen, nil, state) end methods.mix = mix exports.mix = mix blob - 176082a51d2c02c36c3f6ff9a2b36654b8135d18 blob + b9cd18cc5f84a4882e9f96de677adab51b6c271f --- test/tests.lua +++ test/tests.lua @@ -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) @@ -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)