Я пишу интерпретатор языка программирования.
Мне нужна правильная идиома кода как для оценки последовательности выражений, чтобы получить последовательность их значений, так и для распространения состояния от одного оценщика к другому по мере выполнения оценок. Для этого мне нужна идиома функционального программирования.
Это не раз, потому что результаты выходят как карта. Это не карта из-за государственной опоры.
У меня есть этот код, который я использую, чтобы попытаться понять это. Сначала проведите несколько строк тестовой установки:
// test rig
class MonadLearning extends JUnit3Suite {
val d = List("1", "2", "3") // some expressions to evaluate.
type ResType = Int
case class State(i : ResType) // trivial state for experiment purposes
val initialState = State(0)
// my stub/dummy "eval" function...obviously the real one will be...real.
def computeResultAndNewState(s : String, st : State) : (ResType, State) = {
val State(i) = st
val res = s.toInt + i
val newStateInt = i + 1
(res, State(newStateInt))
}
Мое текущее решение. Использует переменную, которая обновляется по мере оценки тела карты:
def testTheVarWay() {
var state = initialState
val r = d.map {
s =>
{
val (result, newState) = computeResultAndNewState(s, state)
state = newState
result
}
}
println(r)
println(state)
}
У меня есть то, что я считаю неприемлемым решением, используя foldLeft, который делает то, что я называю идиомой «упаковать, когда вы сложите»:
def testTheFoldWay() {
// This startFold thing, requires explicit type. That alone makes it muddy.
val startFold : (List[ResType], State) = (Nil, initialState)
val (r, state) = d.foldLeft(startFold) {
case ((tail, st), s) => {
val (r, ns) = computeResultAndNewState(s, st)
(tail :+ r, ns) // we want a constant-time append here, not O(N). Or could Cons on front and reverse later
}
}
println(r)
println(state)
}
У меня также есть несколько рекурсивных вариантов (которые очевидны, но также не ясны или хорошо мотивированы), один из которых использует потоки, что почти терпимо:
def testTheStreamsWay() {
lazy val states = initialState #:: resultStates // there are states
lazy val args = d.toStream // there are arguments
lazy val argPairs = args zip states // put them together
lazy val resPairs : Stream[(ResType, State)] = argPairs.map{ case (d1, s1) => computeResultAndNewState(d1, s1) } // map across them
lazy val (results , resultStates) = myUnzip(resPairs)// Note .unzip causes infinite loop. Had to write my own.
lazy val r = results.toList
lazy val finalState = resultStates.last
println(r)
println(finalState)
}
Но я не могу придумать ничего более компактного или ясного, чем исходное решение 'var' выше, с которым я готов жить, но я думаю, что кто-то, кто ест/пьет/спит идиомы монад, собирается просто сказать .. , используйте это... (Надеюсь!)