习惯用Clojure相当于这个Python代码?

Jor*_*ord 3 python clojure

我在Python中编写了一个简单的基于堆栈的虚拟机,现在我正在尝试在Clojure中重写它,由于我没有太多使用Lisp的经验,这证明很难.这个Python片段处理字节码,它表示为元组列表,如下所示:

[("label", "entry"),
 ("load", 0),
 ("load", 1),
 ("add",),
 ("store", 0)]
Run Code Online (Sandbox Code Playgroud)

或者在Clojure中:

[[:label :entry]
 [:load 0]
 [:load 1]
 [:add]
 [:store 0]]
Run Code Online (Sandbox Code Playgroud)

当Function对象加载字节码时,每个"label"元组都会被专门处理以标记该位置,而其他每个元组都保留在最终的字节码中.我认为Clojure相当于这个函数会涉及折叠,但我不知道如何以优雅或惯用的方式做到这一点.有任何想法吗?

ama*_*loy 10

阅读Python片段,看起来您希望最终输出看起来像

{:code [[:load 0]
        [:load 1]
        [:add]
        [:store 0]]
 :labels {:entry 0}}
Run Code Online (Sandbox Code Playgroud)

一旦你对目标有了明确的描述,编写代码要容易得多,事实上这是一个非常简单的减少.有许多风格上不同的编写缩减器的方法,但对我来说,这种方式似乎最容易阅读.

(defn load [asm]
  (reduce (fn [{:keys [code labels]} [op arg1 & args :as instruction]]
            (if (= :label op)
              {:code code
               :labels (assoc labels arg1 (count code))}
              {:code (conj code instruction)
               :labels labels}))
          {:code [], :labels {}},
          asm))
Run Code Online (Sandbox Code Playgroud)

编辑

此版本支持name参数,并通过不重复不更改的元素来简化还原步骤.

(defn load [name asm]
  (reduce (fn [program [op arg1 :as instruction]]
            (if (= :label op)
              (assoc-in program [:labels arg1] (count (:code program)))
              (update-in program [:code] conj instruction)))
          {:code [], :labels {}, :name name},
          asm))
Run Code Online (Sandbox Code Playgroud)