Substituição de string nomeada?

13

Muitas vezes tenho que fazer várias substituições da mesma string:

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

(é apenas um exemplo fictício, nesse caso, é melhor colar "a" com um espaço em branco, mas, em geral, eu lido com situações mais complicadas)

Existe uma maneira de fazer uma substituição nomeada? Por exemplo, em python, alguém escreveria:

"{0} {0} {0}".format("a") # or:
"{name} {name} {name}".format(name="a")
Adobe
fonte
@ Malabarba: Eu postei uma porção modificada de alguma resposta desse tópico aqui como resposta .
Adobe

Respostas:

16

Reescrever esta resposta fornece outra solução:

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

Edit : Outra format-specsolução

Como Malabarba dá outra solução nos comentários:

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

Edit 2 : Avaliação antes da substituição:

Aqui estão exemplos com avaliação antes da substituição:

(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
fonte
3
Observe também que format-spec-makeé apenas uma lista de '((?a . "a") (?b . "b"))
discussão
1
"parece não estar funcionando para números" - consulte emacs.stackexchange.com/questions/7481/…
npostavs
@npostavs: É bom saber! Eu editei a resposta.
Adobe
14

A biblioteca de manipulação de cadeias de caracteres de Magnar Sveen, s.el, oferece várias maneiras de fazer isso. Por exemplo:

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

Note-se que s-formatpode tomar qualquer função de substituto, mas fornece tratamento especial para aget, elt, e gethash. Então você pode usar uma lista de tokens e referenciá-los por índice, da seguinte forma:

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

Você também pode substituir usando variáveis ​​no escopo, como este:

(let ((name "test"))
  (s-lex-format "${name} ${name} ${name}"))
;; ==> "test test test"
glucas
fonte
1
Excelente, eu não conhecia esse recurso! Eu usei o s.el na maioria das vezes para dar uma olhada em como executar tarefas comuns de manipulação de string no Emacs, mas isso é realmente mais do que apenas um invólucro de uma linha de uma função existente.
wasamasa
3

O formato s-lex do s.el é realmente o que você deseja, mas se você deseja realmente colocar código dentro dos blocos de substituição e não apenas nos nomes das variáveis, escrevi isso como uma prova de conceito.

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

Você pode até incorporar uma fmtligação dentro de outra, fmtse estiver doido

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

O código apenas se expande para uma formatchamada, para que todas as substituições sejam feitas em ordem e avaliadas em tempo de execução.

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

Melhorias poderiam ser feitas com o tipo de formato usado em vez de sempre usar% s, mas isso teria que ser feito em tempo de execução e aumentaria a sobrecarga, mas poderia ser feito envolvendo todos os argumentos de formato em uma chamada de função que formata bem as coisas no tipo, mas realmente o único cenário em que você gostaria que isso seja provavelmente flutua e você pode até fazer um (formato "% f" float) na substituição é que estava desesperado.

Se eu trabalhar mais nisso, é mais provável que atualize essa essência em vez desta resposta. https://gist.github.com/jordonbiondo/c4e22b4289be130bc59b

Jordon Biondo
fonte
3

Não é de uso geral, mas resolverá seu caso:

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

Usando o exemplo fornecido:

(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:"
wvxvw
fonte
Aqui está um exemplo de string com a qual estou lidando: " * - :raw-html:`<img width=\"100%%\" src=\"http://xxx.xxx/images/languages/staff/%s.jpg\" alt=\"%s.jpg\"/>` - .. _%s:"- todos %ssão iguais.
Adobe
@ Adobe Atualizei a resposta com seu exemplo.
Wvxvw