Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
clojure宏如何解析特殊符号?_Clojure_Macros_Syntax Rules - Fatal编程技术网

clojure宏如何解析特殊符号?

clojure宏如何解析特殊符号?,clojure,macros,syntax-rules,Clojure,Macros,Syntax Rules,当我用Clojure重新实现用Scheme编写的宏时,我遇到了麻烦。 宏尝试将成对的测试数据加载到all testsvar中,以供以后使用 因为宏的参数是可变长度的,并且包含特殊的未定义符号,即=>,所以我不知道如何像Scheme语法规则那样解析它 方案版本: (define all-tests '()) ;;; load tests into all-tests (define-syntax add-tests-with-string-output (syntax-rules (=>

当我用Clojure重新实现用Scheme编写的宏时,我遇到了麻烦。 宏尝试将成对的测试数据加载到
all tests
var中,以供以后使用

因为宏的参数是可变长度的,并且包含特殊的未定义符号,即
=>
,所以我不知道如何像Scheme语法规则那样解析它

方案版本:

(define all-tests '())

;;; load tests into all-tests
(define-syntax add-tests-with-string-output
  (syntax-rules (=>)
    [(_ test-name [expr => output-string] ...)
     (set! all-tests
        (cons 
           '(test-name [expr string  output-string] ...)
            all-tests))]))

(add-tests-with-string-output "integers"      
  [0  => "0\n"]                    
  [1  => "1\n"]                    
  [-1 => "-1\n"]                   
  [10  => "10\n"]                    
  [-10 => "-10\n"]                   
  [2736 => "2736\n"]               
  [-2736 => "-2736\n"]             
  [536870911 => "536870911\n"]     
  [-536870912 => "-536870912\n"]   
)
(def all-tests (atom '()))

(defmacro add-tests-with-string-output
  [test-name & body]
  `(loop [bds# (list body)]
    (when-not (empty? bds#)
      (println (first bds#))
      (recur (rest bds#)))))
我当前未成功的Clojure版本:

(define all-tests '())

;;; load tests into all-tests
(define-syntax add-tests-with-string-output
  (syntax-rules (=>)
    [(_ test-name [expr => output-string] ...)
     (set! all-tests
        (cons 
           '(test-name [expr string  output-string] ...)
            all-tests))]))

(add-tests-with-string-output "integers"      
  [0  => "0\n"]                    
  [1  => "1\n"]                    
  [-1 => "-1\n"]                   
  [10  => "10\n"]                    
  [-10 => "-10\n"]                   
  [2736 => "2736\n"]               
  [-2736 => "-2736\n"]             
  [536870911 => "536870911\n"]     
  [-536870912 => "-536870912\n"]   
)
(def all-tests (atom '()))

(defmacro add-tests-with-string-output
  [test-name & body]
  `(loop [bds# (list body)]
    (when-not (empty? bds#)
      (println (first bds#))
      (recur (rest bds#)))))

Ps:我现在正在使用
println
测试我的代码。当它工作时,我将尝试进行解析和加载工作。

经过尝试和错误,最后我找到了解决方法

首先使用解构处理可变长度的参数; 以后不要在宏中使用语法Quoting,即backquote`,因为如果是这样,一旦需要取消引用
~
参数,即
正文
,您将收到类似这样的错误消息,因为特殊符号
=>

CompilerException java.lang.RuntimeException:无法解析 符号:=>在此上下文中

下面是我的解决方案。 如果你有更好的,或者你知道语法引号和Unquote出错的原因,请告诉我

;;; load tests into all-tests
(def all-tests (atom '()))
(defmacro add-tests-with-string-output
  [test-name & body]
  (loop [bds body, tests '()]
    (if (empty? bds)
      (do 
        (swap! all-tests #(cons (cons test-name tests) %))
        nil)
      (let [pair (first bds),
            input (first pair)
            output (last pair)]
        (recur (rest bds) (cons (list input ''string output) tests))))))

第一个宏形成一个循环,第二个宏形成一个
doseq
(因此更简单)。两者的行为应该相同。我还发现,从宏中提取尽可能多的逻辑到辅助函数是一个好主意。函数更易于调试、测试和编写。如果宏稍微复杂一点,我可能会在其中留下更少的逻辑

(def all-tests (atom '()))

(defn add-test [test-name expr output-string]
  (swap! all-tests #(cons (list test-name [expr output-string]) %)))

(defmacro add-tests-with-string-output
  [test-name & body]
  ;`(loop [bds# '(~@body)]
  `(loop [bds# '~body] ; edit
    (when-not (empty? bds#)
      (let [bd# (first bds#)
            expr# (first bd#)
            output-string# (last bd#)]
        (add-test ~test-name expr# output-string#)
        (recur (rest bds#))
        ))))

(defmacro add-tests-with-string-output2
  [test-name & body]
  ;`(doseq [bd# '(~@body)]
  `(doseq [bd# '~body] ; edit
    (let [expr# (first bd#)
          output-string# (last bd#)]
      (add-test ~test-name expr# output-string#))))

user=> (add-tests-with-string-output "test1" [0  => "0\n"] [1  => "1\n"])
nil
user=> (add-tests-with-string-output2 "test2" [0  => "0\n"] [1  => "1\n"])
nil
user=> @all-tests
(("test2" [1 "1\n"]) ("test2" [0 "0\n"]) ("test1" [1 "1\n"]) ("test1" [0 "0\n"]))

defmacro
defn
类似,因为它定义了一个函数,但它在编译时运行(据我所知),并保留返回值作为
defmacro
调用的替换。因此,一个问题是您定义的宏运行
swap在编译时(在repl中仍然有效)。您要做的是让
defmacro
返回要在运行时运行的实际代码。在这种情况下,宏只返回
nil
,因此在运行时可能没有任何行为。由于宏过早地评估输入代码/数据,因此出现
无法解决符号
错误。您可能在解构时执行了类似于
`~args
的操作。您只需使用一个引号即可解决此问题:
`~args
。这将扩展到
”(arg1 arg2 arg3)
(注意引用)。@DominykasMostauskis我找到了下面关于语法
“~args
的帖子,仍然很困惑,但你的解释对我更有用<表达式
(defmacro m[&args]`args)
中的code>“~
”执行以下操作:unquote(
~
)将宏体中的
args
替换为参数分解中的
args
值。给定
(m123)
,该值将是
(123)
。这里有一个陷阱,如果你就这样离开它,这不是一个列表,而是一个函数调用,你会得到一个错误。因此,输入引号(
);有了它(
”~args
),表达式
(m123)
被扩展为
”(1233)
,这是一个符号列表。类似于
[1=>“1\n”]
的东西也是如此:您必须引用它,否则您的“sugar”符号将被解释为var。