16

scalazStateを使用して複雑なステートフルな計算を実行する方法を理解しようとしています。問題は次のとおりです。

List[Int]潜在的な約数の aと数の a を指定して、一致するペア (除数、数)List[Int]の ] を見つけます。ここで、約数は最大 1 つの数と一致することが許可されています。List[(Int, Int)

テストとして:

def findMatches(divs: List[Int], nums: List[Int]): List[(Int, Int)]

そして、次の入力を使用します。

findMatches( List(2, 3, 4), List(1, 6, 7, 8, 9) )

最大で 3 試合を獲得できます。リスト lr をトラバースして発生する順序で一致を作成する必要があると規定すると、一致は次のようになります。

List( (2, 6) ,  (3, 9) , (4, 8) )

したがって、次の 2 つのテストに合格する必要があります。

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)))

緊急の解決策は次のとおりです。

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)]

remainingの状態と累積を更新する必要があることに注意してくださいmatches。scalaz traverse のお仕事みたいですね!

私の無駄な作業は、私をここまでさせました:

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)]) }
                                                                                                                                       ^
4

2 に答える 2

16

StateとTraverseを使用するには、コードを少し変更するだけで済みます。

// using scalaz-seven
import scalaz._
import Scalaz._

def findMatches(divs: List[Int], nums: List[Int]) = {

  // the "state" we carry when traversing
  case class S(matches: List[(Int, Int)], remaining: List[Int])

  // initially there are no found pairs and a full list of nums
  val initialState = S(List[(Int, Int)](), nums)

  // a function to find a pair (div, num) given the current "state"
  // we return a state transition that modifies the state
  def find(div: Int) = modify((s: S) => 
    s.remaining.find(_ % div == 0).map { (n: Int) => 
      S(s.matches :+ div -> n, s.remaining -n)
    }.getOrElse(s))

  // the traversal, with no type annotation thanks to Scalaz7
  // Note that we use `exec` to get the final state
  // instead of `eval` that would just give us a List[Unit].
  divs.traverseS(find).exec(initialState).matches
}

// List((2,6), (3,9), (4,8))
findMatches(List(2, 3, 4), List(1, 6, 7, 8, 9))

runTraverseSトラバーサルを少し異なる方法で記述するために使用することもできます。

 divs.runTraverseS(initialState)(find)._2.matches
于 2012-02-08T13:53:31.713 に答える
1

いろいろいじった後、私は最終的にこれを理解しました:

scala> def findMatches(divs: List[Int], nums: List[Int]): List[(Int, Int)] = {
     | (divs.traverse[({type l[a] = State[List[Int], a]})#l, Option[(Int, Int)]]( div =>
     |   state { (rem: List[Int]) => 
     |     rem.find(_ % div == 0).map(n => rem.filterNot(_ == n) -> Some(div -> n)).getOrElse(rem -> none[(Int, Int)]) 
     |   }
     | ) ! nums).flatten
     | }
findMatches: (divs: List[Int], nums: List[Int])List[(Int, Int)]

ただし、実際に何が起こっているのかについての洞察を得るために、エリックの答えを見ていると思います。


反復 #2

scalaz6 を使用してエリックの答えを探る

scala> def findMatches2(divs: List[Int], nums: List[Int]): List[(Int, Int)] = {
     |   case class S(matches: List[(Int, Int)], remaining: List[Int])
     |   val initialState = S(nil[(Int, Int)], nums)
     |   def find(div: Int, s: S) = {
     |     val newState = s.remaining.find(_ % div == 0).map { (n: Int) =>
     |       S(s.matches :+ div -> n, s.remaining filterNot (_ ==  n))
     |     }.getOrElse(s)
     |     newState -> newState.matches
     |   }
     |   val findDivs = (div: Int) => state((s: S) => find(div, s))
     |   (divs.traverse[({type l[a]=State[S, a]})#l, List[(Int, Int)]](findDivs) ! initialState).join
     | }
findMatches2: (divs: List[Int], nums: List[Int])List[(Int, Int)]

scala> findMatches2(List(2, 3, 4), List(1, 6, 7, 8, 9))
res11: List[(Int, Int)] = List((2,6), (2,6), (3,9), (2,6), (3,9), (4,8))

最後のjoinオンはList[List[(Int, Int)]]悲しみを引き起こしています。代わりに、最後の行を次のように置き換えることができます。

(divs.traverse[({type l[a]=State[S, a]})#l, List[(Int, Int)]](findDivs) ~> initialState).matches

反復 #3

実際、状態計算の余分な出力を完全になくして、さらに単純化することができます。

scala> def findMatches2(divs: List[Int], nums: List[Int]): List[(Int, Int)] = {
     | case class S(matches: List[(Int, Int)], remaining: List[Int])
     | def find(div: Int, s: S) =
     |   s.remaining.find(_ % div == 0).map( n => S(s.matches :+ div -> n, s.remaining filterNot (_ ==  n)) ).getOrElse(s) -> ()
     | (divs.traverse[({type l[a]=State[S, a]})#l, Unit](div => state((s: S) => find(div, s))) ~> S(nil[(Int, Int)], nums)).matches
     | }
findMatches2: (divs: List[Int], nums: List[Int])List[(Int, Int)]

反復 #4

modify上記のApocalispによる記述は scalaz6 でも利用可能であり、(S, ())ペアを明示的に指定する必要がなくなります (ラムダ型では必要ですがUnit):

scala> def findMatches2(divs: List[Int], nums: List[Int]): List[(Int, Int)] = {
     | case class S(matches: List[(Int, Int)], remaining: List[Int])
     | def find(div: Int) = modify( (s: S) =>
     |   s.remaining.find(_ % div == 0).map( n => S(s.matches :+ div -> n, s.remaining filterNot (_ ==  n)) ).getOrElse(s))
     | (divs.traverse[({type l[a]=State[S, a]})#l, Unit](div => state(s => find(div)(s))) ~> S(nil, nums)).matches
     | }
findMatches2: (divs: List[Int], nums: List[Int])List[(Int, Int)]

scala> findMatches2(List(2, 3, 4), List(1, 6, 7, 8, 9))
res0: List[(Int, Int)] = List((2,6), (3,9), (4,8))
于 2012-02-08T13:59:01.037 に答える