IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 实战:构建一个Kotlin版本的四则运算计算器 -> 正文阅读

[移动开发]实战:构建一个Kotlin版本的四则运算计算器

作者:https://csdnimg.cn/release/blogv2/dist/components/js/pc_wap_commontools-36b0ef62e4.min.js

实战:构建一个Kotlin版本的四则运算计算器

计算器 1.0

/*
* 大致列举一下计算器的功能需求:
* 交互式界面,输入算式,按下回车,程序就会帮我们计算出结果;
* 数字与字符之间要求有空格,“1 + 1”是可以的,“1+1”则不行;
* 输入 exit,按下回车,程序就会退出;支持“加减乘除”,四种运算,仅支持两个数的运算。
*/

val help = """
--------------------------------------
使用说明:
1. 输入 1 + 1,按回车,即可使用计算器;
2. 注意:数字与符号之间要有空格;
3. 想要退出程序,请输入:exit
--------------------------------------""".trimIndent()

fun main() {
    while (true) {
        println(help)

        val input = readLine() ?: continue
        if (input == "exit") exitProcess(0)

        val inputList = input.split(" ")
        val result = calculate(inputList)

        if (result == null) {
            println("输入格式不对")
            continue
        } else {
            println("$input = $result")
        }
    }
}

private fun calculate(inputList: List<String>): Int? {
    if (inputList.size != 3) return null

    val left = inputList[0].toInt()
    val operation = Operation.valueOf(inputList[1])
    val right = inputList[2].toInt()

    return when (operation) {
        Operation.ADD -> left + right
        Operation.MINUS -> left - right
        Operation.MULTI -> left * right
        Operation.DIVI -> left / right
    }
}

计算器 2.0

/*
在 2.0 版本中,我们会分成两个阶段:
* 第一个阶段,融入面向对象的思想。1.0 版本中,我们只写了两个函数,一个是 main() 函数,
  另一个是 calculate() 函数。虽然这样的设计非常直观且便于理解,但却不太符合我们工程界的思维习惯。
  我们应该将程序封装到一个类当中,并且尽量让每个函数的功能划分清楚,保持每个函数尽量简单。
* 第二个阶段,兼容输入格式。1.0 版本中,我们对输入有严格的要求,数字和符号之间必须有空格,否则我们的算式解析会出错。
  在 2.0 版本中,我们尝试兼容不同的输入格式,不管数字和符号之间有没有空格,我们都要能成功执行。
*/

class CalculatorV2 {

    val exit = "exit"
    val help = """
--------------------------------------
使用说明:
1. 输入 1 + 1,按回车,即可使用计算器;
2. 注意:数字与符号之间要有空格;
3. 想要退出程序,请输入:exit
--------------------------------------""".trimIndent()

    fun start() {
        while (true) {
            println(help)

            val input = readLine() ?: continue
            val result = calculate(input)

            if (result == null) {
                println("输入格式不对")
                continue
            } else {
                println("$input = $result")
            }
        }
    }

    /*
    拆分 calculate() 方法主要做了三件事:
    * 第一,将“是否退出”的逻辑封装到了 shouldExit() 方法当中,如果将来这部分逻辑变得更复杂,我们只改动这一个方法即可。
    * 第二,将算式的解析,封装到了 parseExpression() 方法当中,而解析算式的时候也需要解析操作符,这时候我们也需要 parseOperator()。
    * 第三,将具体的计算逻辑交给了对应的方法。这么做的原因,是可以让我们的程序变得更加灵活。
      比如,我们在下个版本当中会更改“加法”的计算逻辑,那么我们就只需要改动这一个方法就行了。
    */
    private fun calculate(input: String): String? {
        if (shouldExit(input)) exitProcess(0)

        val exp = parseExpression(input) ?: return null

        val left = exp.left
        val operator = exp.operator
        val right = exp.right

        return when (operator) {
            Operation.ADD -> addString(left, right)
            Operation.MINUS -> minusString(left, right)
            Operation.MULTI -> multiString(left, right)
            Operation.DIVI -> diviString(left, right)
        }
    }

    private fun addString(left: String, right: String): String {
        val result = left.toInt() + right.toInt()
        return result.toString()
    }

    private fun minusString(left: String, right: String): String {
        val result = left.toInt() - right.toInt()
        return result.toString()
    }

    private fun multiString(left: String, right: String): String {
        val result = left.toInt() * right.toInt()
        return result.toString()
    }

    private fun diviString(left: String, right: String): String {
        val result = left.toInt() / right.toInt()
        return result.toString()
    }

