iT邦幫忙

2024 iThome 鐵人賽

DAY 15
0
Software Development

六邊形戰士程式設計系列 第 15

D15 - 樹狀搜尋問題 非同步版 Kotlin 篇

  • 分享至 

  • xImage
  •  

請參考 D14 - 樹狀搜尋問題 非同步版 說明篇

今天我們嘗試換個語言,用 kotlin 來解決非同步版的樹狀搜尋問題,並觀察語言之間的差異


data class Node(
    val id: String,
    val text: String,
    val childrenIds: List<String>,
)

class NodeNotFoundError(
    val id: String,
) : Exception()

suspend fun getNode(id: String): Node =
    when (id) {
        "a" -> Node("a", "a", childrenIds = listOf("a1", "a2", "a33"))
        "a1" -> Node("a1", "a1", childrenIds = listOf("a11"))
        "a11" -> Node("a11", "a11", childrenIds = listOf())
        "a2" -> Node("a2", "a2", childrenIds = listOf())
        "a3" -> Node("a3", "a3", childrenIds = listOf())
        else -> throw NodeNotFoundError(id)
    }

suspend fun getNameById(
    id: String,
    target: String,
): String? =
    runCatching { getNode(id) } // 把錯誤包裹成 Result Class
        .fold( // 分別處理錯誤和成功,並整合成一種型別 String? (nullable String)
            onSuccess = { node ->
                if (node.id == target) {
                    node.text // 找到,回傳
                } else {
                    // 沒找到,往下一層找
                    node.childrenIds.map { getNameById(it, target) }.find { it == target }
                }
            },
            onFailure = {
                // 有某個 id 找不到,進行錯誤處理
                println("get id $id failed, skip this node")
                it.printStackTrace()
                null
            },
        )

對照 Typescript 的寫法如下

Typescript Kotlin 解決的問題 差異
async/await suspend 把非同步寫作風格簡化 kotlin 相對更簡單
Union Type (E|T) Result<T> 更加安全的進行錯誤處理 kotlin 缺少錯誤型別
Promise.all 更有效率的非同步流程控制 原生 kotlin 缺少有效的非同步控制工具
early return when expression 簡化程式碼複雜度 -

要解決以上方法其實也非常簡單,只需要安裝 Arrow-KT 即可

        <dependency>
            <groupId>io.arrow-kt</groupId>
            <artifactId>arrow-core</artifactId>
            <version>1.2.4</version>
        </dependency>
        <dependency>
            <groupId>io.arrow-kt</groupId>
            <artifactId>arrow-fx-coroutines</artifactId>
            <version>1.2.4</version>
        </dependency>

有了新工具,我們再根據新工具改寫一下

//不拋出錯誤,而是用回傳型別包裹錯誤
suspend fun getNodeV2(id: String): Either<NodeNotFoundError, Node> = 
    when (id) {
        "a" -> Node("a", "a", childrenIds = listOf("a1", "a2", "a33")).right()
        "a1" -> Node("a1", "a1", childrenIds = listOf("a11")).right()
        "a11" -> Node("a11", "a11", childrenIds = listOf()).right()
        "a2" -> Node("a2", "a2", childrenIds = listOf()).right()
        "a3" -> Node("a3", "a3", childrenIds = listOf()).right()
        else -> NodeNotFoundError(id).left() 
    }
    

suspend fun getNameByIdV2(
    id: String,
    target: String,
): String? =
    getNodeV2(id)
        .fold(
            ifRight = { result ->
                if (result.id == target) {
                    result.text
                } else {
                    // parMap 相當於平行地執行 map,解決效能問題
                    result.childrenIds
                        .parMap { getNameByIdV2(it, target) }
                        .find { it == target }
                }
            },
            ifLeft = {
                // 錯誤處理時也可以具有型別安全性
                println("get id ${it.id} failed, skip this node")
                it.printStackTrace()
                null
            },
        )

最後我們會得到一個新的表格如下

Typescript Kotlin 解決的問題 差異
async/await suspend 把非同步寫作風格簡化 kotlin 相對更簡單
Union Type (E|T) Either<E|T> 更加安全的進行錯誤處理 -
Promise.all parMap 更有效率的非同步流程控制 -
early return when expression 簡化程式碼複雜度 -

上一篇
D14 - 樹狀搜尋問題 非同步版 說明篇
下一篇
D16 - 樹狀搜尋問題 非同步版 Python 篇
系列文
六邊形戰士程式設計30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言