You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose科学计算器中处理嵌套根号与复杂表达式

问题:处理Jetpack Compose科学计算器中的复杂平方根表达式

我正在用Jetpack Compose开发一款名为"Scientific Calculator"的Android应用,已经实现了√4、√4*2这类简单平方根运算,但处理√(1+2)这类带括号的复杂表达式时遇到困难。需要优化handleSqrtInput函数和evaluate函数,支持嵌套根号和括号的表达式处理与求值。


当前实现

1. ViewModel:handleSqrtInput函数

功能:处理平方根输入,更新显示文本

private fun handleSqrtInput() {
    if (isResultDisplayed) {
        displayText = "√"
    } else {
        if (displayText == "0") {
            displayText = "√"
        } else if (displayText.isNotEmpty()) {
            val lastChar = displayText.last()

            when {
                lastChar.isDigit() -> {
                    try {
                        val res = Math.sqrt(displayText.toDouble())
                        displayText = res.toString()
                    } catch (e: NumberFormatException) {
                        displayText = "Error"
                    }
                }
                lastChar == ')' -> {
                    try {
                        val res = Math.sqrt(displayText.toDouble())
                        displayText = res.toString() 
                    } catch (e: NumberFormatException) {
                        displayText = "Error"
                    }
                }
                lastChar.toString().isOperator() -> {
                    displayText += "√"
                }
                lastChar == '(' -> {
                    displayText += "√"
                }
                lastChar == '√' -> {
                    displayText += "√"
                }
                else -> {
                    displayText = displayText.dropLast(1) + "√("
                }
            }
        } else {
            displayText = "√("
        }
    }
    isResultDisplayed = false
}

2. Model:evaluate函数

功能:通过栈管理运算符和操作数,求值含平方根的表达式

fun evaluate(expression: String): Double {
    val operators = Stack<Char>()
    val operands = Stack<Double>()

    var i = 0
    while (i < expression.length) {
        when {
            expression[i] == '√' -> {
                if (i + 1 < expression.length && expression[i + 1] == '(') {
                    operators.push(expression[i])
                } else {
                    i++
                    val sb = StringBuilder()
                    while (i < expression.length && (expression[i].isDigit() || expression[i] == '.' || expression[i] == '(' || expression[i] == ')')) {
                        sb.append(expression[i])
                        i++
                    }
                    val subExpr = sb.toString()
                    operands.push(sqrt(evaluate(subExpr)))
                    i--
                }
            }
        }
        i++
    }
    // Add additional logic for handling other operators and completing the expression evaluation
}

优化方案

一、ViewModel:优化handleSqrtInput函数

核心改进:智能判断平方根的输入形式,自动补全括号并支持嵌套,保留表达式结构而非直接计算,交给后续求值逻辑处理。

修改后的代码:

private fun handleSqrtInput() {
    if (isResultDisplayed) {
        // 结果显示状态下,直接启动新的平方根表达式
        displayText = "√("
        isResultDisplayed = false
        return
    }

    val currentText = displayText
    when {
        currentText == "0" || currentText.isEmpty() -> {
            displayText = "√("
        }
        else -> {
            val lastChar = currentText.last()
            when {
                // 最后是数字或右括号:自动包裹当前完整表达式
                lastChar.isDigit() || lastChar == ')' -> {
                    // 定位需要包裹的表达式起始位置
                    val startIndex = currentText.findLast { it.isOperator() || it == '(' }?.plus(1) ?: 0
                    val prefix = currentText.substring(0, startIndex)
                    val exprToWrap = currentText.substring(startIndex)
                    displayText = "$prefix√($exprToWrap)"
                }
                // 最后是运算符、左括号、根号:追加带左括号的根号
                lastChar.toString().isOperator() || lastChar == '(' || lastChar == '√' -> {
                    displayText += "√("
                }
                else -> {
                    displayText += "√("
                }
            }
        }
    }
}

// 辅助扩展函数:判断是否为运算符
private fun Char.isOperator(): Boolean = this in setOf('+', '-', '*', '/')

