【问题标题】:Storing a destructuring map for later use存储解构映射以供以后使用
【发布时间】:2014-05-14 02:10:20
【问题描述】:

我一直试图让它与报价、报价拼接、评估以及我能想到的任何其他东西一起使用,但到目前为止还没有运气。我理解它为什么不起作用 - 它被视为一张地图,它正在尝试评估 abc - 只是不知道如何绕过它。

(def destructor {a :a b :b c :c})
; CompilerException java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(:1:15)

(let [destructor my-map]
  'etc)

我有一个相当复杂的解构地图,我正在考虑多次使用它,所以将它藏在某个地方似乎是个好主意。也许有更好的方法来解决它?

【问题讨论】:

    标签: map clojure destructuring


    【解决方案1】:

    好主意,但你不能这样做,因为你想要存储的东西并不是真正的值,所以你不能将它存储在 var 中。

    相反,您可以定义一个在其扩展中包含此内容的宏:

    (defmacro with-abc [abc & body]
      `(let [~'{:keys [a b c]} ~abc]
         ~@body))
    
    (with-abc foo
      (...use a, b, and c...))
    

    【讨论】:

    • let 子句中看起来很有趣的~' 使得a、b 和c 不会通过语法引用获得命名空间限定。我认为这是必要的,但我现在不在一台真正的电脑前,所以如果有人看到它,请不要随意编辑。
    • 非常好。我不能使用:keys(虽然很高兴将其视为示例),因为我有“一个相当复杂的解构地图”(即多个级别),但在我的地图中交换效果很好。我玩了一段时间使用宏,但是——尽管已经成功使用它们很多次——我想我仍然没有完全考虑将所有东西都传入并得到一个全新的、完全改变的东西。谢谢!
    • @amalloy:作为记录,你是对的——~' 是必要的。我对其进行了测试,它按原样工作,但取出~' 会导致java.lang.RuntimeException: Can't let qualified name: user/a
    【解决方案2】:

    类似@amalloy 的回答也是我的第一直觉,这可能是要走的路。也就是说,可能值得考虑一个普通的高阶函数:

    (defn destruct
      [{a :a b :b c :c} f]
      (f a b c))
    
    (destruct my-map
              (fn [a b c]
                (println a)
                (println b)
                (println c)))
    

    这有点吵,而且您每次都不得不为绑定命名,但您可以避免潜在的卫生问题,并且根据您对元编程的熟悉程度,组合起来会更容易一些。

    【讨论】:

    • 是的,我想避免命名事物,因为我的地图不仅包含几十个事物,而且它们还处于几个不同的嵌套级别。不过,在可能的情况下,尝试找到功能路线是件好事。谢谢。
    • 明白了。听起来宏是要走的路。
    【解决方案3】:

    你需要引用破坏模式

    (def my-destructor '{a :a b :b c :c})
    

    您可以通过引用/取消引用的级别来做到这一点,但使用一些辅助函数更容易看到。

    (defn- with-destructor* [binding & body] 
      `(let ~binding ~@body))
    

    这个eval 出现在“编译”时(在宏扩展期间)

    (defmacro with-destructor [[destructor form] & body] 
      (eval `(with-destructor* [~destructor '~form] '~@body)))
    

    如图

    (macroexpand-1 '(with-destructor [my-destructor {:a 1 :c 3}] (+ a c)))
    ;=> (clojure.core/let [{a :a, b :b, c :c} {:a 1, :c 3}] (+ a c))
    

    结果

    (with-destructor [my-destructor {:a 1 :c 3}] (+ a c))
    ;=> 4
    

    【讨论】:

    • with-destructor的定义完全没有理由使用eval。它的主体应该只是(apply with-destructor* [destructor form] body)。当然,如果你从with-destructor* 中删除可变参数,你可以避免apply(在我看来,你应该这样做)。
    • @amalloy 也许我遗漏了一些东西,但我相信您的建议仅适用于 (with-destructor [{a :a b :b c :c} {:a 1 :c 3}] (+ a c)) 中的文字析构函数表达式,但不能像问题中那样存储在 var 中。跨度>
    • 我明白了。您试图让destructor 由用户提供,而不是针对特定宏固定。我很困惑,因为您将宏参数命名为与 var 相同,并且我认为宏指的是 var。
    猜你喜欢
    • 2012-06-26
    • 1970-01-01
    • 1970-01-01
    • 2017-11-28
    • 1970-01-01
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多