Clojure 程式設計/示例/與 Excel 互動
透過 COM 與其他程式互動在 Java 中使用 JACOB 庫可以很容易地實現。但是,處理與應用程式互動的所有底層介面非常繁瑣。特別是由於該介面是為 C 設計的,而不是為 Java 設計的,更不用說 Clojure 了。因此,我們將嘗試利用 Clojure 的強大功能來改善這種情況。
讓我們從一個簡單的例子開始。我們啟動 Excel,使例項可見。然後我們再次關閉整個系統。
(import '(com.jacob.com Dispatch
ComThread))
(def xl (new Dispatch "Excel.Application"))
(Dispatch/put xl "Visible" true)
(Dispatch/call xl "Quit")
(ComThread/Release)
介面的醜陋之處應該立即顯現出來。我們必須透過 Java 呼叫 C 介面。所以讓我們封裝 “Visible” 呼叫。
(defn excel-application-visible
([xl]
; No argument? Return the current value.
(Dispatch/get xl "Visible"))
([xl v]
; Argument given? Set it as new value.
(Dispatch/put xl "Visible" v)))
(excel-application-visible xl true)
這要好多了。我們已經將底層介面隱藏在一個簡單的 Clojure 函式呼叫後面。現在我們必須將所有屬性和函式呼叫包裝到這樣的函式中。但是等等!到目前為止,這沒什麼特別。只是一個函式呼叫。Clojure 可以在這裡為我們節省一些工作。
定義幾個函式來快速封裝介面,顯示了很多重複。以之前的例子為例:我們傳入 Excel 例項,可能還有一個值,並相應地呼叫底層介面。但這是相同的任務。所以我們可以將此功能放到一個驅動函式中,並使用部分求值來建立包裝函式。
(defn property-driver
([prop xl]
(Dispatch/get xl prop))
([prop xl v]
(Dispatch/put xl prop v)))
(def excel-application-visible (partial property-driver "Visible"))
(excel-application-visible xl true)
所以這裡發生了什麼?我們定義了一個新的函式,該函式也由屬性名稱引數化。然後我們使用內建的 partial 來建立一個專門的函式,該函式專門用於作用於 “Visible” 屬性。所以 partial 有一個函式作為返回值。
現在我們有一組包裝器來隱藏底層介面。但是第一個例子中的最後一條指令怎麼辦:(ComThread/Release)?好吧,這是為了清理 COM 端,必須在每個使用 COM 的執行緒中呼叫。當遇到異常時,這會變得特別繁瑣。為了擺脫底層處理,我們使用 Clojure 強大功能的另一個部分:宏!
(defn with-excel*
[thunk]
(let [worker (fn []
(let [xl (excel-application-new)]
(try
(thunk xl)
(finally
(excel-quit xl)
(ComThread/Release)))))
t (new Thread worker)]
(. t start)
(. t join)))
(defmacro with-excel
[varname & body]
`(with-excel* (fn [~varname] ~@body)))
首先我們再次定義一個驅動函式,它完成繁重的工作。它接受一個函式,在新建的執行緒中呼叫。函式傳遞給 Excel 例項。所有內容都包裝在 try 中以確保即使函式丟擲異常也能正確清理。
當然,我們的目的不是每次想要呼叫這個包裝器時都手動定義一個新函式。因此我們定義了一個宏,它為我們做了這件事。它接受一個符號,該符號使 Excel 例項可用於主體。主體被包裝在一個匿名函式中,該函式將主體關閉在其繫結上,並將所有內容打包到我們的包裝器中。
使用這些修改重寫我們的初始示例,我們得到了這段簡潔的程式碼。
(with-excel xl
(excel-application-visible xl true))
當然,這個例子並不實用,但它清楚地顯示了與之前純 Java 程式碼相比的改進。此外,力量在於我們看不到的部分:即使我們在 with-excel 中丟擲異常,一切也會被清理乾淨。
進一步的實驗留給使用者。遺漏的內容包括:例如,一個構建包裝器的宏,或者一個處理 COM 介面 Variant 型別的函式。所以去實驗,享受 樂趣!
存在 docjure - Clojure 庫,它使用 Apache POI 允許讀寫 MS Office 檔案(電子表格)。
示例
(use 'dk.ative.docjure.spreadsheet)
; Load a spreadsheet and read the first two columns from the price list sheet:
(->> (load-workbook "spreadsheet.xlsx")
(select-sheet "Price List")
(select-columns {:A :name, :B :price}))
> [{:name "Foo Widget", :price 100}, {:name "Bar Widget", :price 200}]