Sostituzione stringa denominata?


13

Spesso devo fare diverse sostituzioni della stessa stringa:

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

(è solo un esempio fittizio, in questo caso è meglio incollare "a" con uno spazio bianco, ma in generale mi occupo di situazioni più complicate)

C'è un modo per effettuare una sostituzione nominata? Ad esempio in Python uno scriverebbe:

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


@Malabarba: ho pubblicato una parodia modificata di alcune risposte da quella discussione qui come risposta .
Adobe,

Risposte:


16

Riscrivere questa risposta offre un'altra soluzione:

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

Modifica : un'altra format-specsoluzione

Come Malabarba offre un'altra soluzione nei commenti:

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

Modifica 2 : valutazione prima della sostituzione:

Ecco alcuni esempi con valutazione prima della sostituzione:

(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
Si noti inoltre che format-spec-makeè solo un elenco:'((?a . "a") (?b . "b"))
Malabarba,

1
"non sembra funzionare per i numeri" - vedi emacs.stackexchange.com/questions/7481/…
npostavs

@npostavs: Ottimo a sapersi! Ho modificato la risposta.
Adobe,

14

La libreria di manipolazione delle stringhe s.el di Magnar Sveen offre una varietà di modi per farlo. Per esempio:

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

Si noti che s-formatpuò assumere qualsiasi funzione sostituto, ma fornisce una gestione speciale per aget, elte gethash. Quindi potresti usare un elenco di token e fare riferimento a loro per indice, in questo modo:

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

Puoi anche sostituire usando le variabili nell'ambito, in questo modo:

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

1
Eccellente, non sapevo di questa funzione! Ho usato s.el la maggior parte del tempo solo per dare un'occhiata a come eseguire le comuni attività di manipolazione delle stringhe in Emacs, ma questo è davvero molto più di un semplice wrapper di una riga di una funzione esistente.
Wasamasa,

3

Il formato s-lex di s.el è davvero quello che vuoi, ma se vuoi davvero essere in grado di inserire il codice all'interno dei blocchi di sostituzione e non solo i nomi delle variabili, ho scritto questo come una prova del concetto.

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

Puoi anche incorporare una fmtchiamata in un'altra fmtse sei pazzo

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

Il codice si espande a una formatchiamata, quindi tutte le sostituzioni vengono eseguite in ordine e valutate in fase di esecuzione.

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

Potrebbero essere apportati miglioramenti con quale tipo di formato viene utilizzato invece di utilizzare sempre% s, ma ciò dovrebbe essere fatto in fase di esecuzione e aggiungerebbe overhead ma potrebbe essere fatto circondando tutti gli argomenti di formattazione in una chiamata di funzione che formatta le cose in modo ben basato sul tipo, ma in realtà l'unico scenario in cui vorresti che fosse probabilmente float e potresti anche fare un (formato "% f" float) nella sostituzione è che eri disperato.

Se ci lavoro di più, ho maggiori probabilità di aggiornare questa sintesi anziché questa risposta. https://gist.github.com/jordonbiondo/c4e22b4289be130bc59b


3

Non uno scopo generale, ma risolverà il tuo caso:

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

Utilizzando l'esempio fornito:

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

dà:

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

Ecco una stringa di esempio con cui ho a che fare: " * - :raw-html:`<img width=\"100%%\" src=\"http://xxx.xxx/images/languages/staff/%s.jpg\" alt=\"%s.jpg\"/>` - .. _%s:"- %ssono tutti uguali.
Adobe,

@Adobe Ho aggiornato la risposta con il tuo esempio.
wvxvw,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.