刚刚在学习 pcase
时遇到一个 format
使用场景:
(pcase '(1 1)
(`(,x ,x) (format "Matching (%s %s) where %s eq %s" x x x x)))
=> "Matching (1 1) where 1 eq 1"
同一个 x
要写 4 次,不是很友好,想到 Emacs 自带的 format-spec.el
,用 format-spec
写一次就够了:
(pcase '(1 1)
(`(,x ,x) (format-spec "Matching (%x %x) where %x eq %x" `((?x . ,x)))))
=> "Matching (1 1) where 1 eq 1"
format-spec
的用法是:
(format-spec FORMAT SPECIFICATION)
其中 SPECIFICATION
是个 Alist,且它的 Key 是个字符。
撇开 pcase
再举个例子,假设要表达 a + b = b + a
:
(format "%s + %s = %s + %s" 1 2 2 1)
=> "1 + 2 = 2 + 1"
(format-spec "%a + %b = %b + %a" '((?a . 1) (?b . 2)))
=> "1 + 2 = 2 + 1"
明显这里用 format-spec
的代码更清楚些。
值得注意的是:format-spec.el
是个非常简单的库,仅相当于 (format "%s")
的封装,所以不能完全取代 format
,如:
(format "%.2f" pi)
=> "3.14"
(format-spec "%.2x" (list (cons ?x pi)))
=> "3."
因为上面的 format-spec
实际等价于:
(format "%.2s" pi)
=> "3."
Emacs 26 的 format
现在支持指定位置了:
原来的
(format "%s + %s = %s + %s" 1 2 2 1)
=> "1 + 2 = 2 + 1"
现在可写成:
(format "%1$s + %2$s = %2$s + %1$s" 1 2)
=> "1 + 2 = 2 + 1"
---
** Two new commands for finding the source code of Emacs Lisp
libraries: 'find-library-other-window' and 'find-library-other-frame'.
+++
** The new variable 'display-raw-bytes-as-hex' allows you to change
the display of raw bytes from octal to hex.
+++
** You can now provide explicit field numbers in format specifiers.
For example, '(format "%2$s %1$s %2$s" "X" "Y")' produces "Y X Y".
+++
** Emacs now supports optional display of line numbers in the buffer.
This is similar to what 'linum-mode' provides, but much faster and
doesn't usurp the display margin for the line numbers. Customize the
buffer-local variable 'display-line-numbers' to activate this optional
display. Alternatively, you can use the 'display-line-numbers-mode'
minor mode or the global 'global-display-line-numbers-mode'. When
using these modes, customize 'display-line-numbers-type' with the same
(let ((name "John"))
(princ #"Hello, ${name}!\n"))
;-> Hello, John!
其实如果能实现成这样应该更方便一些。
范例来自 CL21
单个字母的 placeholder 有点弱,而且 (format-spec "%c" '((?c . "string")))
看起来有点迷惑,总会想起 fromat
:
%s means print a string argument. Actually, prints any object, with ‘princ’.
%d means print as signed number in decimal.
%o means print as unsigned number in octal, %x as unsigned number in hex.
%X is like %x, but uses upper case.
%e means print a number in exponential notation.
%f means print a number in decimal-point notation.
%g means print a number in exponential notation if the exponent would be
less than -4 or greater than or equal to the precision (default: 6);
otherwise it prints in decimal-point notation.
%c means print a number as a single character.
%S means print any object as an s-expression (using ‘prin1’).
其实 format-spec
还可以再完善一些,让 placeholder 更具可读性,我这里写了一个简陋的 format-spec+ :
(format-spec+ ":%-10{key}s %{val}d" '((key . "foo") (val . 100)))
(format-spec+ "%10{val}d :%{key}s" '((key . "bar") (val . 200)))
(format-spec+ ":%-10{key}s %{val}d, :%-10{key}s %{val}d" '((key . "quux") (val . 300)))
;; => :foo 100
;; => 200 :bar
;; => :quux 300, :quux 300
1 个赞
写成 #"Hello, ${name}!\n"
这样就得引入了新「语法」,或者说「概念」.我也试了下实现类似的效果:
(defmacro my-format (string)
(let* ((vars ())
(format-string
(replace-regexp-in-string
(rx "${" (group (1+ alnum)) "}")
(lambda (substring)
(push (intern (match-string 1 substring)) vars)
"%s")
string)))
`(format ,format-string ,@(nreverse vars))))
(let ((name "John"))
(my-format "hello, ${name}"))
=> "hello, John"
Ruby 有个 String interpolation 概念:
name = "Ada"
puts "Hello, #{name}!"
用 Emacs Lisp 感受了下:
(defmacro chunyang-string-interpolation (string)
(let (format-string forms)
(setq format-string
(replace-regexp-in-string
"#{\\(.+\\)}"
(lambda (substring)
(push (read (match-string 1 substring)) forms)
"%s")
string))
`(format ,format-string ,@forms)))
(chunyang-string-interpolation "Hello, #{user-full-name}!")
↦ (format "Hello, %s!" user-full-name)
⇒ "Hello, Xu Chunyang!"
(chunyang-string-interpolation "1 + 2 = #{(+ 1 2)}")
↦ (format "1 + 2 = %s" (+ 1 2))
⇒ "1 + 2 = 3"
1 个赞
Python 风格:
'{} {}'.format('one', 'two') # => one two
'{1} {0}'.format('one', 'two') # => two one︎
'{:04d}'.format(42) # => 0042
'{num:04d}'.format(num=4) # => 0042
{num:04d}
比 %04{num}d
好看一些,而且处理也更方便。