请分享您对该2024 Clojure 状态调查的看法!

欢迎!请查看关于页面以了解更多关于该网站的信息。

0
语法和读取器

我开始学习 Clojure。教程要求定义 constantly。我使用 fn 成功做到了。如何使用 #() 来实现呢?

2 条答案

0

编辑
 
最佳答案

[由于答案中缺少 # 被编辑]

这种方法不太妥当,因为 constantly 旨在允许任意数量的参数,并总是返回一个常量。#(...) 匿名函数简写提供了一种快速定义匿名函数的方法,该函数适用于各种归约(arity)

0 - #(vector)
1- #(vector %1)
2 - #(vector %1 %2)
...
n - #(vector %1 %2 ... %n)
& args - #(apply vector %&)

然而,由于您在定义函数体,这会有一些限制...如果您尝试定义类似 identity 的内容,它最终会看起来像一个调用...

#(%) ;; 这将尝试将单个参数视为一元函数进行调用。

可以通过将输入绑定到 let 或其他形式来解决此问题,以传达归约...

#(let [v %] v)

或者

#(do %)

将像 identity 一样工作,我们只需要在体中返回它。

但我们想定义一个可变参数的函数,因此我们想用 %& 来指示参数。然而,对于结果来说,参数是没有意义的...它们唯一传达给匿名函数糖的只是参数的数量或归约。

虽然有些尴尬,但这是其中一种方法

(defn constantly2 [v]
;;ignore the arguments, but communicate a varargs function
   #(let [_ %&]  
          v))
user=> (def f (constantly2 10))
#'user/f
user=> (f 1 2 3 4)
10
user=> (f 1)
10
user=> (f 5)
10
user=> (f :a)
10

我可能不会在这些事物上使用#(..)语法糖。这种简写对于非常简单的内联匿名函数很有用,即使在那时,我也通常更喜欢明确写出fn

谢谢。我觉得这可能会有些难懂。我不明白为什么 #(%) 被视为函数调用。读者会先看到 (%) 并尝试在解析 # 之前对其进行评估吗?
很确定的是 [FnReader](https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LispReader.java#L861),它映射到 \( 字符的派发读取器,会逐字读取形式并构造一个要评估的 `fn` 形式。
  
    用户=> (read-string "#(+ % 2)")
    (fn* [p1__3#] (+ p1__3# 2))
    用户=> (read-string "#(%)")
    (fn* [p1__6#] (p1__6#))
感谢read-string函数。之前不知道这个。看起来 #() 扩展到fn模板时,额外多了一个(),这使得 #() 中的形式可以评估。
总的来说,我看到了以下情况(转换为clojure,缺失一些验证内容)


(require '[clojure.walk :as w])
(require '[clojure.string :as s])

(def the-form '(+ 1 (* % 3)))

(def anons #{"%" "%&"})
(def multi #"%[0-9]*")
(defn infer-args [form]
  (->> form
       (tree-seq coll? seq)
       (filter
        #(and (symbol? %)
                    ))
                  重新查找多名称(name%)
                  %))))

(defn derive-function [form]
  (let [args       (infer-args form)
        args->syms (zipmap args (repeatedly #(gensym "arg")))
        new-args   (vals args->syms)]
    `(fn [~@new-args] ~(w/postwalk-replace args->syms form))))


因此,读取器提供了一个Clojure形式,`(+ 1 (* % 3))`,由the-form定义。 此形式被推断为`fn`调用的"主体",参数由任何前缀为%的符号的存在暗示。 因此,我们将主体形式传递给一个函数,该函数提取它以确定参数,然后发射一个带有推断参数和原始主体的`fn`形式,其中原始参数已被gensym'd替换。 由于分发函数与`(`关联,我们总是必须传递一个可读的sexpr作为函数主体。 因此,没有直接使用现有语法传递返回简单值的函数的方法。
+1
by

使用`#()`快捷方式定义`constantly`。让我们假设我想创建一个总是返回`1`的函数。

我可以这样做:

(constantly 1)

但要使用`#()`:

#(do %& 1)

`%&`是必须的,以确保它适用于任何参数数量。但我们想忽略参数。《do》返回最后一个表达式的值,所以在这种情况下,它将返回`1`。

...