【问题标题】:How to pass the rest args of a variadic function to another function?如何将可变参数函数的其余参数传递给另一个函数?
【发布时间】:2021-11-09 03:07:36
【问题描述】:

我想编写一个函数,它只是用新值更新地图中的向量,但可以接受任意数量的 args,但至少一个。

示例如下:

(defn my-update [what item & items]
  (update what :desired-key conj item items))

很遗憾,这不起作用。尽管update 确实有一个包含多个值的签名(如[m k f x y]),但my-update 的所有剩余参数将被连接到一个序列中,该序列将作为一个参数传递给conj

相反,将conjapply 包装在匿名函数中确实有效,但看起来并不那么优雅:

(defn my-update [what item & items]
  (update what :desired-key #(apply conj % item items))

编写像my-update这样的函数的惯用方式是什么?

【问题讨论】:

    标签: clojure idioms


    【解决方案1】:

    您只需在update 之前插入apply。这将调用函数update,并使用后面的参数,除了最后一个参数应该是一个序列,其元素成为调用中的剩余参数:

    (defn my-update [what item & items]
      (apply update what :desired-key conj item items))
    
    (my-update {:desired-key [0]} 1 2 3 4)
    ;; => {:desired-key [0 1 2 3 4]}
    
    (my-update {:desired-key [0]})
    ;; Exception: Wrong number of args (1) passed to: my-update
    

    这样,您可以保留函数参数列表[what item & items],明确需要提供至少一项。

    一般来说,调用(apply f a b c ... [x y z ...]) 的计算结果与(f a b c ... x y z ...) 相同。

    【讨论】:

    • @tosh 您可以调用(apply f a b c ... [x y z ...]),对于任何函数f 的选择,它的计算结果都与(f a b c ... x y z ...) 相同。
    • 我从来没有想过这个技巧。我仍然倾向于引入一个辅助函数,这样您就可以引入一个描述性的名称并避免在单个表单中过于复杂。
    【解决方案2】:

    您现有的解决方案还不错。一个小的改进是使用into 函数,它在内部使用conj 将两个序列连接在一起:

    (defn my-update [what & items]
      (update what :a into items))
    

    结果

    (my-update {:a [1]} 2 3 4) => {:a [1 2 3 4]}
    

    另一种选择是将匿名函数提取到命名函数中:

    (defn append-to-seq
      [seq item items]
      (-> (vec seq)  ; ensure it is a vector so conj adds to the end, not beginning
        (conj item)
        (into items)))
    
    (defn my-update [what item & items]
      (update what :a append-to-seq item items))
    
    
    

    【讨论】:

      猜你喜欢
      • 2018-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多