Đặt tên thay thế chuỗi?


13

Tôi thường phải thực hiện một số thay thế của cùng một chuỗi:

(format "%s %s %s" "a" "a" "a") ;; gives: "a a a"

(đây chỉ là một ví dụ giả, trong trường hợp này tốt hơn là dán "a" với khoảng trắng, nhưng nói chung tôi xử lý các tình huống phức tạp hơn)

Có cách nào để thay thế được đặt tên? Ví dụ trong python người ta sẽ viết:

"{0} {0} {0}".format("a") # or:
"{name} {name} {name}".format(name="a")


@Malabarba: Tôi đã đăng một bản sửa đổi của một số câu trả lời từ chủ đề đó ở đây như là một câu trả lời .
Adobe

Câu trả lời:


16

Viết lại câu trả lời này đưa ra một giải pháp khác:

(format-spec "%a %a %a %b %b %b" (format-spec-make ?a "a" ?b "b"))

Chỉnh sửa : Một format-specgiải pháp khác

Như Malabarba đưa ra một giải pháp khác trong các bình luận:

(format-spec "%a %a %a %b %b %b" '((?a . "a") (?b . "b")))

Chỉnh sửa 2 : Đánh giá trước khi thay thế:

Dưới đây là các ví dụ với đánh giá trước khi thay thế:

(let ( (a 1)
       (b 2) )
  (message (format-spec "a = %a; b = %b" (format-spec-make ?a a ?b b))) )
;; ⇒ a = 1; b = 1

(let ( (a 1)
       (b 2) )
  (message (format-spec "a = %a; b = %b" `((?a . ,a) (?b . ,b)))) )
;; ⇒ a = 1; b = 2

3
Cũng lưu ý rằng đó format-spec-makechỉ là một sự tồn tại:'((?a . "a") (?b . "b"))
Malabarba

1
"dường như không hoạt động cho các con số" - xem emacs.stackexchange.com/questions/7481/ mẹo
npostavs

@npostavs: Thật tuyệt khi biết! Tôi chỉnh sửa câu trả lời.
Adobe

14

Thư viện thao tác chuỗi của Magnar Sveen s.el cung cấp nhiều cách khác nhau để làm điều này. Ví dụ:

(require 's)
(s-format "${name} ${name} ${name}" 'aget '(("name" . "test")))
;; ==> "test test test"

Lưu ý rằng s-formatcó thể mất bất kỳ chức năng thay thế, nhưng cung cấp xử lý đặc biệt cho aget, eltgethash. Vì vậy, bạn có thể sử dụng danh sách mã thông báo và tham chiếu chúng theo chỉ mục, như vậy:

(s-format "$0 $0 $0 $1 $1 $1" 'elt '("a" "b"))
;; ==> "a a a b b b"

Bạn cũng có thể thay thế bằng cách sử dụng các biến trong phạm vi, như thế này:

(let ((name "test"))
  (s-lex-format "${name} ${name} ${name}"))
;; ==> "test test test"

1
Tuyệt vời, tôi không biết về tính năng này! Tôi đã sử dụng s.el hầu hết thời gian để xem qua cách thực hiện các tác vụ thao tác chuỗi phổ biến trong Emacs, nhưng đây thực sự không chỉ là một trình bao bọc một dòng của một hàm hiện có.
wasamasa

3

Định dạng s-lex của s.el thực sự là những gì bạn muốn, nhưng nếu bạn thực sự muốn có thể đặt mã bên trong các khối thay thế và không chỉ là tên biến, tôi đã viết nó như một bằng chứng về khái niệm.

(defmacro fmt (str)
  "Elisp string interpolation for any expression."
  (let ((exprs nil))
    (with-temp-buffer
      (insert str)
      (goto-char 1)
      (while (re-search-forward "#{" nil t 1)
        (let ((here (point))
              (emptyp (eql (char-after) ?})))
          (unless  emptyp (push (read (buffer-substring (point) (progn (forward-sexp 1) (point)))) exprs))
          (delete-region (- here 2) (progn (search-forward "}") (point)))
          (unless emptyp (insert "%s"))
          (ignore-errors (forward-char 1))))
      (append (list 'format (buffer-string)) (reverse exprs)))))

;; demo with variable and code substitution 
(fmt "My name is #{user-full-name}, I am running Emacs #{(if (display-graphic-p) \"with a GUI\" \"in a terminal\")}.")
;; results in
"My name is Jordon Biondo, I am running Emacs with a GUI."

Bạn thậm chí có thể nhúng một fmtcuộc gọi bên trong một cuộc gọi khác fmtnếu bạn điên

(fmt "#{(fmt\"#{(fmt\\\"#{user-full-name}\\\")}\")}")
;; =>
"Jordon Biondo"

Mã chỉ mở rộng cho một formatcuộc gọi để tất cả các thay thế được thực hiện theo thứ tự và được đánh giá trong thời gian chạy.

(cl-prettyexpand '(fmt "Hello, I'm running Emacs #{emacs-version} on a #{system-type} machine with #{(length (window-list))} open windows."))

;; expands to

(format "Hello, I'm running Emacs %s on a %s machine with %s open windows."
        emacs-version
        system-type
        (length (window-list)))

Các cải tiến có thể được thực hiện với loại định dạng được sử dụng thay vì luôn sử dụng% s, nhưng điều đó sẽ phải được thực hiện trong thời gian chạy và sẽ thêm chi phí nhưng có thể được thực hiện bằng cách bao quanh tất cả các định dạng trong một lệnh gọi hàm định dạng độc đáo mọi thứ dựa trên về loại nhưng thực sự là kịch bản duy nhất mà bạn muốn có lẽ là nổi và bạn thậm chí có thể thực hiện (định dạng "% f" float) trong thay thế là bạn đã tuyệt vọng.

Nếu tôi làm việc với nó nhiều hơn, nhiều khả năng tôi sẽ cập nhật ý chính này thay vì câu trả lời này. https://gist.github.com/jordonbiondo/c4e22b4289be130bc59b


3

Không phải là mục đích chung, nhưng sẽ giải quyết trường hợp của bạn:

(apply 'format "%s %s %s" (make-list 3 'a))

Sử dụng ví dụ được cung cấp:

(apply 'format (concat " * - :raw-html:`<img width=\"100%%\" "
                       "src=\"http://xxx.xxx/images/languages/"
                       "staff/%s.jpg\" alt=\"%s.jpg\"/>` - .. _%s:")
       (make-list 3 'some-image))

cho:

" * - :raw-html:`<img width=\"100%\" src=\"http://xxx.xxx/images/languages/staff/some-image.jpg\" alt=\"some-image.jpg\"/>` - .. _some-image:"

Đây là một chuỗi mẫu tôi đang xử lý: " * - :raw-html:`<img width=\"100%%\" src=\"http://xxx.xxx/images/languages/staff/%s.jpg\" alt=\"%s.jpg\"/>` - .. _%s:"- tất cả %sđều giống nhau.
Adobe

@Adobe Tôi đã cập nhật câu trả lời với ví dụ của bạn.
wvxvw
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.