3

如何使clojure'()算作nil

例如:如何制作类似的东西

(if '() :true :false) 

;to be 

:false

;Or easier 

(my-fun/macro/namespace/... (if '() :true :false))

:false

而不仅仅是如果。各方面。

(= nil '()) or (my-something (= nil '()))

true

并且每个代码都被 (= '() nil) 保存。

(something (+ 1 (if (= nil '()) 1 2)))

2

我在想某种正则表达式。它将查看代码并替换'()nil,但有一些类似的东西(rest '(1))和许多其他的东西'(),我不知道如何处理它。

有人告诉我,宏允许您构建自己的语言。我想通过更改clojure来尝试它。所以这主要是关于“clojure 如何工作以及如何改变它?” 而不是“我的工作真的需要它”。

谢谢你的帮助。

4

4 回答 4

11

'()只是不是一回事nil——你为什么想要它呢?

您可能正在寻找的是该seq函数,如果给定一个空集合,它将返回 nil:

(seq [1 2 3])
=> (1 2 3)

(seq [])
=> nil

(seq '())
=> nil

seq因此通常用于测试“空虚”,成语如下:

(if (seq coll)
  (do-something-with coll)
  (get-empty-result))
于 2012-08-09T07:35:42.340 回答
2

您说您想使用宏更改 Clojure。目前,据我所知,这不是你可以用“常规”宏系统做的事情(术语修复任何人?)。你真正需要的(我认为)是一个阅读器宏。我在网上看到的东西(例如这里)似乎说在 Clojure 1.4 中存在类似阅读器宏之类的东西——但我对此并不熟悉,因为我真的很喜欢使用clooj作为我的 IDE,而且它目前没有使用 Clojure 1.4. 也许其他人对这种“可扩展阅读器”魔术有更好的信息。

无论如何,我不太喜欢以这种方式更改语言的想法,而且我认为有一个潜在的非常好的替代方案:即 Clojure 函数not-empty

此函数接受任何集合并按原样返回该集合,或者nil如果该集合为空则返回。这意味着在任何你想()返回的地方nil,你都应该把它包装起来not-empty。这个答案与上面 mikera 的答案非常相似,只是您不必集合转换为序列(这可能很好)。

在您拥有“手写”集合的情况下,两者都使用seq并且非常愚蠢。not-empty毕竟,如果您是手写(或者更确切地说是手动输入),那么您将确定它是否为空。这很有用的情况是当您有一个返回集合的表达式或符号,并且您不知道返回的集合是否为空时。

例子:

=> (if-let [c (not-empty (take (rand-int 5) [:a :b :c :d]))]
     (println c)
     (println "Twas empty"))
;//80% of the time, this will print some non-empty sub-list of [:a :b :c :d]
;//The other 20% of the time, this will return...
Twas empty
=> nil
于 2012-08-09T13:13:38.193 回答
1

怎么样empty??这是最有表现力的。

(if (empty? '())
  :true
  :false)
于 2012-08-09T08:42:21.970 回答
1

您可以覆盖宏和函数。例如:

(defn classic-lisp [arg]
  (if (seq? arg) (seq arg) arg))

(defn = [& args]
  (apply clojure.core/= (map classic-lisp args)))

(defmacro when [cond & args] 
  `(when (classic-lisp ~cond) ~@args))

不幸的是,您不能覆盖 if,因为它是一种特殊形式而不是宏。您将不得不用另一个宏包装您的代码。

让我们将 if* 宏设为具有 common-lisp 行为的 if:

(defmacro if* [cond & args]
    `(if (classic-lisp ~cond) ~@args)

有了这个,我们可以用 if*s 替换所有的 if:

(use 'clojure.walk)
(defn replace-ifs [code]
  (postwalk-replace '{if if*} (macroexpand-all code)))
(defmacro clojure-the-old-way [& body]
  `(do ~@(map replace-ifs body)))

现在:

=> (clojure-the-old-way (if '() :true :false) )
:false

您也应该能够加载文件并替换其中的 ifs:

(defn read-clj-file [filename]
  ;; loads list of clojure expressions from file *filename*
  (read-string (str "(" (slurp filename)  ")"))) 

(defn load-clj-file-the-old-way [filename]
  (doseq [line (replace-ifs (read-clj-file filename))] (eval line))

请注意,我没有测试加载文件的代码,它可能与 leiningen 或命名空间不兼容。我相信它应该与 overriden = 一起使用。

于 2012-08-10T16:40:39.707 回答