Skip to content

Missing primitive take_from(bag, amount) #209

@spicychickensauce

Description

@spicychickensauce

First of, thank you for creating and maintaining this great library!

I think there is a missing primitive which I needed quite often when writing generators: take_from(bag, amount)

Sample usage:

cards = [:jack, :queen, :king, :ace, 2, 3, 4, 5, 6, 7, 8, 9, 10]
check all(
        {hand_p1, remaining} <- take_from(cards, 2),
        {hand_p2, _remaining} <- take_from(remaining, 2)
      ) do
  # assert some poker logic
end

I think this should be in the library, since I struggled quite a bit with implementing it myself.
First I tried this horrible mess:
It works, but throws StreamData.TooManyDuplicatesError quite often.

defp take_from(bag, n) do
  indexed_bag =
    Enum.with_index(bag)

  StreamData.uniq_list_of(StreamData.one_of(indexed_bag |> Enum.map(&StreamData.constant/1)),
    length: n
  )
  |> StreamData.map(fn indexed_items ->
    {taken_items, taken_indexes} =
      Enum.unzip(indexed_items)

    taken_indexes = MapSet.new(taken_indexes)

    new_bag =
      indexed_bag
      |> Enum.reject(fn {_, i} -> MapSet.member?(taken_indexes, i) end)
      |> Enum.map(&elem(&1, 0))

    {new_bag, taken_items}
  end)
end

I then improved it quite a bit with this recursive version:

defp take_from(bag, n), do: do_take_from(bag, n, [])

defp do_take_from(bag, 0, acc), do: StreamData.constant({acc, bag})

defp do_take_from(bag, n, acc) do
  StreamData.one_of(Enum.with_index(bag) |> Enum.map(&StreamData.constant/1))
  |> StreamData.bind(fn {item, i} ->
    new_bag = List.delete_at(bag, i)
    do_take_from(new_bag, n - 1, [item | acc])
  end)
end

But I still think there could be a better version, probably one that would use Enum.take_random/2.

But I can't figure it out.

Any ideas? Could we get this into the library?

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions