Цепочка ряда переходов с государственной монадой - PullRequest
0 голосов
/ 16 декабря 2018

Я начинаю использовать монаду состояния для очистки моего кода.У меня это работает для моей проблемы, когда я обрабатываю транзакцию с именем CDR и соответствующим образом изменяю состояние.Он отлично работает для отдельных транзакций, используя эту функцию для выполнения обновления состояния.

def addTraffic(cdr: CDR): Network => Network = ...

Вот пример:

scala> val processed: (CDR) => State[Network, Long] = cdr =>
 |   for {
 |     m <- init
 |     _ <- modify(Network.addTraffic(cdr))
 |     p <- get
 |   } yield p.count
processed: CDR => scalaz.State[Network,Long] = $$Lambda$4372/1833836780@1258d5c0

scala> val r = processed(("122","celda 1", 3))
r: scalaz.State[Network,Long] = scalaz.IndexedStateT$$anon$13@4cc4bdde

scala> r.run(Network.empty)
res56: scalaz.Id.Id[(Network, Long)] = (Network(Map(122 -> (1,0.0)),Map(celda 1 -> (1,0.0)),Map(1 -> Map(1 -> 3)),1,true),1)

Что я хочу сделать сейчас, это создать цепочкуколичество транзакций на итераторе.Я нашел кое-что, что работает довольно хорошо, но переходы между состояниями не требуют ввода (изменения состояния через RNG)

  import scalaz._
  import scalaz.std.list.listInstance
  type RNG = scala.util.Random

  val f = (rng:RNG) => (rng, rng.nextInt)
  val intGenerator: State[RNG, Int] = State(f)
  val rng42 = new scala.util.Random
  val applicative = Applicative[({type l[Int] = State[RNG,Int]})#l]

  // To generate the first 5 Random integers
  val chain: State[RNG, List[Int]] = applicative.sequence(List.fill(5)(intGenerator))
  val chainResult: (RNG, List[Int]) = chain.run(rng42)
  chainResult._2.foreach(println)

Я безуспешно пытался адаптировать это, но я не могу заставить их печатать сигнатуры, чтобы соответствовать, потому что мойфункция состояния требует ввода cdr (транзакции)

Спасибо

1 Ответ

0 голосов
/ 17 декабря 2018

TL; DR
вы можете использовать traverse из класса типов Traverse для коллекции (например, List) CDR с,используя функцию с такой подписью: CDR => State[Network, Long].Результатом будет State[Network, List[Long]].В качестве альтернативы, если вас не волнует List[Long], вы можете вместо этого использовать traverse_, что вернет State[Network, Unit].Наконец, если вы хотите «агрегировать» результаты T по мере их поступления, и T образует Monoid, вы можете использовать foldMap из Foldable, который вернет State[Network, T], где T - это объединенный (например, сложенный) результат всех T s в вашей цепочке.

Пример кода
Теперь немного подробнее, с примерами кода.Я отвечу на это, используя Cats State, а не Scalaz, поскольку я никогда не использовал последний, но концепция та же самая, и, если у вас все еще есть проблемы, я найду правильный синтаксис.

Предположим, что у нас есть следующие типы данных и импорт для работы:

import cats.implicits._
import cats.data.State

case class Position(x : Int = 0, y : Int = 0)

sealed trait Move extends Product
case object Up extends Move
case object Down extends Move
case object Left extends Move
case object Right extends Move

Как ясно, Position представляет точку в 2D-плоскости, и Move может перемещатьсятакая точка вверх, вниз, влево или вправо.

Теперь давайте создадим метод, который позволит нам увидеть, где мы находимся в данный момент времени:

def whereAmI : State[Position, String] = State.inspect{ s => s.toString }

и метод для изменениянаша позиция, с учетом Move:

def move(m : Move) : State[Position, String] = State{ s => 
  m match {
    case Up => (s.copy(y = s.y + 1), "Up!")
    case Down => (s.copy(y = s.y - 1), "Down!")
    case Left => (s.copy(x = s.x - 1), "Left!")
    case Right => (s.copy(x = s.x + 1), "Right!")
  }
}

Обратите внимание, что это вернет String с названием хода, за которым следует восклицательный знак.Это просто для того, чтобы смоделировать изменение типа с Move на что-то другое, и показать, как будут агрегированы результаты.Подробнее об этом чуть позже.

Теперь давайте попробуем поиграть с нашими методами:

val positions : State[Position, List[String]] = for{
  pos1 <- whereAmI 
  _ <- move(Up)
  _ <- move(Right)
  _ <- move(Up)
  pos2 <- whereAmI
  _ <- move(Left)
  _ <- move(Left)
  pos3 <- whereAmI
} yield List(pos1,pos2,pos3)

И мы можем дать ему начальное значение Position и посмотреть результат:

positions.runA(Position()).value // List(Position(0,0), Position(1,2), Position(-1,2))

(вы можете игнорировать .value там, это странно из-за того, что State[S,A] действительно просто псевдоним для StateT[Eval,S,A])

Как вывидите, это ведет себя так, как вы ожидаете, и вы можете создавать различные «чертежи» (например, последовательности изменений состояния), которые будут применяться после предоставления начального состояния.

Теперь, чтобы фактически ответить вамвопрос, скажем, у нас есть List[Move], и мы хотим применить их последовательно к начальному состоянию и получить результат: мы используем traverse из Traverse типа класса .

val moves = List(Down, Down, Left, Up)
val result : State[Position, List[String]] = moves.traverse(move)
result.run(Position()).value // (Position(-1,-1),List(Down!, Down!, Left!, Up!))

В качестве альтернативы, если вам вообще не нужен A (в вашем случае List), вы можете использовать traverse_ вместо traverse, и тип результата будет:

val result_ : State[Position, List[String]] = moves.traverse_(move)
result_.run(Position()).value // (Position(-1,-1),Unit)

Наконец, если ваш A тип в State[S,A] образует Monoid, то вы также можете использовать foldMap из Foldable для объединения (например, fстарые) все A с, как они рассчитаны.Тривиальный пример (вероятно, бесполезный, потому что это просто объединит все String с) будет следующим:

val result : State[Position,String] = moves.foldMap(move)
result.run(Position()).value // (Position(-1,-1),Down!Down!Left!Up!)

Является ли этот последний подход полезным или нет для вас, действительно зависит от того, что A выиметь и, если имеет смысл объединить это.

И это должно быть все, что вам нужно в вашем сценарии.

...