I'm trying to understand how to use scalaz State
to perform a complicated stateful computation. Here is the problem:
Given a
List[Int]
of potential divisors and aList[Int]
of numbers, find aList[(Int, Int)
] of matching pairs (divisor, number) where a divisor is allowed to match at most one number.
As a test:
def findMatches(divs: List[Int], nums: List[Int]): List[(Int, Int)]
And with the following input:
findMatches( List(2, 3, 4), List(1, 6, 7, 8, 9) )
We can get at most 3 matches. If we stipulate that the matches must be made in the order in which they occur traversing the lists l-r, then the matches must be:
List( (2, 6) , (3, 9) , (4, 8) )
So the following two tests need to pass:
assert(findMatches(List(2, 3, 4), List(1, 6, 7, 8, 9)) == List((2, 6), (3, 9), (4, 8)))
assert(findMatches(List(2, 3, 4), List(1, 6, 7, 8, 11)) == List((2, 6), (4, 8)))
Here's an imperative solution:
scala> def findMatches(divs: List[Int], nums: List[Int]): List[(Int, Int)] = {
| var matches = List.empty[(Int, Int)]
| var remaining = nums
| divs foreach { div =>
| remaining find (_ % div == 0) foreach { n =>
| remaining = remaining filterNot (_ == n)
| matches = matches ::: List(div -> n)
| }
| }
| matches
| }
findMatches: (divs: List[Int], nums: List[Int])List[(Int, Int)]
Notice that I have to update the state of remaining
as well as accumulating matches
. It sounds like a job for scalaz traverse!
My useless working has got me this far:
scala> def findMatches(divs: List[Int], nums: List[Int]): List[(Int, Int)] = {
| divs.traverse[({type l[a] = State[List[Int], a]})#l, Int]( div =>
| state { (rem: List[Int]) => rem.find(_ % div == 0).map(n => rem.filterNot(_ == n) -> List(div -> n)).getOrElse(rem -> List.empty[(Int, Int)]) }
| ) ~> nums
| }
<console>:15: error: type mismatch;
found : List[(Int, Int)]
required: Int
state { (rem: List[Int]) => rem.find(_ % div == 0).map(n => rem.filterNot(_ == n) -> List(div -> n)).getOrElse(rem -> List.empty[(Int, Int)]) }
^