关键改动:

  • 移除直接计算当前文本的逻辑,保留表达式结构(如√(1+2))
  • 针对不同上下文自动补全括号:数字/右括号后点击√,自动将目标表达式包裹进√();运算符/左括号/根号后点击√,直接追加√(
  • 天然支持嵌套根号输入(如连续点击√会生成√(√(,方便输入多层嵌套表达式)

二、Model:优化evaluate函数

核心改进:完善双栈求值逻辑,明确运算符优先级,正确解析嵌套括号与根号,兼容加减乘除等基础运算。

修改后的代码:

import kotlin.math.sqrt

fun evaluate(expression: String): Double {
    val operators = Stack<Char>()
    val operands = Stack<Double>()
    val trimmedExpr = expression.replace("\\s+".toRegex(), "") // 去除表达式中的空格
    var i = 0

    while (i < trimmedExpr.length) {
        val c = trimmedExpr[i]
        when {
            c.isDigit() || c == '.' -> {
                // 解析完整数字(含小数)
                val sb = StringBuilder()
                while (i < trimmedExpr.length && (trimmedExpr[i].isDigit() || trimmedExpr[i] == '.')) {
                    sb.append(trimmedExpr[i])
                    i++
                }
                operands.push(sb.toString().toDouble())
                continue
            }
            c == '(' -> {
                operators.push(c)
            }
            c == ')' -> {
                // 计算括号内所有运算,直到遇到左括号
                while (operators.isNotEmpty() && operators.peek() != '(') {
                    applyOperator(operators, operands)
                }
                operators.pop() // 弹出左括号

                // 若左括号前是根号,计算根号值
                if (operators.isNotEmpty() && operators.peek() == '√') {
                    val value = operands.pop()
                    operands.push(sqrt(value))
                    operators.pop()
                }
            }
            c == '√' -> {
                operators.push(c)
            }
            c.isOperator() -> {
                // 按优先级处理栈内运算符:当前运算符优先级更低时,先计算栈顶运算
                while (operators.isNotEmpty() && getPrecedence(operators.peek()) >= getPrecedence(c)) {
                    applyOperator(operators, operands)
                }
                operators.push(c)
            }
        }
        i++
    }

    // 处理剩余的运算符
    while (operators.isNotEmpty()) {
        applyOperator(operators, operands)
    }

    return operands.pop()
}

private fun Char.isOperator(): Boolean = this in setOf('+', '-', '*', '/')

private fun getPrecedence(op: Char): Int {
    return when (op) {
        '√' -> 4 // 根号优先级最高
        '*', '/' -> 3
        '+', '-' -> 2
        '(' -> 1
        else -> 0
    }
}

private fun applyOperator(operators: Stack<Char>, operands: Stack<Double>) {
    val op = operators.pop()
    when (op) {
        '√' -> {
            val num = operands.pop()
            operands.push(sqrt(num))
        }
        '+' -> {
            val b = operands.pop()
            val a = operands.pop()
            operands.push(a + b)
        }
        '-' -> {
            val b = operands.pop()
            val a = operands.pop()
            operands.push(a - b)
        }
        '*' -> {
            val b = operands.pop()
            val a = operands.pop()
            operands.push(a * b)
        }
        '/' -> {
            val b = operands.pop()
            if (b == 0.0) throw ArithmeticException("Division by zero")
            val a = operands.pop()
            operands.push(a / b)
        }
    }
}

关键改动:

  • 完善双栈求值逻辑,支持根号与加减乘除的混合运算
  • 定义明确的运算符优先级,确保根号运算优先执行
  • 处理括号时,先完成括号内所有运算,再将结果应用到前置根号
  • 原生支持嵌套根号(如√(√(12+4))会按12+4=16→√16=4→√4=2的顺序计算)
  • 增加完整的数字解析逻辑,支持小数运算

内容的提问来源于stack exchange,提问作者Sundaram Pandey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 01:56:02