Clojure 元编程问题(针对初学者!)

标签 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

感谢您为我提供的任何帮助!

最佳答案

我将用要点来回答,因为这些问题可以整齐地分成许多单独的问题。

  • 隐式包含在接下来的内容中的东西,但也许值得一提:由 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] ...)
    

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

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

    (defn foo [x y & args] ...)
    
  • 要将函数应用于已组装成可序列对象的某些参数(例如上面示例中的 args seq 或向量 &c。),请使用 申请:

    (defn all? [& args]
      (apply every? args))
    
  • 如果您想编写函数来创建别名(而不是宏),您需要研究函数 internwith-metameta - 可能还有 resolve/ns-resolve,具体取决于函数是否接受符号或变量。我将把填写详细信息作为练习留给读者。 :-)

关于Clojure 元编程问题(针对初学者!),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3183256/

相关文章:

java - 从外部 clojar 导入/使用资源

Clojure - 不使用 next 来索引序列

macros - 如何使用 `clojure.tools.macro/name-with-attributes` ?

networking - Clojure (aleph) 检测服务器何时断开连接

file - 使 Clojure 识别并隔离文件中的行

list - 如何从 Clojure 列表中删除重复项?

java - 尝试使用java和clojure在eclipse中创建maven动态Web项目

filter - Clojure:将过滤器与具有多个参数的函数一起使用

node.js - ClojureScript (nodejs) 返回文件字符串内容的函数

clojure - 如何在 clojure 中处理 java 可变长度参数?