본문 바로가기

코틀린

코틀린: 5장 테스트에 도전해 보세요 (정답 해설)

문제 1  4개의 Person 타입 객체를 갖는 시퀀스 heroes 를 생성했습니다. 
(1) 나이가 가장 젊은 객체를 찾으세요.
(2) 이름 순으로 객체를 정렬하세요.
(3) 미혼인 객체를 모두 찾으세요.
(4) 객체의 평균 나이를 구하세요.

class Person(val name: String,
             val age: Int,
             val gender: Char,
             val martialStatus: Boolean)  {
    override fun toString() = "$name $age $gender $martialStatus"
}

fun main() {
    val heroes = sequenceOf(
        Person("홍길동", 21, 'M', false),
        Person("신사임당", 47, 'F', true),
        Person("유관순", 18, 'F', false),
        Person("임꺽정", 33, 'M', true),
    )

    println(heroes.minByOrNull { it.name }) // 가나다 순으로 첫 번째 객체
    // println(heroes.maxByOrNull { it.name })
    println(heroes.minByOrNull { it.age }) // 나이 순으로 가장 젊은 객체
    // println(heroes.maxByOrNull { it.age })

    println(heroes.sortedBy { it.age }.toList()) // 나이를 기준으로 정렬
    // println(heroes.sortedByDescending { it.age }.toList())

    println(heroes.filter { it.martialStatus }.toList()) // 기혼 객체만을 찾음
    // println(heroes.filter { !it.martialStatus }.toList())

    val n = heroes.count()
    println("나이 평균 = " + heroes.sumOf { it.age / n })
}

 

문제 2  검색 조건을 클래스로 만들어 최종 검색은 클래스 FindPersonFilters의 메소드 foundPerson()에서 구현하면 됩니다. 아래 예제는 시퀀스 객체 heroes에서 성이 '홍'씨이고, '미혼'인 사람을 찾습니다.

class Person(val name: String, ...)  {
    override fun toString() = "$name $age $gender $martialStatus"
}

class FindPersonFilters {
    var familyName: String = ""
    var married: Boolean = false
    fun foundPerson(): (Person) -> Boolean {
        val p = { p: Person ->
            p.name.startsWith(familyName) && p.martialStatus == married
        }
        return p
    }
}

fun main() {
    val heroes = listOf(
        Person("홍길동", 21, 'M', false), ...)

    val filters = FindPersonFilters()
    with (filters) {
        familyName = "홍"
        married = true
    }
    val found = heroes.filter(filters.foundPerson())
    if (found != null && found.isNotEmpty())
        println(found.toString())
    else
        println("not found")
}

 

문제 3  1부터 시작해 3의 배수를 생성하는 시퀀스([1, 3, 9, 27, 81]를 생성하고, 이 시퀀스를 3진수로 변환하시오. 단,  (1, 1), (3, 10), (9, 100), ... )과 같이 10진수 숫자와 3진수 숫자를 같이 출력해야 합니다. associate 메소드를 적용하는 것이 핵심입니다.

fun main() {
    val seq = generateSequence(1) { if (it > 50) null else it*3 }
        .associate {
            val num = it.toString(3)
            "$it" to num
        }
    println(seq.toList())
}

 

문제 4 리스트 컬렉션 객체 stat 는 어떤 사이트(site)의 웹/앱 방문 통계 데이터를 저장하고 있습니다. 이를 위해 2개의 enum class(SNS, OS)와 1개의 data 클래스(VisitorStatistics)를 정의하고 있습니다.
(1) 운영체제 Windows를 사용해 접속한 사용자들의 평균 방문 시간(duration) 을 구하세요.
(2) 월별 방문 건수 최대인 원소를 구하세요.
(3) mobile 운영체제(ANDROID, IOS)를 사용해 접속한 사례 중 월별 방문 건수가 최대인 원소를 구하세요.
(4) 주별 방문 건수의 총합을 구하세요.

enum class SNS { CACAO, FACEBOOK, TWITTER, YOUTUBE, INSTAGRAM }
enum class OS { WINDOWS, MAC, ANDROID, IOS }

data class VisitorStatistics (
    val weeklyVisit: Int,  // 주별 방문 건수
    val monthlyVisit: Int, // 월별 방문 건수
    val duration: Double,  // 평균 방문 시간(단위: 분)
    val os: OS,
    val sns: SNS
)

val stat = listOf(
    VisitorStatistics(61, 225, 11.8, OS.WINDOWS, SNS.CACAO),
    VisitorStatistics(86, 347, 12.3, OS.IOS, SNS.INSTAGRAM),
    VisitorStatistics(98, 391, 12.5, OS.ANDROID, SNS.INSTAGRAM),
    VisitorStatistics(76, 248, 16.7, OS.MAC, SNS.YOUTUBE),
    VisitorStatistics(59, 233, 16.9, OS.ANDROID, SNS.YOUTUBE),
    VisitorStatistics(82, 251, 17.4, OS.WINDOWS, SNS.YOUTUBE),
    VisitorStatistics(11, 27, 8.6, OS.WINDOWS, SNS.FACEBOOK),
    VisitorStatistics(3, 15, 5.1, OS.IOS, SNS.TWITTER),
)

fun main() {
    val avgDuration = stat
        .filter { it.os == OS.WINDOWS }
        .map(VisitorStatistics::duration)
        .average()
    println(String.format("%.2f", avgDuration))
    
    println(stat.maxByOrNull { it.monthlyVisit }.toString())
    
    val maxMonthlyVisit = stat
        .filter { it.os in setOf(OS.IOS, OS.ANDROID)  }
        .maxByOrNull { it.monthlyVisit }
    println(maxMonthlyVisit.toString())
    
    val totalWeeklyVisit = stat.sumOf { it.weeklyVisit }
    println(totalWeeklyVisit.toString())
}

 

문제 5 문제 4의 답을 메소드 체이닝(method chaining) 방식으로 구현했다면 이를 확장 함수로 구현해 보는 문제입니다.
(1) 문제 4의 "
(1) 운영체제 Windows를 사용해 접속한 사용자들의 평균 방문 시간(duration) 을 구하세요."를 확장 함수로 구현해 보세요

fun List<VisitorStatistics>.avgDuration(os: OS) =
    filter { it.os == os }.map(VisitorStatistics::duration).average()

fun main() {
    println(stat.avgDuration(OS.WINDOWS))
}


(2) (1)번의 확장 함수를 함수 타입을 선언하는 방식으로 수정해서 문제 4의 (3)을 구현해 보세요.

fun List<VisitorStatistics>.modifiedAvgDuration(func: (VisitorStatistics) -> Boolean) =
    filter (func).map(VisitorStatistics::duration).average()

fun main() {
    val averageWindowDuration = stat
        .modifiedAvgDuration { it.os in setOf(OS.IOS, OS.ANDROID) }
    println(averageWindowDuration)
}