From c4e90971c97a24c1f61fe1a8764e6aace584aaa5 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 12 Oct 2018 20:45:18 -0400 Subject: [PATCH] improved control structure usage --- .idea/misc.xml | 2 +- res/sample.grades | 2 +- src/three_feature.kt | 75 ++++++++++++++++++++++++++------------------ 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index bfbb0ff..e0844bc 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/res/sample.grades b/res/sample.grades index a70e9e4..0f67dc8 100644 --- a/res/sample.grades +++ b/res/sample.grades @@ -24,7 +24,7 @@ programming languages: 89 differential equations: 59 differential equations: 97 software architecture: 56 -parallel and distributed computing: 89 +parallel and distributed computing: parallel and distributed computing: 66 differential equations: 58 software architecture: 98 diff --git a/src/three_feature.kt b/src/three_feature.kt index c56850f..aa41724 100644 --- a/src/three_feature.kt +++ b/src/three_feature.kt @@ -1,51 +1,64 @@ import java.io.File +class ReportCard { + // Maps and Lists are, by default, immutable. + // Specify our data structure to be mutable at both levels + private val courses = mutableMapOf>() + + // use getOrPut to get a list of course grades, and add the new grade to that list + fun addAssignment(course: String, grade: Int) = courses.getOrPut(course) { mutableListOf() }.add(grade) + + fun grades(func: (String, String, Double) -> Unit) { + // Iteration over maps is easy when combined with iterable unpacking + for ((course, grades) in courses) { + val average = grades.average() // List has helper methods such as `sum()` and `average()` + + // use a `when` block as an alternative to case. Supports rich pattern matching, + // but we just use it here to check which grade range we're in + val letter = when { + average > 90 -> "A" + average > 80 -> "B" + average > 70 -> "C" + average > 60 -> "D" + else -> "F" + } + + // call back to the block given + func(course, letter, average) + } + } +} + +// Create an extension method on `Double` that allows us to specify precision +fun Double.format(digits: Int) = java.lang.String.format("%.${digits}f", this) + fun main(args: Array) { // File I/O is easily done with `File()` constructor val file = File("res/sample.grades") - // Maps and Lists are, by default, immutable. - // Specify our data structure to be mutable at both levels - val courses = mutableMapOf>() + val report = ReportCard() - // File supports line iteration with forEachLine, which accepts a code block. + // `File` supports line iteration with `forEachLine`, which accepts a code block. // `it` is the iteration variable in the code block file.forEachLine { // It is possible to unpack iterables into multiple variables // This is very brittle, though, if improperly formatted - val (rawCourse, rawGrade) = it.split(":") + val (rawCourse, rawGrade) = it.split(":").let { + Pair(it[0], it.getOrElse(1) { _ -> "" }) + } // use `trim()` to strip whitespace from either side of the strings // string conversion is easy with helper methods like `toInt()` and `toIntOrNull()` val course = rawCourse.trim() - val grade = rawGrade.trim().toInt() - - // collection testing is easy with `in` and `!in` operators - if (course !in courses) - courses[course] = mutableListOf() // implicit type arguments are passed: `mutableListOf()` - - // use `!!` to assert the hash lookup will not be null. We just added it to the map. - courses[course]!!.add(grade) + val grade = rawGrade.trim().toIntOrNull() + grade?.let { report.addAssignment(course, it) } } - - // Iteration over maps is easy when combined with iterable unpacking - for ((course, grades) in courses) { - val average = grades.average() // List has helper methods such as `sum()` and `average()` - - // use a `when` block as an alternative to case. Supports rich pattern matching, - // but we just use it here to check which grade range we're in - val letter = when { - average > 90 -> "A" - average > 80 -> "B" - average > 70 -> "C" - average > 70 -> "D" - else -> "F" - } - - // input and output from console are as easy as `println()` and `readline()` - // uses string interpolation for easier formatting - println("$course:\n\t$letter ($average)\n") + // input and output from console are as easy as `println()` and `readline()` + // uses string interpolation for easier formatting + // grade precision is specified with the `format` extension method defined above + report.grades { course: String, letter: String, average: Double -> + println("$course:\n\t$letter (${average.format(2)})\n") } } \ No newline at end of file