2015-07-31 14 views
6

Tôi đang cố viết macro ELisp để tạo nhiều hàm dựa trên một số dữ liệu phổ biến. Ví dụ, khi tôi muốn để tính toán các tên fn tôi viết một cái gì đó tương tự (tôi bỏ qua vệ sinh cho thời điểm này, tôi đi qua một biểu tượng văn chương vào vĩ mô để đánh giá không phải vấn đề):Không thể gọi các hàm được xác định bằng macro với tên được tạo bởi biểu tượng thực hiện

(cl-defmacro def-fns (sym) 
    "SYM." 
    (let ((s1 (make-symbol (concat (symbol-name sym) "-1"))) 
     (s2 (make-symbol (concat (symbol-name sym) "-2")))) 
    `(progn (defun ,s1() (+ 1 2 3)) 
      (defun ,s2() "six")))) 

mà tôi mong muốn tạo ra 2 fns khi được gọi, được gọi là foo-1foo-2.

Sau đó tôi sẽ có thể gọi vĩ mô và FNS như vậy:

(def-fns foo) 
(foo-1) 
;; => 6 
(foo-2) 
;; -> "six 

Ngay cả những macroexpansion của (def-fns foo) trong Emacs gợi ý rằng đây phải là trường hợp:

(progn 
    (defun foo-1 nil (+ 1 2 3)) 
    (defun foo-2 nil "six")) 

Tuy nhiên, khi Tôi đánh giá định nghĩa def-fns và gọi nó là không tạo các chức năng đó. Tại sao điều này là trường hợp? Kỹ thuật này hoạt động trong Common Lisp và trong Clojure (có hệ thống macro rất giống nhau), vậy tại sao không trong ELisp?

Trả lời

9

Mã của bạn sẽ không hoạt động trong CL.

Vấn đề là với make-symbol - nó tạo ra một biểu tượng mới, vì vậy mà

(eq (make-symbol "A") (make-symbol "A")) 
==> nil 

Điều này có nghĩa rằng macro của bạn tạo ra các chức năng nhưng liên kết với họ để những biểu tượng mà bạn không còn có một xử lý trên.

Khi bạn đánh giá (foo-1), Emacs Lisp đọc cố gắng để tìm ra chức năng liên kết của các thực tập nội trú biểu tượng foo-1, không phải là biểu tượng uninterned tươi macro của bạn tạo ra.

Bạn cần phải sử dụng intern thay vì: nó làm cho các biểu tượng "thường có sẵn", có thể nói:

(eq (intern "a") (intern "a)) 
==> t 

Vì vậy, các mã sửa trông như thế này:

(defmacro def-fns (sym) 
    "SYM." 
    (let ((s1 (intern (concat (symbol-name sym) "-1"))) 
     (s2 (intern (concat (symbol-name sym) "-2")))) 
    `(progn (defun ,s1() (+ 1 2 3)) 
      (defun ,s2() "six")))) 
(def-fns foo) 
(foo-1) 
==> 6 
(foo-2) 
==> "six" 

Ghi chú :

  1. Nếu bạn đang sử dụng CL, biểu tượng không được kiểm duyệt s sẽ được in dưới dạng #:foo-1 và nguồn gốc của vấn đề của bạn sẽ rõ ràng đối với bạn.
  2. Đó là cực kỳ hiếm khi bạn thực sự cần sử dụng make-symbol. Thông thường, bạn muốn sử dụng hoặc intern hoặc gensym.
+0

Thật vậy. Nó đã được một thời gian kể từ khi tôi đã viết bất kỳ CL/Clojure/Elisp phong cách macro và đó là một sai lầm dễ dàng để thực hiện, rõ ràng dễ dàng hơn trong Elisp hơn trong CL :) – jjpe

Các vấn đề liên quan