Benannte Zeichenfolgensubstitution?


13

Oft muss ich dieselbe Zeichenfolge mehrfach ersetzen:

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

(es ist nur ein Dummy-Beispiel, in diesem Fall ist es besser, "a" mit einem Leerzeichen zu kleben, aber im Allgemeinen befasse ich mich mit komplizierteren Situationen)

Gibt es eine Möglichkeit, eine benannte Ersetzung vorzunehmen? Zum Beispiel würde man in Python schreiben:

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


@Malabarba: Ich habe hier eine modifizierte Version einer Antwort aus diesem Thread als Antwort gepostet .
Adobe

Antworten:


16

Das Umschreiben dieser Antwort ergibt eine andere Lösung:

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

Edit : Eine andere format-specLösung

Malabarba gibt in Kommentaren eine andere Lösung:

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

Edit 2 : Bewertung vor der Substitution:

Hier einige Beispiele mit Bewertung vor der Substitution:

(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
Beachten Sie auch, dass dies format-spec-makenur eine Liste ist:'((?a . "a") (?b . "b"))
Malabarba

1
"scheint nicht für Zahlen zu funktionieren" - siehe emacs.stackexchange.com/questions/7481/…
npostavs

@npostavs: Gut zu wissen! Ich habe die Antwort bearbeitet.
Adobe

14

Die String-Manipulationsbibliothek von Magnar Sveen an el bietet eine Vielzahl von Möglichkeiten, dies zu tun. Beispielsweise:

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

Beachten Sie, dass s-formatjede replacer Funktion übernehmen können, sondern bietet eine spezielle Handhabung für aget, eltund gethash. Sie könnten also eine Liste von Tokens verwenden und sie nach Index referenzieren, wie folgt:

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

Sie können auch in-scope-Variablen wie folgt ersetzen:

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

1
Hervorragend, ich wusste nichts über diese Funktion! Ich habe die meiste Zeit an el gearbeitet, um nur einen Blick darauf zu werfen, wie in Emacs übliche Zeichenfolgenmanipulationsaufgaben ausgeführt werden, aber dies ist wirklich mehr als nur ein einzeiliger Wrapper einer vorhandenen Funktion.
Wasamasa

3

Das s-lex-Format von sel ist genau das, was Sie wollen, aber wenn Sie in der Lage sein möchten, Code in die Substitutionsblöcke und nicht nur in Variablennamen einzufügen, habe ich dies als Proof of Concept geschrieben.

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

Sie können sogar einen fmtAnruf in einen anderen einbetten, fmtwenn Sie verrückt sind

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

Der Code wird nur zu einem formatAufruf erweitert, sodass alle Ersetzungen der Reihe nach ausgeführt und zur Laufzeit ausgewertet werden.

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

Es könnte verbessert werden, welcher Formattyp verwendet wird, anstatt immer% s zu verwenden. Dies müsste jedoch zur Laufzeit erfolgen und würde zusätzlichen Aufwand verursachen. Dies könnte jedoch dadurch erreicht werden, dass alle Formatierungsargumente in einem Funktionsaufruf zusammengefasst werden, der die Dinge gut formatiert on type aber wirklich das einzige szenario, in dem du das willst, ist wahrscheinlich floats und du könntest sogar ein (format "% f" float) in der ersetzung machen, du warst verzweifelt.

Wenn ich mehr dran arbeite, aktualisiere ich eher diesen Kern als diese Antwort. https://gist.github.com/jordonbiondo/c4e22b4289be130bc59b


3

Kein allgemeiner Zweck, löst aber Ihren Fall:

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

Anhand des bereitgestellten Beispiels:

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

gibt:

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

Hier ist eine Beispielzeichenfolge, mit der ich es zu tun habe: " * - :raw-html:`<img width=\"100%%\" src=\"http://xxx.xxx/images/languages/staff/%s.jpg\" alt=\"%s.jpg\"/>` - .. _%s:"- Alle %ssind gleich.
Adobe

@Adobe Ich habe die Antwort mit Ihrem Beispiel aktualisiert.
wvxvw
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.