Правильное использование изменяемых / неизменяемых списков

В данный момент Я пытаюсь понять функциональное программирование в Scala, и я столкнулся с проблемой, которую сам не могу понять.

Представьте себе следующую ситуацию:

У вас есть два класса: Controller и Bot . Bot - это независимый Actor, который инициируется контроллером , выполняет дорогостоящую операцию и возвращает результат контроллеру . Поэтому цель контроллера легко описать: создать несколько объектов бота , запустить их и получить результат.

Пока все хорошо; Я могу реализовать все это без использования изменяемых объектов.

Но что мне делать, если мне нужно сохранить результат, который возвращает Bot , использовать его позже в качестве входных данных для другого бота (и позже означает, что я не знаю, когда во время компиляции!)?

Сделать это с изменяемым списком или коллекцией довольно легко, но я добавляю много проблем в свой код (поскольку мы имеем дело с параллелизмом здесь).

Возможно ли, следуя парадигме FP, решить эту проблему, используя безопасные неизменяемые объекты (списки ...)?

Кстати, я новичок в FP, так что этот вопрос может показаться глупым, но я не могу понять, как решить эту проблему :)

16
задан Jens K. 29 August 2010 в 17:44
поделиться

2 ответа

Вот как Erlang-подобный актор мог бы выглядеть в Scala:

case class Actor[State](val s: State)(body: State => Option[State]) { // immutable
  @tailrec
  def loop(s1: State) {
    body(s1) match {
      case Some(s2) => loop(s2)
      case None => ()
    }
  }

  def act = loop(s)
}

def Bot(controller: Actor) = Actor(controller) { 
  s => 
    val res = // do the calculations
    controller ! (this, res)
    None // finish work
} 

val Controller = Actor(Map[Bot, ResultType]()) {s =>
  // start bots, perhaps using results already stored in s
  if ( 
    // time to stop, e.g. all bots already finished 
  )
    None
  else
    receive {
      case (bot, res) => Some(s + (bot -> res)) // a bot has reported result
    }
}

Controller.act
6
ответ дан 30 November 2019 в 23:00
поделиться

Актеры обычно имеют внутреннее состояние, будучи сами изменчивыми зверями. Обратите внимание, что актеры — это не FP.

Похоже, что описанная вами установка основана на изменяемом контроллере, и ее трудно обойти на языке, который по умолчанию не является нестрогим. Однако в зависимости от того, что вы делаете, вы можете полагаться на фьючерсы. Например:

case Msg(info) =>
  val v1 = new Bot !! Fn1(info)
  val v2 = new Bot !! Fn2(info)
  val v3 = new Bot !! Fn3(info)
  val v4 = new Bot !! Fn4(v1(), v2(), v3())
  reply(v4())

В данном случае -- потому что !! возвращает Future -- v1, v2 и v3 будут вычисляться параллельно. Сообщение Fn4 принимает фьючерс в качестве параметров, что означает, что он будет ждать, пока не будут вычислены все значения, прежде чем начать вычисления.

Аналогичным образом, ответ будет отправлен только после того, как v4 будет вычислено, поскольку будущее также было подано.

По-настоящему функциональным способом выполнения этих задач является функциональное реактивное программирование, или сокращенно FRP. Это другая модель, чем актеры.

Прелесть Scala, однако, в том, что вы можете комбинировать такие парадигмы в той мере, в какой это лучше подходит для вашей задачи.

7
ответ дан 30 November 2019 в 23:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: