2016-12-31 3 views
5

처음으로 cats을 사용하여 코드가 출현하여 day 1을 해결했습니다. 개선이 가능한지 궁금합니다. 고양이와 주립 모나드 사용 방법

내가 함께 왔어요 def update(i: Instruction): PosAndDir => PosAndDir

다음 서명하는 방법 update을 감안할 때 : 더 정확하게

val state: State[PosAndDir, List[Unit]] = instructions.map(i => State.modify(update(i))).toList.sequenceU 
val finalState = state.runS(PosAndDir(Pos(0, 0), North)).value 

또한

def update2(i: Instruction): State[PosAndDir, Option[Pos]] = 
    State.modify(update(i)).inspect(pad => if (i == Walk) Some(pad.pos) else None) 
    … 
    val state = instructions.map(update2).toList.sequenceU 
    val positions = state.runA(PosAndDir(Pos(0, 0), North)).value.flatten 

질문은 다음과 같습니다

  1. .value (scalaz는 투명 함)으로 전화해야하는 이유는 무엇입니까?
  2. 은 가독성을 높이기 위해 이해를 돕기 위해 update2을 작성하는 방법이 있습니까?
  3. cat에 SeqApplicative 인스턴스가 있습니다 (scalaz에는 없습니다). ?
  4. 코드 개선을위한 아이디어가 있습니까?
+0

당신은 PosAndDir','Pos'와'Dir' – Odomontois

+0

물론'의 정의를 제공 할 수있다. 전체 코드는 다음과 같습니다. https://gist.github.com/YannMoisan/18e44d8998d42d745a3ea9caaae4c16a –

답변

5
  1. 고양이에 대한 별칭으로 State[S, A]를 정의 스택 안전 StateT[Eval, S , A] scalaz 측면에서 StateT[Trampoline, S, A], 그래서 runS 반환 value 심지어 매우 긴 flatMap 시퀀스에 유래없이 실행됩니다 Eval[A]입니다. 당신이 당신의 기능이 솔루션은 2.12에서 작동하지 않습니다하지 않는 것이이

    def update2(i: Instruction): StateT[Option, PosAndDir, Pos] = 
        for (pad ← get if i == Walk) yield pad.pos 
    

    처럼 보이게 할 수

    type Walk[x] = StateT[Option, PosAndDir, x] 
    val stateMonad = MonadState[Walk, PosAndDir] 
    
    import stateMonad._ 
    

    좀 더 추가 수입

    import cats.data.{State, StateT} 
    import cats.MonadState 
    import cats.syntax.functorFilter._ 
    import cats.instances.option._ 
    

    어떤 준비를 사용

  2. improvement으로 인해이 해결 방법을 사용할 수 있습니다.

    implicit class FunctorWithFilter[F[_] : FunctorFilter, A](fa: F[A]) { 
        def withFilter(f: A ⇒ Boolean) = fa.filter(f) 
    } 
    
  3. this answer 이유를 설명 Seq에 대한 인스턴스가 없습니다. alleycats 프로젝트에는 일부 비 정통 인스턴스가 있지만 나는 당신이 을 필요로하는지, Traverse[Seq]을 필요로하는지, 또는 sequencesequence_으로 바꾼다면 심지어 Foldable[Seq]을 필요로하는지 확신 할 수 없다.여기 저기 alleycats에서 Foldable[Iterable]이며, 좋은 소식

  4. 많은 시간을 소비하지 않았다 Seq 예를

    implicit val seqInstance = new MonadFilter[Seq] with Traverse[Seq] { 
        def traverse[G[_] : Applicative, A, B](fa: Seq[A])(f: (A) ⇒ G[B]): G[Seq[B]] = 
        fa match { 
         case head +: tail ⇒ f(head).map2(traverse(tail)(f))(_ +: _) 
         case _empty ⇒ Seq.empty[B].pure[G] 
        } 
    
        def foldLeft[A, B](fa: Seq[A], b: B)(f: (B, A) ⇒ B): B = fa.foldLeft(b)(f) 
    
        def foldRight[A, B](fa: Seq[A], lb: Eval[B])(f: (A, Eval[B]) ⇒ Eval[B]): Eval[B] = 
        fa match { 
         case head +: tail ⇒ f(head, foldRight(tail, lb)(f)) 
         case _empty ⇒ lb 
        } 
    
        def pure[A](x: A): Seq[A] = Seq(x) 
    
        def empty[A]: Seq[A] = Seq.empty[A] 
    
        def flatMap[A, B](fa: Seq[A])(f: (A) ⇒ Seq[B]): Seq[B] = fa.flatMap(f) 
    
        def tailRecM[A, B](a: A)(f: (A) ⇒ Seq[Either[A, B]]): Seq[B] = { 
        @tailrec def go(seq: Seq[Either[A, B]]): Seq[B] = 
         if (seq.contains((_: Either[A, B]).isLeft)) 
         go(seq.flatMap { 
          case Left(a) ⇒ f(a) 
          case b ⇒ Seq(b) 
         }) else seq.collect { case Right(b) ⇒ b } 
    
        go(Seq(Left(a))) 
        } 
        override def mapFilter[A, B](fa: Seq[A])(f: (A) ⇒ Option[B]): Seq[B] = 
        fa.flatMap(f(_).toSeq) 
    } 
    
  5. 위해 뭔가 닮은를 정의하는 나의 시도하지만, 여기에 Monocle library를 통해 일부를 단순화에 내 시도이다 :

    import cats.{MonadState, Foldable, Functor} 
    import cats.instances.option._ 
    import cats.syntax.foldable._ 
    import cats.syntax.functor._ 
    import cats.syntax.functorFilter._ 
    import monocle.macros.Lenses 
    
    @Lenses 
    case class Pos(x: Int, y: Int) 
    
    sealed abstract class Dir(val cmd: Pos ⇒ Pos) 
    
    case object South extends Dir(Pos.y.modify(_ - 1)) 
    case object North extends Dir(Pos.y.modify(_ + 1)) 
    case object East extends Dir(Pos.x.modify(_ + 1)) 
    case object West extends Dir(Pos.x.modify(_ - 1)) 
    
    @Lenses 
    case class PosAndDir(pos: Pos, dir: Dir) 
    
    val clockwise = Vector(North, East, South, West) 
    val right: Map[Dir, Dir] = clockwise.zip(clockwise.tail :+ clockwise.head).toMap 
    val left: Map[Dir, Dir] = right.map(_.swap) 
    
    sealed abstract class Instruction(val cmd: PosAndDir ⇒ PosAndDir) 
    case object TurnLeft extends Instruction(PosAndDir.dir.modify(left)) 
    case object TurnRight extends Instruction(PosAndDir.dir.modify(right)) 
    case object Walk extends Instruction(pd ⇒ PosAndDir.pos.modify(pd.dir.cmd)(pd)) 
    
    def runInstructions[F[_] : Foldable : Functor](instructions: F[Instruction])(start: PosAndDir): PosAndDir = 
        instructions.map(i => State.modify(i.cmd)).sequence_.runS(start).value 
    
+0

단안을 지적 해 주셔서 감사합니다. 놀랍게도 데이터와 동작을 구분하는 대신 '방향'에 'cmd'를 넣습니다. 왜 ? 다음 방향의 사전 계산은 똑똑합니다. StateMonad 란 무엇입니까? (저는 State가 모나드이고 StateT가 모나드 변압기라는 것을 압니다.) 추신 : 주저하지 마시고, 다른 멋진 질문을 기다리고 있습니다. –