clojure/clojurescript 與 left-pad

最近鬧得沸沸揚揚的事情,大概就是 Azer Koçulu 在 npm 上下架 left-pad這件事,導致許許多多依賴他的 Node.js 專案 不能使用

就讓我們趁這個機會,看一下我們在 clojure/clojurescript 下到底有沒有需要這個函式庫,或是是否已經有內建可替代的函式可以處理這個功能。

leftpad 做了哪些事?

在我們開始前,讓我們來看看 left-pad 這個函式庫到底多好用,為什麼一堆套件都依賴他。實際上 left-pad 只有短短的幾行,內容如下:

function leftpad (str, len, ch) {
    str = String(str);

    var i = -1;

    if (!ch && ch !== 0) ch = ' ';

    len = len - str.length;

    while (++i < len) {
        str = ch + str;
    }

    return str;
}

而他的功能也很單純,就是將輸入的東西依據設定,進行 padding 的動作

var leftpad = require('left-pad')

leftpad('foo', 5)
// => "  foo"

leftpad('foobar', 6)
// => "foobar"

leftpad(1, 2, 0)
// => "01"

如何在 clojure 上實作

那我們在 clojure 下要怎樣實作 leftpad 這函式的功能呢?實際上我們使用 clojure.core/format 搭配一些字串的替代就可以辦到 (註: 參考自 java – Left padding a String with Spaces and Zeros 一文)

(defn leftpad
  "If S is shorter than LEN, pad it with CH on the left."
  ([s len] (leftpad s len " "))
  ([s len ch]
   (-> (format (str "%" len "s") (str s))
       (clojure.string/replace #"\s" (str ch)))))

(leftpad 1 2 0)
;; => "01"

當然像這樣用字串查找替代的方式很容易打死自己,此外,clojure.core/format 因為 CLJS-324 ISSUE 的關係,目前是無法在 clojurescript 上使用的,因此我們應該考慮看看 clojure/clojurescript 都可以共用的 cl-format

如何在 clojurescript 上實作

cl-format 是移植自 common lisp 的 format 函式,是一種非常強大的字串格式化方式。具體範例在 Praticle Common Lisp 一書內文有提到,我們在這邊只注重如何透過 cl-format 進行 padding:

(cl-format nil "~5,'qd" "hi")
;; => "qqqhi"

(cl-format nil "~6,'0d" 1)
;; => "000001"

(cl-format nil "~7d" 1)
;; => "      1"

了解了 cl-format 在 padding 功能上的使用,我們就可以透過它來製作 clojure/clojurescript 皆能使用的 leftpad 函式:

(ns coldnew.left-pad
  (:require #?(:clj  [clojure.pprint :refer [cl-format]]
               :cljs [cljs.pprint    :refer [cl-format]])))

(defn leftpad
  "If S is shorter than LEN, pad it with CH on the left."
  ([s len] (leftpad s len " "))
  ([s len ch]
   (cl-format nil (str "~" len ",'" ch "d") (str s))))

後記

本文只是我看到了 NPM & left-pad: Have We Forgotten How To Program? 這篇文章後,來確認一下我到底會不會寫程式之用,文內實作的 clojure/clojurescript 版本你可以在 coldnew/left-pad.clj 這個專案看到。

如果你想看看 emacs-lisp 下要怎樣實作 leftpad 函式,則可以在 coldnew/left-pad.el 看到我實作的範例。

實際上 leftpad 的事件還衍生了另外一個問題: 套件是否可以這麼容易被下架?

在 Clojure 的世界中,我們會把我們的專案放置到 Clojars 去,而在 Clojars 則是這樣規定 How do I delete a jar?

Deletion of entire projects or particular versions is discouraged as you will break anyone's build that depends on the project or version. There is also a cost associated with deletion, as it requires an admin's time. If you're sure nobody is using your jar or absolutely must remove it open an issue against the Clojars GitHub project. If you need it done privately contact the server caretakers. Before doing so, push a new revision with "Delete me" in the description in order to prove you're the owner of the project.


Clojars 的維護者是不太贊同開發者移除自己的套件的,我曾經要求 clojar 幫我移除一個以前誤上傳的套件 (請見 issue/391),也是花了好一段時間這個套件才真的被刪除,因此在 Clojure 世界中我們應該不太需要擔心有使用者下架套件這件事 :S。