跳转到内容

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}]
华夏公益教科书