    private fun shouldExit(input: String): Boolean {
        return input == exit
    }

    private fun parseExpression(input: String): Expression? {
        val operation = parseOperator(input) ?: return null
        val list = input.split(operation.value)
        if (list.size != 2) return null
        return Expression(
            left = list[0].trim(),
            operator = operation,
            right = list[1].trim()
        )
    }

    private fun parseOperator(input: String): Operation? {
        Operation.values().forEach {
            if (input.contains(it.value)) {
                return it
            }
        }
        return null
    }

    private fun parseOperator1(input: String): Operation? {
        return when {
            input.contains(Operation.ADD.value) -> Operation.ADD
            input.contains(Operation.MINUS.value) -> Operation.MINUS
            input.contains(Operation.MULTI.value) -> Operation.MULTI
            input.contains(Operation.DIVI.value) -> Operation.DIVI
            else -> null
        }
    }
}

enum class Operation(val value: String) {
    ADD("+"),
    MINUS("-"),
    MULTI("*"),
    DIVI("/")
}

data class Expression(
    val left: String,
    val operator: Operation,
    val right: String
)

fun main() {
    val calculator = CalculatorV2()
    calculator.start()
}

/*
在这个过程中,创建了三个类:
* “Calculator”类,代表整个计算器;
* “Operation”枚举类,代表加减乘除四种运算操作符;
* “Expression”数据类,代表我们算式当中的数字和操作符。
  之后,我们又对计算器的核心功能进行了更细颗粒度的拆分,
  提高了程序的灵活性,为我们的功能扩展打下了基础。
*/

计算器 3.0

/*
针对 3.0 这个版本,我们也分为了两个阶段:
* 第一阶段,增加单元测试。单元测试是软件工程当中的一个概念,它指的是对软件当中的最小可执行单元进行测试,
  以提高软件的稳定性。在 Java 当中,最小单元一般会认为是类,因此,我们一般会以类为单元,对类当中的方法进行一一测试。
* 第二阶段,支持大数的加法。我们知道 Java、Kotlin 当中的整型都是有范围限制的,
  如果我们输入两个特别大的数字进行计算,那么程序是无法正常工作的。因此,我们需要对特别大的数进行兼容。
*/

//在 Kotlin 当中,如果要使用单元测试,我们需要在 gradle 文件当中,添加 Kotlin 官方提供的依赖:
//testImplementation 'org.jetbrains.kotlin:kotlin-test'

单元测试的代码,我们一般会放在工程的 test 目录下:
在这里插入图片描述
可以从这个图中看出很多信息:
·第一,test 目录、main 目录,它们是平级的目录,内部拥有着相同的结构。main 目录下放的是功能代码,test 目录下放的则是测试代码。
·第二,由于我们要开发 3.0 版本,所以我们在 main 目录下创建了 CalculatorV3 这个类;另外,由于我们需要在 3.0 版本加入单元测试,所以对应的,我们在 test 目录下相同的地方,创建了 TestCalculatorV3。这两个类的关系是一一对应的,CalculatorV3 是为了实现 3.0 版本的功能,TestCalculatorV3 是为了测试 3.0 版本的功能,确保功能正常。

/*
针对 3.0 这个版本,我们也分为了两个阶段:
* 第一阶段,增加单元测试。单元测试是软件工程当中的一个概念,它指的是对软件当中的最小可执行单元进行测试,
  以提高软件的稳定性。在 Java 当中,最小单元一般会认为是类,因此,我们一般会以类为单元,对类当中的方法进行一一测试。
* 第二阶段,支持大数的加法。我们知道 Java、Kotlin 当中的整型都是有范围限制的,
  如果我们输入两个特别大的数字进行计算,那么程序是无法正常工作的。因此,我们需要对特别大的数进行兼容。
*/

//在 Kotlin 当中,如果要使用单元测试,我们需要在 gradle 文件当中,添加 Kotlin 官方提供的依赖:
//testImplementation 'org.jetbrains.kotlin:kotlin-test'

//单元测试的代码,我们一般会放在工程的 test 目录下:

class CalculatorV3 {

    private val exit = "exit"
    private val help = """
--------------------------------------
使用说明:
1. 输入 1 + 1,按回车,即可使用计算器;
2. 注意:数字与符号之间要有空格;
3. 想要退出程序,请输入:exit
--------------------------------------""".trimIndent()

