programming  

Nov 3, 2016 • Michael Chen

最近在 Biostars 上看到這個蠻有意思的討論串,主要是在探討在開發生物資訊軟體時,是否應該要以 BioPerl、BioPython 或其他的類似的專案為基礎,或者應該重新實作相關的函式庫。在程式圈裡流傳著這樣一句話:「不要重覆發明輪子。」 (Don’t re-invent the wheel.) 這句話主要的用意是提醒程式設計師,要善用已有的函式庫、框架、開發工具等,減少重覆實作相同的演算法所浪費的時間和心力。但是對於生物資訊學家來說,這個問題的答案並不是那麼單純。

有些軟體專案,經過多年的實戰經驗,幾乎變成程式設計者共同的資産。像是 Java 平台的穩定性相當良好,吸引許多程式設計師在其上開發函式庫,甚至有數個 Java 平台上的新語言,像是 Groovy, Clojure, Scala, Jython, JRuby 等等。在生物資訊圈,也可以用學術論文來衡量某個軟體專案的可信度。然而,學術論文評量的是該軟體專案在學理上的正確性,有時和該專案的軟體品質不完全成正比。如果有在看生物資訊的學術論文,會發現有些論文的想法很新穎,也解決了很重要的問題。但是,實際去看看該專案,卻發現各種慘不忍睹的情形,像是專案中混合著數種命令稿語言,將指令或檔案路徑寫死在程式碼中,沒有良好的套件形式,缺乏易用的安裝機制,缺乏清楚的說明文件等。如果稍微研究過,應該不會想把自已的專案建立在這樣子的基礎上。

值得慶幸的是,BioPerl 或 BioPython 或其他類似的軟體專案,程式碼的品質好得多。不過,這些專案仍然相對缺乏說明文件,要花一些時間去學習才能上手;而且,這些專案意圖解決的問題較冷門,使用者量也較小,相關的教程或文章因而較難找到,使得這個問題更加惡化。那麼,這類型的函式庫是否就完全無用武之地呢?其實,端看使用這些函式庫的目的是什麼,不同的目的其考量也有所不同,不能一概而論。

如果是要將這些函式庫應用在某個特定的問題,撰寫內部使用的命令稿。那麼,這些函式庫還是值得學習的。我們第一次撰寫命令稿時,有時候只考量到特定的情形,往往沒有針面各種特殊的情形去撰寫測試程式。這些函式庫,雖然沒辦法保證完全沒有臭蟲,大抵上是可用的,其中有些專案,還先行撰寫過一些測試程式,就更能保證程式碼的品質了。不過,天下沒有白吃的午餐,第一次使用這些函式庫時,還是需要花費額外的心力去學習。這類型的函式庫,試圖去解決生物資訊中常見的問題,使得這類函式庫的功能包山包海,變成一個龐大的函式/物件的集合。這些函式庫的初學者,時常要在 API 文件中查找半天,才找到自已所需的功能,因而抵銷了使用函式庫帶來的便利。

如果時常要從事生物資訊相關的任務,可以選定某個自己熟悉的命令稿語言,花一些時間學習該語言的 BioSomething 函式庫。這些任務多多少少會有重覆性,前期投資在學習這些函式庫的心力,會像倒吃甘蔗般,大幅減少後續所花費的心力。另外,對於學習的歷程,最好能用某種方式記錄起來,像是部落格 (blog) 或維基 (wiki) 等。以後碰到重覆的流程,不用再從 API 文件中大海撈針,只要查一下自己的筆記,很快就可以解決手邊的問題。由於這類型的函式庫過一段時間有可能又會出現新的,而這些專案間其實是用不同的程式語言解決類似的問題領域,也不太需要去追新,就當做是新奇有趣的玩具,把玩一下即可。

然而,如果我們是要以這些函式庫為基礎,繼續開發新的生物資訊軟體,就應該要更加慎重。使用這些函式庫,就代表自己放棄了這些部分的程式碼的控制權。難保這些專案在一段時間後,不會更動其 API 或實作,造成預期之外的行為。如果將這些函式庫和自己的專案分開,讓使用者盲目地分別安裝,這個情形就更加嚴重了。使用者在分別安裝兩個專案後,如果出現問題,使用者往往無法分辨是這些函式庫還是我們的專案的程式碼所造成的問題,而使用者通常也不會那麼熱心地幫開發者除錯,這也影響到使用者對我們專案的信任度。雖然這些生物資訊軟體不會拿來營利,使用者的引用 (citation) 數,也有可能會影響下一年度的資助 (funding)。

如果我們想使用某個 BioSomething 函式庫,我們應該要對這些程式碼有更多的控制權。例如,Java 可以將外部的函式庫和自己專案的程式碼包裝成一個 JAR 檔,透過這樣的方式,我們就可以指定使用某個特定版本的外部程式碼,而不會受到外部專案更動的影響。或者,Python 和 Ruby 在建立套件時,可以透過一些機制,指定外部專案的版本,使得不確定因素減至最低。其他語言在建立套件時,通常也會有類似的機制,讀者可自行查閱相關文件。

我們在使用這些外部專案時,應該寫一個簡易的 wrapper 函式或物件,在呼叫相關功能時,呼叫這些 wrapper 函式/物件即可,而不要讓這些外部專案直接影響我們的程式碼。雖然撰寫和呼叫 wrapper 看起來有些冗餘,難保我們以後不會因為某些因素抽換這些實作內容,這時候,我們只要更換 wrapper 內部的程式碼,而其他的部分都不需要再去更動,對我們的專案影響可降到最低。

不論是引用外部函式庫,或是自行重新實作,相關的測試程式仍然不可省略。雖然撰寫測試程式需要額外的心力,日後專案的程式碼有所變動時,這些測試程式可以確保我們的專案可以正確地運作。很多的現代程式語言,都有自已的 testing framework 可用,即使是像 C 或 C++ 這種有年紀的語言,也在近年來發展出像是 CUnit 或 CppUnit 等 testing framework。透過這些框架,更可將這些流程自動化,節省程式設計者的時間。

BioPerl、BioPython 或其他類似的專案,的確可以減少重覆開發的時間和心力。然而,天下沒有白吃的午餐,對於這些函式庫,仍然要去了解和評估,而不是盲目地安裝和使用。透過本文介紹的方法,可以讓這些外部程式碼對我們專案的影響降到最低,確保專案運作順暢。