跳轉到內容

Clojure 程式設計/示例/與 Excel 互動

來自 Wikibooks,開放的書本,開放的世界

透過 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}]
華夏公益教科書