【问题标题】:Translating macro from Common Lisp to Clojure将宏从 Common Lisp 翻译成 Clojure
【发布时间】:2016-12-04 00:17:39
【问题描述】:

我正在尝试将 the original "Calendrical Calculations" paper by Derhowitz and Reingold 中的 Common Lisp 代码翻译成 Clojure。在本文中,有一个名为 sum 的宏,在 Common Lisp 中给出为

(defmacro sum (expression index initial condition)
;; sum expession for index = initial and successive integers,
;; as long as condition holds.
 (let* ((temp (gensym)))
   `(do ((,temp 0 (+ ,temp ,expression))
         (,index ,initial (i+ ,index))) 
        ((not ,condition) ,temp))))

我已经将其翻译成 Clojure

(defmacro sum [expression index initial condition]
  ; sum expession for index = initial and successive integers,
  ; as long as condition holds.
  (let [temp (gensym)]
    `(do ((~temp 0 (+ ~temp ~expression))
          (~index ~initial (inc ~index))) 
         ((not ~condition) ~temp))))

但是,当我在一个函数中使用上面的(例如,下面)

(defn absolute-from-gregorian [date]
  ;; Absolute date equivalent to the Gregorian date.
  (let [month (extract-month date) 
        year  (extract-year date)]
    ; Return
    (+ (extract-day date)           ;; Days so far this month.
       (sum                         ;; Days in prior months this year.
         (last-day-of-gregorian-month m year) m 1 (< m month))
       (* 365 (dec year))           ;; Days in prior years.
       (quotient (dec year) 4)      ;; Julian leap days in prior years...
       (-                           ;; ... minus prior century years...
         (quotient (dec year) 100))
       (quotient                    ;; ...plus prior years divisible...
         (dec year) 400))))         ;; ... by 400.

我收到类似于以下内容的错误:

CompilerException java.lang.RuntimeException: Unable to resolve
  symbol: G__53 in this context, compiling:(NO_SOURCE_PATH:165:8) 

我几乎完全不熟悉 Lisp/Clojure 宏系统,但据我所知,let 块中的 gensym 调用创建了一个符号(在本例中为 G__53)正在调用,但它不存在。 是有道理的,但我怀疑这不是原始代码的意图 - 但是,我对 Common Lisp 了解不多,因此我无法弄清楚 A) 原始 CL代码试图在这里做,以及 B) 如何生成与此等效的 Clojure。

对于它的价值 - absolute-from-gregorian 中使用的其他函数的 Clojure 版本是:

(defn quotient [m n]
  (int (Math/floor (/ m n))))

(defn extract-month [date]
  (first date))

(defn extract-day [date]
  (second date))

(defn extract-year [date]
  (second (rest date)))

非常感谢任何和所有关于如何使这项工作的指针。

【问题讨论】:

    标签: clojure macros common-lisp


    【解决方案1】:

    do 在 common lisp 中的行为与 clojure 中的 do 不同。您必须查看 do 的实际作用(在普通 lisp 中它是一个循环结构,在 clojure 中是一个分组结构),并弄清楚如何完成同样的事情。

    【讨论】:

      【解决方案2】:

      这可以用 Clojure 中的 loop 构造表示:

      (defmacro sum [expression index initial condition]
        ; sum expession for index = initial and successive integers,
        ; as long as condition holds.
        `(loop [~index ~initial temp# 0]
           (if ~condition 
             (recur (inc ~index) (+ temp# ~expression))
             temp#)))
      

      例子:

      (sum (* x x) x 1 (<= x 3)) ;; => 14
      

      还请注意,使用 Clojure 的标准构造代替自定义宏可能就足够了。例如,代码中带有日历计算的行可以重写为:

      (reduce + (map #(last-day-of-gregorian-month % year) (range 1 month)))
      

      【讨论】:

      • 非常感谢。在发布我的问题后,我继续研究这个问题并确定使用recur 的循环是合适的,但对 Clojure/Lisp 宏了解不多,我还没有达到弄清楚如何将它们组合在一起的地步.看来我得接听the book on Clojure macros 并花一些时间了解更多关于它们的信息。
      猜你喜欢
      • 2014-02-20
      • 1970-01-01
      • 1970-01-01
      • 2010-12-15
      • 2015-06-04
      • 2018-09-06
      • 1970-01-01
      • 2020-09-18
      • 2016-02-17
      相关资源
      最近更新 更多