// represents that a linked list can either be empty, // or a node with numeric data and a reference to // the next such list sealed class LinkedListNumbers { object Empty: LinkedListNumbers() data class Node( val myData: Int, val next: LinkedListNumbers ): LinkedListNumbers() } val emptyNumberList = LinkedListNumbers.Empty val list1 = LinkedListNumbers.Node(1, emptyNumberList) val list21 = LinkedListNumbers.Node(2, list1) val list521 = LinkedListNumbers.Node(5, list21) println(list521) // guiding questions for type-driven development, // based upon the "main" input type ... // // 1. how many value/type cases? // (if >1, `when`; types -> `is`) // 2. what sub-pieces can you extract? // (if data class, all fields) // 3. are sub-pieces are designed types? // (if so, call helper based on that type) // // Example: L09_01 (Quiz 1 Problem 3) // // TODO: apply to LinkedListNumbers // fun doSomethingLL(ll: LinkedListNumbers) { // when (ll) { // is LinkedListNumbers.Empty -> ??? // is LinkedListNumbers.Node -> // ll.myData // doSomethingLL(ll.next) // } // } // guiding process for resulting recursion... // 1. what to return in the base case(s)? // 2. now imagine the recursive call worked... // a) what type would you get? // b) what would that represent? // c) focus on just the current value, // how can you combine it with the // recursive result to make this // current element work? // // example... sum up a list of numbers // 1. what is the "sum" of an empty list? // 2. given a list, consider the sum of // all numbers after the first element... // a) what is the type of that? // b) what does it represent? // c) how can we combine it with the // first number to produce the // overall sum? // fun llSum(ll: LinkedListNumbers): Int { return when (ll) { is LinkedListNumbers.Empty -> 0 is LinkedListNumbers.Node -> ll.myData + llSum(ll.next) } } // TODO: what *must* be tested? // (given the input type) println(llSum(emptyNumberList)) // 0 println(llSum(list521)) // 8 // TODO: product of the numbers in a list fun llProd(ll: LinkedListNumbers): Int { return when (ll) { is LinkedListNumbers.Empty -> 1 is LinkedListNumbers.Node -> ll.myData * llProd(ll.next) } } println(llProd(emptyNumberList)) // 1 println(llProd(list521)) // 10 // WARNING fun scary(nums: LinkedListNumbers): Int { return when (nums) { is LinkedListNumbers.Empty -> 0 is LinkedListNumbers.Node -> nums.myData + scary(nums) } } // println(scary(emptyNumberList)) // println(scary(list521)) // we can also use this type of iteration with // Kotlin lists; consider the following // self-referential definition... // a Kotlin list is either.. // - empty // - an element, followed by a Kotlin list // fun doSomethingKL(l: List) { // when (l.isEmpty()) { // true -> ??? // false -> // l[0] // doSomethingKL(l.drop(1)) // } // } // TODO: design mySum fun mySum(l: List): Int { return when (l.isEmpty()) { true -> 0 false -> l[0] + mySum(l.drop(1)) } } println(mySum(emptyList())) // 0 println(mySum(listOf(1, 2, 5))) // 8