Substitusi string bernama?

13

Saya sering harus membuat beberapa substitusi dari string yang sama:

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

(Ini hanya contoh tiruan, dalam hal ini lebih baik merekatkan "a" dengan spasi putih, tetapi secara umum saya berurusan dengan situasi yang lebih rumit)

Apakah ada cara untuk membuat pengganti bernama? Sebagai contoh dalam python orang akan menulis:

"{0} {0} {0}".format("a") # or:
"{name} {name} {name}".format(name="a")
Adobe
sumber
@Malabarba: Saya memposting bagian yang dimodifikasi dari beberapa jawaban dari utas itu di sini sebagai jawaban .
Adobe

Jawaban:

16

Menulis ulang jawaban ini memberikan solusi lain:

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

Sunting : format-specSolusi lain

Seperti Malabarba memberikan solusi lain dalam komentar:

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

Sunting 2 : Evaluasi sebelum penggantian:

Berikut adalah contoh dengan evaluasi sebelum penggantian:

(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
Adobe
sumber
3
Perhatikan juga bahwa format-spec-makeini hanya daftar lagu:'((?a . "a") (?b . "b"))
Malabarba
1
"Sepertinya tidak berfungsi untuk angka" - lihat emacs.stackexchange.com/questions/7481/…
npostavs
@npostavs: Senang tahu! Saya mengedit jawabannya.
Adobe
14

Pustaka manipulasi string milik Magnar Sveen s.el menyediakan berbagai cara untuk melakukan ini. Sebagai contoh:

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

Perhatikan bahwa s-formatdapat mengambil fungsi pengganti, tetapi memberikan penanganan khusus untuk aget, elt, dan gethash. Jadi Anda bisa menggunakan daftar token dan merujuknya berdasarkan indeks, seperti:

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

Anda juga dapat mengganti menggunakan variabel dalam-lingkup, seperti ini:

(let ((name "test"))
  (s-lex-format "${name} ${name} ${name}"))
;; ==> "test test test"
glukas
sumber
1
Luar biasa, saya tidak tahu tentang fitur ini! Saya telah menggunakan s.el sebagian besar waktu untuk hanya mengintip bagaimana melakukan tugas manipulasi string yang umum di Emacs, tetapi ini benar-benar lebih dari sekadar pembungkus satu baris dari fungsi yang ada.
wasamasa
3

s-lex-format s.el benar-benar seperti yang Anda inginkan, tetapi jika Anda ingin benar-benar dapat menempatkan kode di dalam blok substitusi dan bukan hanya nama variabel, saya menulis ini sebagai bukti konsep.

(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."

Anda bahkan dapat menyematkan fmtpanggilan di dalam yang lain fmtjika Anda gila

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

Kode hanya mengembang ke formatpanggilan sehingga semua pergantian dilakukan secara berurutan dan dievaluasi pada saat dijalankan.

(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)))

Perbaikan dapat dilakukan dengan jenis format apa yang digunakan daripada selalu menggunakan% s, tapi itu harus dilakukan saat runtime dan akan menambah overhead tetapi bisa dilakukan dengan mengelilingi semua format args dalam pemanggilan fungsi yang memformat dengan baik hal-hal berdasarkan pada jenis tetapi sebenarnya satu-satunya skenario di mana Anda ingin yang mungkin mengapung dan Anda bahkan bisa melakukan (format "% f" float) di substitusi adalah Anda putus asa.

Jika saya mengerjakannya lebih banyak, saya lebih cenderung memperbarui inti ini daripada jawaban ini. https://gist.github.com/jordonbiondo/c4e22b4289be130bc59b

Jordon Biondo
sumber
3

Bukan tujuan umum, tetapi akan menyelesaikan kasus Anda:

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

Menggunakan contoh yang disediakan:

(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))

memberi:

" * - :raw-html:`<img width=\"100%\" src=\"http://xxx.xxx/images/languages/staff/some-image.jpg\" alt=\"some-image.jpg\"/>` - .. _some-image:"
wvxvw
sumber
Berikut ini contoh string yang saya hadapi: " * - :raw-html:`<img width=\"100%%\" src=\"http://xxx.xxx/images/languages/staff/%s.jpg\" alt=\"%s.jpg\"/>` - .. _%s:"- semuanya %ssama.
Adobe
@ Adobe Saya telah memperbarui jawaban dengan contoh Anda.
wvxvw