Clojure Metaprogramming问题(适合初学者!)

时间:2010-07-06 03:02:04

标签: clojure

所有,我开始看看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

感谢您提供的任何帮助!

3 个答案:

答案 0 :(得分:17)

我将在要点中回答,因为这些问题可以整齐地分成若干单独的问题。

  • 隐含在后续内容中的东西,但也许保证了它自己的子弹:由def&amp;创建的顶级对象。公司(特别是defn)是Vars。所以你真正想做的是别名 Var ;函数只是常规值,它们实际上没有名称(除非它们可能在其身体内部本地绑定了一个名称;但这与手头的问题无关)。

  • 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)的优点是它复制了元数据(例如docstring);它甚至似乎与当前HEAD中的宏一起使用,尽管我记得在早期版本中出现了一个阻止它的错误。

  • Vars以符号命名,而非关键字。 Clojure(和其他Lisps)中的符号文字不以冒号开头(:foo是关键字,而不是符号)。因此,要定义一个名为foo的函数,您应该编写

    (defn foo [...] ...)
    
  • defn是一个辅助宏,通过允许程序员使用def&amp;的混合,轻松创建 new 函数保持Vars。 fn语法。所以defn对于创建具有预先存在的值(可能是函数)的Vars是不可能的,这是创建别名所必需的;请改为使用defalias或简称def

  • 要创建可变参数函数,请使用以下语法:

    (fn [x y & args] ...)
    

    xy将是必需的位置参数;传递给函数的其余参数(任意数量的参数)将被收集到seq中,并以名称args提供。如果不需要,您不必指定任何“必需的位置参数”:(fn [& args] ...)

    要创建包含可变参数函数的Var,请使用

    (defn foo [x y & args] ...)
    
  • 要将函数应用于某些参数,您已将其组合成一个seqable对象(例如上面示例中的args seq或者vector&amp; c。),请使用{{1 }}:

    apply
  • 如果您想编写函数来创建别名 - 而不是宏 - 您需要调查函数(defn all? [& args] (apply every? args)) ,{{1} },intern - 可能with-meta / meta,具体取决于函数是接受符号还是Vars。我将把细节作为练习留给读者。 : - )

答案 1 :(得分:6)

您需要做的就是绑定每一个?功能全部?符号,通过def完成:

(def all? every?)

有关详情,请参阅Clojure macro to create a synonym for a function

答案 2 :(得分:3)

不要认为我可以在这里添加很多现有的解释,除非在Ruby旅行者的字典中填写关于参数收集和解构的几个空白:

(defn foo [& args]                 ; Ruby: def foo(*args)
  (println args))    
user=> (foo 1 2 3)
(1 2 3)

(defn foo [& args] 
  (+ args))   
user=> (foo 1 2 3)
java.lang.ClassCastException       ; + takes numbers, not a list

(defn foo [& args] 
  (apply + args))                  ; apply: as Ruby proc.call(*args)
user=> (foo 1 2 3)
6

(defn foo [& args]
  (let [[a b & other] args]        ; Ruby: a, b, *other = args
    (println a b other)))
user=> (foo 1 2 3)
1 2 (3)