iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 30
0
Software Development

新手也能懂的 Kotlin Collection 賞玩門道系列 第 30

第三十天:活用 Collection - 用 kscript 做資料處理

眾所皆知 Kotlin 是一個需要編譯的程式語言,所以每次一更動程式就必須重新編譯一次。雖然編譯式的語言對程式正確性和效能都有不錯的幫助,但假如你是拿 Kotlin 做大量的資料處理時,這樣的特性讓開發效率無法與其他直譯語言相比。有沒有什麼辦法可以讓 Kotlin 有類似直譯語言的敏捷,同時又有編譯語言的安全與效能呢?

我們很幸運,Kotlin 還真的可以!

初探 Kotlin Script

Kotlin 雖然是編譯式語言,每次運行在 JVM 前都需要先用 Kotlin 編譯器編譯,編譯後再用 java 指令執行:

$ kotlinc [file].kt -include-runtime -d [file].jar
$ java -jar [file].jar

但其實 Kotlin 編譯器有另外一種如同直譯語言運行程式的方式,上面這兩行指令用 Kotlin Script 來執行的話,就只需要一行:

$ kotlinc -script [file].kts

使用 Kotlin Script 有 3 個重點:第一,程式裡不需要 main,或說 Kotlin Script 會把整個檔案當成 main 來執行;第二,Kotlin Script 的檔案要以 .kts 做為副檔名;第三,使用 kotlinc 編譯時,要加 -script 參數。

讓 Kotlin Script 更棒的 kscript

雖然 Kotlin Script 為 Kotlin 帶來了動態運行的特性,但仍不夠完美。比方說,Kotlin 編譯器實際上還是每次都得重新編譯,就算沒有更動程式碼也一樣;對 IDE 不友善;無法載入相依套件;也沒有打包部署需要的 jar 檔的功能。

幸運的是,由 Kotlin 社群的高手設計出 kscript 工具,解決了上述的這些問題,讓 Kotlin Script 更好用!

安裝/執行 kscript

要使用 kscript 很簡單,只要用第三天介紹的 SDKMAN,即可一行指令搞定:

$ sdk install kscript

接著,我們就可以把原本用 kotlinc 的指令換成:

$ kscript [file].kts

甚至我們也可以在 Script 檔案的開頭定義執行方式。

#!/usr/bin/env kscript

然後就可以用像是 Bash Script 的方式行執行:

$ chmod +x [file].kts
$ ./[file].kts

有了 kscript,假如在 Kotlin Script 裡需要使用第三方套件,也可以用 Annotation 的方式載入,而不需要依頼 Gradle 等工具。

@file:DependsOn("[套件名稱]:[套件版本]")

kscript 也會自動幫你 cache 編譯的結果,若是程式碼沒有變更的話就會直接執行。另外,若想部署程式,也可以一行指令打包成 fat-jar。

$ kscript --package [file].kts

kscript 來做資料處理

其實 kscript 的作者 Holger Brandl 之所以開發 kscript,是因為他覺得 Kotlin 是一個非常適合拿來做資料處理的程式語言(驚訝吧!居然不是 Python),而且效能還不差。甚至為了讓 kscript 更好用,他也設計了一包 kscript-support 專門拿來處理大量資料。

只要新增一個 .kts 的檔案,先定義以下這幾行:

#!/usr/bin/env kscript

// 載入 kscript-support
@file:DependsOn("com.github.holgerbrandl:kscript-support:1.2.4")

import kscript.text.*

// 從 Terminal 接收要處理的檔案
val lines = resolveArgFile(args)

有了以上的基礎後,就可以傳入要處理的 .csv 資料,kscript 就會把每一行的數據放到 lines 變數。接著我們就可以用 Collection 的操作來處理這些資料,提取出我們想要的結果。

lines.map { it.trim() }.print()

上面這段範例會把每一行資料的空白或 tab 去除後印出,等同於我們用 awk 這樣處理。

awk '{sub(/[ \t]*$/, "");print}' data/some_flights.tsv

我們也可以截取某一段資料、增加或刪除欄,處理像這種表格型 .csv 資料就可以更輕鬆了。

lines.split().select(10, 1, 12).print()
// 選取每一列資料裡的第 10、1、12 欄並印出
// 等同 awk '{print $10, $1, $12}' data/some_flights.tsv

lines.split().map { listOf(it[1], it[2], "F11-"+ it[7]) }.print()
// 增加一個欄位
// 等同 awk '{print $1, $2, "F11-"$7}' some_flights.tsv

lines.split().selectByName(-3).print()
// 刪除一個欄位
// 等同 awk '!($3="")' data/some_flights.tsv

拿 Kotlin 做資料科學有沒有搞頭?

看完這個系列後,你應該會發現,Kotlin Collection 非常的強大,除了有完整的 API 操作外,加上 kscript 提供的工具,拿 Kotlin 做資料科學不是夢。以 Holger Brandl 在 kscript as substitute for awk 裡分享的數據,效能並不輸給其他工具呢!

身為 Kotlin 愛好者的你,別忘了善用手上好用的工具喔!

參考資料


上一篇
第二十九天:活用 Collection - Scope Function
下一篇
回顧與展望
系列文
新手也能懂的 Kotlin Collection 賞玩門道31

尚未有邦友留言

立即登入留言