返回

Kotlin解析数学表达式 轻松实现函数/括号/常量处理

Android

解析数学表达式:Kotlin 中的高效方法

在软件开发中,处理数学表达式是一个常见的需求。Kotlin 作为一门现代语言,提供了全面的语法和库,使数学表达式解析成为一项相对简单的任务。本文将深入探讨一种使用 Kotlin 进行数学表达式解析的高效方法。

算法概述

我们的算法分为三个主要步骤:

1. 词法分析: 将表达式字符串分解为一系列称为标记的组成部分,包括数字、运算符、函数和括号等。

2. 语法分析: 基于标记序列构建一个语法树,表示表达式的结构和优先级。

3. 语义分析: 根据语法树,计算表达式的值。

词法分析

词法分析器负责将数学表达式字符串分解为标记。我们可以使用正则表达式来匹配不同的标记类型。以下示例展示了如何匹配数字、运算符、函数和括号:

// 正则表达式匹配数字
val numberRegex = Regex("[0-9]+")

// 正则表达式匹配运算符
val operatorRegex = Regex("[+-*/]")

// 正则表达式匹配函数
val functionRegex = Regex("[a-zA-Z]+\\(.*\\)")

// 正则表达式匹配括号
val bracketRegex = Regex("[()]")

一旦匹配到标记,我们将它们存储在标记列表中:

val tokens = mutableListOf<Token>()

var input = "1 + 2 * (3 + 4)"

while (input.isNotEmpty()) {
    val numberMatch = numberRegex.find(input)
    val operatorMatch = operatorRegex.find(input)
    val functionMatch = functionRegex.find(input)
    val bracketMatch = bracketRegex.find(input)

    val match = listOfNotNull(numberMatch, operatorMatch, functionMatch, bracketMatch).minByOrNull { it?.range?.start }

    if (match != null) {
        tokens.add(Token(match.value, match.range))
        input = input.replaceRange(match.range, "")
    } else {
        break
    }
}

语法分析

语法分析器将标记列表转换为语法树。语法树是一个分层结构,表示表达式的组成部分及其相互关系。我们可以使用递归下降解析器来实现语法分析。

以下示例展示了如何解析表达式:

fun parseExpression(tokens: List<Token>) {
    val root = ExpressionNode()

    var index = 0

    fun parseTerm(): ExpressionNode {
        val node = ExpressionNode()

        if (tokens[index].type == TokenType.NUMBER) {
            node.value = tokens[index].value
            index++
        } else if (tokens[index].type == TokenType.FUNCTION) {
            node.function = tokens[index].value
            index++

            node.args = mutableListOf()

            while (tokens[index].type != TokenType.BRACKET_CLOSE) {
                node.args.add(parseTerm())

                if (tokens[index].type == TokenType.COMMA) {
                    index++
                }
            }

            index++
        } else if (tokens[index].type == TokenType.BRACKET_OPEN) {
            index++

            node.left = parseExpression(tokens)

            if (tokens[index].type == TokenType.BRACKET_CLOSE) {
                index++
            } else {
                throw SyntaxError("Expected closing bracket")
            }
        } else {
            throw SyntaxError("Unexpected token: ${tokens[index].value}")
        }

        return node
    }

    fun parseOperator(): ExpressionNode {
        val node = ExpressionNode()

        if (tokens[index].type == TokenType.OPERATOR) {
            node.operator = tokens[index].value
            index++

            node.left = parseTerm()
            node.right = parseTerm()
        } else {
            throw SyntaxError("Expected operator")
        }

        return node
    }

    root.right = parseTerm()

    while (index < tokens.size) {
        root.left = root.right
        root.operator = tokens[index].value
        index++

        root.right = parseOperator()
    }

    return root
}

语义分析

语义分析器根据语法树计算表达式的值。我们可以使用递归下降解释器来实现语义分析。

以下示例展示了如何计算表达式的值:

fun evaluateExpression(node: ExpressionNode): Double {
    if (node.value != null) {
        return node.value!!.toDouble()
    } else if (node.function != null) {
        val args = node.args.map { evaluateExpression(it) }

        return when (node.function) {
            "sin" -> Math.sin(args[0])
            "cos" -> Math.cos(args[0])
            "tan" -> Math.tan(args[0])
            "asin" -> Math.asin(args[0])
            "acos" -> Math.acos(args[0])
            "atan" -> Math.atan(args[0])
            "pow" -> Math.pow(args[0], args[1])
            "log" -> Math.log(args[0])
            else -> throw RuntimeError("Unknown function: ${node.function}")
        }
    } else {
        val left = evaluateExpression(node.left!!)
        val right = evaluateExpression(node.right!!)

        return when (node.operator) {
            "+" -> left + right
            "-" -> left - right
            "*" -> left * right
            "/" -> left / right
            else -> throw RuntimeError("Unknown operator: ${node.operator}")
        }
    }
}

完整示例

以下代码提供了完整示例,展示了如何使用 Kotlin 解析和计算数学表达式:

fun main() {
    val input = "sin(x) + cos(x) * (x + 1)"
    val tokens = tokenize(input)
    val root = parseExpression(tokens)
    val result = evaluateExpression(root)

    println("Result: $result")
}

fun tokenize(input: String): List<Token> {
    val tokens = mutableListOf<Token>()

    var index = 0

    fun isOperator(c: Char): Boolean {
        return c == '+' || c == '-' || c == '*' || c == '/'
    }

    while (index < input.length) {
        val c = input[index]

        if (Character.isDigit(c)) {
            val numberBuilder = StringBuilder()

            while (index < input.length && Character.isDigit(input[index])) {
                numberBuilder.append(input[index])
                index++
            }

            tokens.add(Token(TokenType.NUMBER, numberBuilder.toString(), index - 1))
        } else if (isOperator(c)) {
            tokens.add(Token(TokenType.OPERATOR, c.toString(), index))
            index++
        } else if (c == '(') {
            tokens.add(Token(TokenType.BRACKET_OPEN, "(", index))
            index++
        } else if (c == ')') {
            tokens.add(Token(TokenType.BRACKET_CLOSE, ")", index))
            index++
        } else if (c == ' ') {
            index++
        } else {
            throw SyntaxError("Unexpected character: $c")
        }
    }

    return tokens
}

fun parseExpression(tokens: List<Token