【问题标题】:How to return dereferenced value as a result of a function in Clojure如何作为 Clojure 中函数的结果返回取消引用的值
【发布时间】:2016-12-05 20:32:54
【问题描述】:

我有一个带有向量值的原子:

(def v (atom [1 2]))

我想编写一个函数来返回这个取消引用的原子。也就是说,我基本上是想写一个函数,它会返回和这个一样的结果:

=> @v
[1 2]

我的第一个想法是这样的:

=> (let [f #(@v)]
     (println (f)))
ArityException Wrong number of args (0) passed to: PersistentVector  clojure.lang.AFn.throwArity (AFn.java:429)

但这当然不起作用,因为取消引用的原子不是函数:

=> (@v)
ArityException Wrong number of args (0) passed to: PersistentVector  clojure.lang.AFn.throwArity (AFn.java:429)

我最终得到的解决方法是:

=> (let [f #(vec @v)]
     (println (f)))
[1 2]
nil

...但肯定有更好的方法来做到这一点,而不必假设 atom 的值是向量?

【问题讨论】:

  • (defn deref-v [] (deref v))
  • 这只是deref 函数。我不知道为什么人们将它作为 asnwer 包装在一个函数中,但无论如何,你在 cmets 中提到这是一个简化的问题。我认为你简化了太多,所以也许编辑并给我们更多细节?正如您目前所提出的问题,deref 的功能。如果你只使用deref,你根本不会污染你的命名空间,你也可以使用@阅读器宏快速deref
  • 不仅仅是deref 函数!它并没有取消它的论点,而是v。 FWIW,你也可以写:#(-> @v)
  • @MasterMastic 不,这是不正确的。正如 ClojureMostly 所说,deref等同于 (fn [] (deref v))
  • 哦,我明白了,我认为它比这更笼统(尽管您在 cmets 中也提到您希望能够将不同的原子传递给它?),但我仍然认为您应该更具体。

标签: function clojure functional-programming


【解决方案1】:

现有的答案都没有给出两个最简单、最简洁的解决方案,所以他们在这里。作为@Stefan 的stated,您需要与此功能等效的东西:

(fn [] (deref v))

但是,deref 的这种用法正是 @ 阅读器宏旨在简化的用例:

(fn [] @v)

或者,由于(deref v) 是一个函数调用,您可以改用#() 读取器宏:

#(deref v)

这里的要点是,在这种情况下,您不能同时使用 @#() together。但这并不意味着您不能或不应该使用其中一个

【讨论】:

    【解决方案2】:

    有时最好将#@ 之类的阅读器结构留在外面,只使用函数(或宏和特殊形式):

    user> (def v (atom [1 2]))
    #'user/v
    user> (let [f (fn [] (deref v))] (f))
    [1 2]
    

    我认为,您的示例是您真正想做的非常简化的版本。如果没有,deref 可能正是您正在寻找的功能。

    【讨论】:

    • (deref @v) 而不是@v,太棒了!我知道必须有一个简单的解决方案。 :) 是的,我花了大约一周左右的时间来找出我真正的问题是什么并想出了简化的例子。
    • (derev v) 不是(derev @v)@ 只是由 /reader/ 执行的 deref 的语法糖。请参阅clojure.org/reference/reader 上的“宏字符”部分
    • 你是对的,我在评论中打错了。感谢您的回答,特别是提醒我,有时最好省略 # 和 @ 并自己使用函数,因为这正是正确的情况。
    【解决方案3】:

    我想写一个函数来返回这个取消引用的原子

    (def v (atom [1 2]))
    (defn foo [] @v)
    (foo) ;;=> [1 2]
    

    【讨论】:

    • 是的!我想使用匿名函数,这样我就不会用只调用一次的函数污染我的命名空间,但你的回答让我想到了简单地使用扩展形式的匿名函数:(let [f (fn [] @v) ] (prn (f)))
    • @Domchi (let [f (fn [] @v)] (prn (f))) 实际上与 (prn @v) 相同 - 为什么是函数?
    • 这是一个真实用例的简化示例。我的用例是我实际上有一个函数可以对原子执行某些操作(在 go-loop 中),我通过传递不同的原子在不同的地方使用它。
    【解决方案4】:

    首先:这个错误的原因,是lambda表达式糖语法的扩展。你可以这样想:

    #(expression*) => (fn [] (expression*))
    

    所以,这样想:

    #(@v) => (fn [] (@v))@v 依次扩展为 (deref v)(通过宏扩展很容易看到),这会将您带到这种形式:

    (fn [] ((deref v)))

    事实上你对错误的假设原因有一半是正确的:取消引用的结果被作为一个函数调用,但实际上它可能是(因为向量具有函数语义),而它只是缺少一个强制参数(正是错误文本中所说的)。 所以它应该适用于此:#(@v 0) => (fn [] ((deref v) 0))

    user> (let [f #(@v 0)]
            (println (f)))
    1
    nil
    

    我个人只会使用部分应用程序,而不是 lambda 表达式来解决这个问题:

    user> (let [f (partial deref v)]
            (println (f)))
    [1 2]
    nil
    

    【讨论】:

    • 我的立场是正确的 - 这实际上很酷,当然它期望向量中的位置作为参数。感谢您的解释和部分想法;看到其他人如何看待同样的问题,我总是很惊讶。
    猜你喜欢
    • 2015-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-09
    • 2023-04-03
    • 1970-01-01
    • 2012-01-01
    • 2017-11-25
    相关资源
    最近更新 更多