우석
객체에서 함수로 - 2장 함수로 HTTP 다루기 (opens in a new tab)
연습문제 2.1 - 중위 표기법 사용해보기
infix fun <A, B, C> FUN<A, B>.andThen(other: FUN<B, C>): FUN<A, C> = { a -> this(a).let(other) }
연습문제 2.2 - FunStack 구현해보기
data class FunStack<T>(
private val elements: List<T> = emptyList()
) {
fun push(element: T): FunStack<T> = FunStack(listOf(element) + elements)
fun pop(): Pair<T, FunStack<T>> = Pair(elements.first(), FunStack(elements.drop(1)))
fun size(): Int = elements.size
}
연습문제 2.3 - RPN 계산기 만들기
// 내 코드
object RpnCalc {
private val operations: Map<String, (Double, Double) -> Double> = mapOf(
"+" to Double::plus,
"-" to Double::minus,
"*" to Double::times,
"/" to Double::div
)
fun calc(expr: String): Double {
val operands: Stack<Double> = Stack()
expr.split(" ").forEach { numOrOper ->
if (operations.containsKey(numOrOper)) {
val operation = operations.getValue(numOrOper)
val secondOperand = operands.pop()
val firstOperand = operands.pop()
operands.push(operation(firstOperand, secondOperand))
} else {
operands.push(numOrOper.toDouble())
}
}
return operands.pop()
}
}
// 책 옮긴이 코드
object RpnCalc {
val operationsMap = mapOf<String, (Double, Double) -> Double>(
"+" to Double::plus,
"-" to Double::minus,
"*" to Double::times,
"/" to Double::div
)
val funStack = FunStack<Double>()
fun calc(expr: String): Double =
expr.split(" ")
.fold(funStack, ::reduce)
.pop().first
private fun reduce(stack: FunStack<Double>, token: String): FunStack<Double> =
if (operationsMap.containsKey(token)) {
val (b, tempStack) = stack.pop()
val (a, newStack) = tempStack.pop()
newStack.push(operation(token, a, b))
} else {
stack.push(token.toDouble())
}
private fun operation(token: String, a: Double, b: Double) =
operationsMap[token]?.invoke(a, b) ?: error("Unknown operation $token")
}
- 내 코드와 옮긴이의 코드 중 가장 큰 차이는 뚱뚱한 함수 1개냐 작은 함수 여럿을 합성한 거냐의 차이라 느껴졌다.
- reduce 개념은 아직 익숙하지 않은 것 같다.. 무슨 역할인지는 알겠는데 활용할 수 있는 곳에서 reduce가 떠오르지 않는 걸 보면 좀 더 익숙해져야 할듯..
만혁
2. 함수로 HTTP 다루기
연습문제 2.1
class E01_AndThenTest {
// g(x) * f(x) = g(f(x))
// f andThen g = g(f(x))
infix fun <A, B, C> FUN<A, B>.andThen(other: FUN<B, C>): FUN<A, C> = { a: A -> other(this(a)) }
}
typealias FUN<A, B> = (A) -> B
연습문제 2.2
class ManStack<T>(
private val elements: List<T> = listOf()
) {
fun push(c: T): ManStack<T> {
return ManStack(elements.plus(c))
}
fun pop(): Pair<T, ManStack<T>> {
val last = elements.last()
val popped = elements.dropLast(1)
return Pair(last, ManStack(popped))
}
fun size(): Int = elements.size
}
class E02_FunStackTest {
@Test
fun `push into the stack`() {
val stack1 = ManStack<Char>()
val stack2 = stack1.push('A')
expectThat(stack1.size()).isEqualTo(0)
expectThat(stack2.size()).isEqualTo(1)
}
@Test
fun `push and pop`() {
val (q, stack) = ManStack<Char>().push('Q').pop()
expectThat(stack.size()).isEqualTo(0)
expectThat(q).isEqualTo('Q')
}
@Test
fun `push push pop`() {
val (b, stack) = ManStack<Char>()
.push('A')
.push('B')
.pop()
expectThat(stack.size()).isEqualTo(1)
expectThat(b).isEqualTo('B')
}
}
연습문제 2.3
// v1
class RpnCalc {
companion object {
fun calc(exp: String): Double {
val exps = exp.split(" ").toList()
var stack = ManStack<Double>()
exps.forEach {
when (it) {
"+" -> {
val (element2, popped2) = stack.pop()
val (element1, popped1) = popped2.pop()
val result = element1 + element2
stack = popped1.push(result)
}
"-" -> {
val (element2, popped2) = stack.pop()
val (element1, popped1) = popped2.pop()
val result = element1 - element2
stack = popped1.push(result)
}
"*" -> {
val (element2, popped2) = stack.pop()
val (element1, popped1) = popped2.pop()
val result = element1 * element2
stack = popped1.push(result)
}
"/" -> {
val (element2, popped2) = stack.pop()
val (element1, popped1) = popped2.pop()
val result = element1 / element2
stack = popped1.push(result)
}
else -> {
stack = stack.push(it.toDouble())
}
}
}
return stack.pop().first
}
}
}
// v2
class RpnCalc {
companion object {
fun calc(exp: String): Double {
val result = exp.split(" ")
.fold(ManStack<Double>()) { stack, it -> perform(stack, it) }
return result.pop().first
}
private fun isOperation(x: String): Boolean {
val ops = setOf("+", "-", "/", "*")
return ops.contains(x)
}
private fun perform(stack: ManStack<Double>, operation: String): ManStack<Double> {
return if (isOperation(operation)) {
val (y, stack1) = stack.pop()
val (x, stack2) = stack1.pop()
val res = when (operation) {
"+" -> plus(x, y)
"-" -> minus(x, y)
"/" -> divide(x, y)
"*" -> multiply(x, y)
else -> throw IllegalArgumentException("Unknown operation")
}
stack2.push(res)
} else {
stack.push(operation.toDouble())
}
}
private fun plus(x: Double, y: Double): Double = x + y
private fun minus(x: Double, y: Double): Double = x - y
private fun divide(x: Double, y: Double): Double = x / y
private fun multiply(x: Double, y: Double): Double = x * y
}
}
// v3
class RpnCalc {
companion object {
fun calc(exp: String): Double =
exp.split(" ")
.fold(ManStack<Double>()) { stack, token -> perform(stack, token) }
.pop().first
private val operations: Map<String, (Double, Double) -> Double> = mapOf(
"+" to { x, y -> x + y },
"-" to { x, y -> x - y },
"*" to { x, y -> x * y },
"/" to { x, y -> x / y }
)
private fun perform(stack: ManStack<Double>, token: String): ManStack<Double> =
operations[token]?.let { op ->
val (y, stack1) = stack.pop()
val (x, stack2) = stack1.pop()
stack2.push(op(x, y))
} ?: stack.push(token.toDouble())
}
}
머리 부셔지는줄 알았다
계산기 만드는데 2시간쯤 걸린듯......