总而言之,我开始研究 Clojure 语言,并且对我正在尝试做的事情有几个问题。主要目标是将序列函数 every?
别名为 all?
。我确信有一个函数或宏可以执行别名(或类似的操作),但我想看看到目前为止我所知道的一些基本构造是否可能。我的方法是定义一个名为 all?
的函数,将其参数应用于 every?
实现。
我很好奇这是否可以变得不可知,所以我想参数化我的别名函数以采用两个参数,新名称(作为关键字)和旧名称(作为函数引用)。在实现这一目标的过程中,我遇到了两个问题。
1) 使用关键字定义命名函数会引发错误。显然它需要 clojure.lang.IObj
。
user=> (defn :foo "bar")
java.lang.ClassCastException: clojure.lang.Keyword cannot be cast to clojure.lang.IObj (NO_SOURCE_FILE:0)
是否有一个函数可以将关键字转换为 IObj,或者是否有其他方法可以使用某些提供的值来参数化新定义的函数的名称? (在 Ruby 中,define_method 以及其他技术可以实现此目的)
irb(main)> self.class.instance_eval do
irb(main)* define_method(:foo) { "bar" }
irb(main)> end
=> #<Proc>
irb(main)> foo
=> "bar"
2) 将函数的所有参数收集到单个变量中。即使像 (+ 1 2 3 4)
这样的基本函数也采用可变数量的参数。到目前为止,我见过的所有函数定义技术都采用特定数量的参数,无法将所有内容聚合到列表中以便在函数体中进行处理。再说一遍,我想要的事情是用 Ruby 完成的,如下所示:
irb(main)> def foo(*args)
irb(main)> p args
irb(main)> end
=> nil
irb(main)> foo(1, 2, 3)
[1, 2, 3]
=> nil
感谢您为我提供的任何帮助!
最佳答案
我将用要点来回答,因为这些问题可以整齐地分成许多单独的问题。
隐式包含在接下来的内容中的东西,但也许值得一提:由
def
& Co. 创建的顶级对象(特别是由defn
) 是变量。所以你真正想要做的是给 Var 起一个别名;函数只是没有真正名称的常规值(除非它们可能有一个在其体内本地绑定(bind)到自身的名称;但这与当前的问题无关)。Clojure 中确实有一个可用的“别名宏”——
clojure.contrib.def/defalias
:(use '[clojure.contrib.def :only [defalias]]) (defalias foo bar) ; => foo can now be used in place of bar
与
(def foo bar)
相比,它的优点是它复制元数据(例如文档字符串);它甚至似乎可以与当前 HEAD 中的宏一起使用,尽管我记得在早期版本中有一个错误阻止了这一点。变量由符号命名,而不是关键字。 Clojure(和其他 Lisp)中的符号文字不以冒号开头(
:foo
是关键字,而不是符号)。因此,要定义一个名为foo
的函数,您应该编写(defn foo [...] ...)
defn
是一个帮助宏,允许程序员混合使用def
,从而简化新函数保持变量的创建> &fn
语法。因此,用预先存在的值(可能是函数)创建 Var 是不可能的,正如创建别名所需要的那样;使用defalias
或简单地使用def
来代替。要创建可变参数函数,请使用以下语法:
(fn [x y & args] ...)
x
和y
将是必需的位置参数;传递给函数的其余参数(任意数量)将被收集到一个 seq 中,并在名称args
下可用。如果不需要,则不必指定任何“必需的位置参数”:(fn [& args] ...)
。要创建一个包含可变参数函数的 Var,请使用
(defn foo [x y & args] ...)
要将函数应用于已组装成可序列对象的某些参数(例如上面示例中的
args
seq 或向量 &c。),请使用申请
:(defn all? [& args] (apply every? args))
如果您想编写函数来创建别名(而不是宏),您需要研究函数
intern
、with-meta
、meta
- 可能还有resolve
/ns-resolve
,具体取决于函数是否接受符号或变量。我将把填写详细信息作为练习留给读者。 :-)
关于Clojure 元编程问题(针对初学者!),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3183256/