Как вернуть только последний результат приложения функции для объекта итератора с помощью средства чтения монад

Я перевожу свою проблему в этот небольшой «упрощенный» пример. Цель состоит в том, чтобы вернуть только последние вычисления, основанные на выполнении простой функции для каждого элемента объекта итератора.

val l = List(1,2,3,4).toIterator
def myPersonnalMultiply(number:Int) = number * 2

Для этого списка я хочу, чтобы моя оценка итератора возвращала «8»

Я начал пытаться с yield , но, похоже, я не могу вычислить две вещи:

val result = for (
  e <- l
  computation = myPersonnalMultiply(e)
if ((l.hasNext) == false) yield computation

Таким образом, есть решение map, затем slice, чтобы сохранить только последний оцененный элемент, но после обсуждения с коллегой кажется, что это на самом деле не соответствует красивому функциональному коду, и есть лучший подход к сделать это (правда?).

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

Редактировать: мой пример "слишком упрощен", извините за эту ошибку.

На самом деле эквивалент функции myPersonallMultiply эквивалентен выполнению симуляции, для которой может потребоваться вычисление текущего состояния или предыдущего состояния.

Функция состояний моего симулятора возвращает итератор состояния:

 def states(implicit aprng: Random = new Random): Iterator[State] = {

    // Initial State loaded from file
    val loadedState = initState(this.getClass.getClassLoader.getResourceAsStream("init-situation.txt"))
    val territory = computeTerritory(loadedState)

    def simulationStep(state: State): State = {
        // 3b - Apply on each city the function evolveCity() which contain the sequence of action for each city
        // and return a list of tuple (city, exchange) which represent a new state at time t+1 for these cities
        val evolved =
          state.cities.map {
            city => evolveCity(city.id, state.cities, territory(city.id), state.date)
          }

        // 4 - take only the first object (cities) in the list "evolved"
        new State {
          val cities = evolved.map { case(city, _) => city }
          val date = state.date + 1
        }        
    }

    def ended(state: State) = {
      val maxInnov = maxInnovation(state.cities)
      val maxCity = maxCities(state.cities)

      // 3a - Break the simulation loop if one of these conditions is true
      state.date >= 4000 || /*maxPop > 70.0 || */ maxInnov > maxInnovation
    }

    // 1 - Launch the recursive simulation loop with initial loaded step, a the date 1
    val initialState: State = new State {
      val cities = loadedState
      val date = 1
    }

    Iterator.iterate(initialState)(simulationStep).takeWhile(s => !ended(s))
  }

Когда вы вычисляете каждое из этих состояний (шаг моей симуляции = 1 состояние), вам нужно сохранить/записать результат в любом месте, поэтому я инкапсулирую запуск симуляции в класс Writer, здесь «CSVwriter».

class CSVWriter(path: String, idExp:Int, seed:Long) {

  def apply(s: Simulation)(implicit aprng: Random) = {
    val writer = new BufferedWriter(new FileWriter(new File(path)))

    // TODO: Reader Nomad ? http://mergeconflict.com/reading-your-future/
    // Run stepWriter on each state, and write the result
    // Actually this code run the writer but don't return the final state ...

    try {
      writer.append("v_idn, v_exp, v_ticks, v_seed, v_pop" + "\n")
      s.states.foreach(stepWriter(_, writer))
    } finally writer.close
   }

  def stepWriter(dataToWrite: State, writer: Writer) = {
    writer.synchronized {
      val cities = dataToWrite.cities
      val year = dataToWrite.date
      cities.map {
        c => writer.append(List[Any](c.id.toInt, idExp.toInt, year.toInt, seed, c.population).map{_.toString}.mkString(",") + "\n")
      }
    }
  }

}

1 - Как видите, здесь каждому шагу не нужно предыдущее состояние, но в действительно ближайшем будущем это тот случай: например, чтобы вычислить новый обмен между нашими городами в нашем моделирование состояния T, нам нужно получить доступ к обменнику, созданному в предыдущем состоянии T-1

2. Мне нужно вернуть последнее состояние, чтобы вычислить некоторую оценку для моей симуляции, поэтому мне нужно, чтобы класс CSVWriter записывал каждое состояние, возвращаемое s.states, а также возвращал последнее вычисленное состояние.

3 Я думаю, что можно создать лучшее и более универсальное решение этой проблемы: возможно, reader monad может помочь мне создать лучший интерфейс для этого сложного поведения записи для итератора состояния, но, может быть, я ошибаюсь?

Я надеюсь, что мой вопрос более понятен.

После некоторых исследований я нашел функциональный шаблон monad reader, но если я понимаю глобальный интерес этого подхода, я не понимаю, как я могу перевести другой пример, который я прочитал в Интернете (например, первый пример здесь http://mergeconflict.com/reading-your-future/ ) к этой простой задаче. В настоящее время я безуспешно пробую другой код:/

Есть ли у вас какое-нибудь простое объяснение или указатели, которые помогут мне понять «читатель монад»?

Или, может быть, я полностью ошибаюсь и не могу решить свою проблему с помощью этого подхода?


person reyman64    schedule 23.10.2012    source источник
comment
А как насчет list.view.map(...).last?   -  person kiritsuku    schedule 23.10.2012
comment
Мне неясно, что вы здесь ищете, но я отмечу, что итераторы по своей сути не функциональны, и что в last нет ничего нефункционального.   -  person Travis Brown    schedule 23.10.2012
comment
Я обновляю свой вопрос новой информацией, потому что мой пример, наконец, слишком упрощен.   -  person reyman64    schedule 24.10.2012
comment
чтобы иметь последнее состояние из CSVWriter, вам нужно всего лишь добавить s.states.last после блока try/catch. Для получения дополнительной информации о потоке состояния для шага симуляции, я предполагаю, что вы смотрите не на ту монаду: вам следует взглянуть на монаду State. Некоторые примеры здесь.   -  person pagoda_5b    schedule 24.10.2012
comment
Если вы чувствуете себя смелым, вы можете получить более подробную информацию по адресу Блог Рунара Бьярнасона или Тони Блог Морриса. Не для слабонервных.   -  person pagoda_5b    schedule 24.10.2012
comment
Отказ: я забыл, что вам сначала нужно преобразовать свой s.states в последовательность, как в s.states.toSeq, прежде чем вызывать last. Вы можете создать val steps = s.states.toSeq перед вызовом foreach, а затем last   -  person pagoda_5b    schedule 24.10.2012


Ответы (2)


Возможно, вы упростили свой случай, но если вам нужно обрабатывать все элементы независимо (имеется в виду, что результат вашей операции над i-м-элементом не нужен для работы над ith+1-element) вам не нужна монада.

Можно просто оперировать всеми элементами (даже параллельно!), вернуть результаты обратно и получить последний

def doAndGetLast[A, B](input: List[A])(computation: A => B): B = 
    (input.par map computation).seq.last

val inputs = List(1,2,3,4)

def myMul(n: Int) = n * 2

val withInputs: (Int => Int) => Int = doAndGetLast(inputs) _

val result = withInputs(myMul) // or directly doAndGetLast(inputs)(myMul)
person pagoda_5b    schedule 23.10.2012
comment
Спасибо за ответ на вопрос, возможно, теперь мне нужно отредактировать, чтобы задать второй вопрос с моим реальным вариантом использования... - person reyman64; 24.10.2012

Вы можете просто (ab) использовать foldLeft:

val myDefaultValue = 0
l.foldLeft(myDefaultValue){ (_,x) => myPersonnalMultiply(x) }

где myDefaultValue — это значение, возвращаемое в случае, когда список пуст.

person Régis Jean-Gilles    schedule 23.10.2012