    fun start() {
        while (true) {
            println(help)

            val input = readLine() ?: continue
            val result = calculate(input)

            if (result == null) {
                println("输入格式不对")
                continue
            } else {
                println("$input = $result")
            }
        }
    }

    fun calculate(input: String): String? {
        if (shouldExit(input)) exitProcess(0)

        val exp = parseExpression(input) ?: return null

        val left = exp.left
        val operator = exp.operator
        val right = exp.right

        return when (operator) {
            Operation.ADD -> addString(left, right)
            Operation.MINUS -> minusString(left, right)
            Operation.MULTI -> multiString(left, right)
            Operation.DIVI -> diviString(left, right)
        }
    }
/*
* 注释①,我们创建了一个 StringBuilder 对象,用于存储最终结果,
  由于我们的结果是一位位计算出来的,所以每一位结果都是慢慢拼接上去的,
  在这里,为了提高程序的性能,我们选择使用 StringBuilder。
* 注释②,我们定义了两个可变的变量 index,它们分别指向了两个数字的个位,这是因为我们的计算是从个位开始的。
* 注释③,carry,我们用它来存储每一位计算结果的进位。
* 注释④,这个 while 循环当中,我们会让两个 index 从低位一直到高位,直到遍历完它们所有的数字位。
* 注释⑤,这里的逻辑是取每一位上的数字,其中有个细节就是补零操作,比如当程序运行到百位的时候,99 没有百位,这时候 rightVal = 0。
* 注释⑥,当我们的程序计算出结果后,我们要分别算出 carry,以及当前位的结果。这时候我们分别使用“除法”计算 carry,使用“取余”操作计算当前位的结果。
* 注释⑦,这里是为了兼容一个特殊的场景,在“99+1”的情况下,我们的 while 循环最多只会遍历到十位,
  如果不做特殊处理的话,结果将变成“99+1=00”。这并不是我们想要的,所以,为了兼容这种特殊情况,
  我们在 while 循环结束后增加了一个判断,如果 carry=1,那就说明在最大的那一位数计算完以后,仍然有进位,我们要手动添加。
* 注释⑧,对于一个算式“135+99”,我们的 result 拼接其实是倒叙的“432”,这时候我们需要将其翻转一下,才能得到正确的结果“135+99=234”。
*/
    private fun addString(leftNum: String, rightNum: String): String {
        // ①
        val result = StringBuilder()
        // ②
        var leftIndex = leftNum.length - 1
        var rightIndex = rightNum.length - 1
        // ③
        var carry = 0

        // ④
        while (leftIndex >= 0 || rightIndex >= 0) {
            // ⑤
            val leftVal = if (leftIndex >= 0) leftNum.get(leftIndex).digitToInt() else 0
            val rightVal = if (rightIndex >= 0) rightNum.get(rightIndex).digitToInt() else 0
            val sum = leftVal + rightVal + carry
            // ⑥
            carry = sum / 10
            result.append(sum % 10)
            leftIndex--
            rightIndex--
        }
        // ⑦
        if (carry != 0) {
            result.append(carry)
        }

        // ⑧
        return result.reverse().toString()
    }

    private fun minusString(left: String, right: String): String {
        val result = left.toInt() - right.toInt()
        return result.toString()
    }

    private fun multiString(left: String, right: String): String {
        val result = left.toInt() * right.toInt()
        return result.toString()
    }

    private fun diviString(left: String, right: String): String {
        val result = left.toInt() / right.toInt()
        return result.toString()
    }

    private fun shouldExit(input: String): Boolean {
        return input == exit
    }

    private fun parseExpression(input: String): Expression? {
        val operation = parseOperator(input) ?: return null
        val list = input.split(operation.value)
        if (list.size != 2) return null
        return Expression(
            left = list[0].trim(),
            operator = operation,
            right = list[1].trim()
        )
    }

    private fun parseOperator(input: String): Operation? {
        Operation.values().forEach {
            if (input.contains(it.value)) {
                return it
            }
        }
        return null
    }
}

fun main() {
    val calculator = CalculatorV3()
    calculator.start()
}

编写测试代码:

/*
首先,我们定义了一个方法 testCalculate(),
并且使用了一个注解 @Test 来修饰它。因为这样做以后,
IntelliJ 就会知道:哦,这是一个用来做测试的方法。
*/
class TestCalculatorV3 {
    @Test
    fun testCalculate() {
        val calculator = CalculatorV3()

        val res1 = calculator.calculate("1+2")
        assertEquals("3", res1)
    }
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-16 13:11:02  更:2022-01-16 13:12:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:23:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码