【发布时间】:2018-11-29 17:01:24
【问题描述】:
我正在尝试在 Racket 中做些什么,我想在数字后面加上字母。
对于这个例子,我只想将10000 表示为10K,将1000000 表示为1M。
有没有办法(使用宏或其他方式)我可以将1M 扩展为:
(* 1 1000000)
或者类似的东西?
【问题讨论】:
我正在尝试在 Racket 中做些什么,我想在数字后面加上字母。
对于这个例子,我只想将10000 表示为10K,将1000000 表示为1M。
有没有办法(使用宏或其他方式)我可以将1M 扩展为:
(* 1 1000000)
或者类似的东西?
【问题讨论】:
在 Racket 中,10K 之类的东西是标识符,通常是指变量。有两种方法可以将它们变成数字:
您可以通过定义 #%top 宏来重新定义对未定义标识符执行的操作。
#lang racket
(require syntax/parse/define
(only-in racket [#%top old-#%top]))
(define-syntax-parser #%top
[(_ . x:id)
#:when (id-has-a-k-at-the-end? #'x)
(transform-id-into-number #'x)]
[(_ . x)
#'(old-#%top . x)])
但是,这有一个微妙的问题。如果您的程序中有任何标识符或变量以 K 结尾,它们可以覆盖任何以这种方式编写的数字。您需要注意不要意外覆盖本来是数字的内容。
这将花费更多时间,但它更接近执行此操作的“正确方法”,因为它避免了当变量恰好有 K 结尾时发生冲突。
扩展阅读器的一种更简单的方法是使用readtable。您可以创建一个扩展 readtable 的函数,如下所示:
;; Readtable -> Readtable
(define (extend-readtable orig-rt)
;; Char InputPort Any Nat Nat Nat -> Any
(define (rt-proc char in src ln col pos)
....)
...
(make-readtable orig-rt
#f 'non-terminating-macro rt-proc
...))
要使用它来定义#lang 语言,您需要将阅读器实现放在your-language/lang/reader.rkt 中。这里是number-with-k/lang/reader.rkt,其中number-with-k 目录安装为单一集合包(raco pkg install path/to/number-with-k)。
number-with-k/lang/reader.rkt
#lang racket
(provide (rename-out [-read read]
[-read-syntax read-syntax]
[-get-info get-info]))
(require syntax/readerr
syntax/module-reader)
;; Readtable -> Readtable
(define (extend-readtable orig-rt)
;; Char InputPort Any Nat Nat Nat -> Any
(define (rt-proc char in src ln col pos)
....)
...
(make-readtable orig-rt
#f 'non-terminating-macro rt-proc))
;; [X ... -> Y] -> [X ... -> Y]
(define ((wrap-reader rd) . args)
(parameterize ([current-readtable (extend-readtable (current-readtable))])
(apply rd args)))
(define-values [-read -read-syntax -get-info]
(make-meta-reader 'number-with-k
"language path"
lang-reader-module-paths
wrap-reader
wrap-reader
identity))
主要工作是填补extend-readtable函数中的....漏洞。例如,您可以让它识别以K 结尾的标识符,如下所示:
;; Readtable -> Readtable
(define (extend-readtable orig-rt)
;; Char InputPort Any Nat Nat Nat -> Any
(define (rt-proc char in src ln col pos)
(define v (read-syntax/recursive src in char orig-rt #f))
(cond
[(and (identifier? v) (id-has-a-k-at-the-end? v))
(transform-id-into-number v)]
[else
v]))
(make-readtable orig-rt
#f 'non-terminating-macro rt-proc))
完成此操作后,您将number-with-k 目录安装为一个包,您应该可以像这样使用#lang number-with-k:
#lang number-with-k racket
(+ 12K 15)
; => 12015
【讨论】:
#%top,你可以使用identifier-binding来检查标识符是否已经有绑定——如果有,你可能想跳过转换。
identifier-binding 检查不足以避免这些问题,因为如果定义了标识符,它首先不会调用 #%top。
最简单的方法是定义你需要的后缀。
(define K 1000)
(define M 1000000)
然后写(* 3.14 M) 314 万。
正如其他人提到的,Racket 支持科学计数法3.14E6 也是 314 万。
另一种选择是定义函数K、M 等,例如:
(define (M x) (* x 1000000))
那你就可以写了
(M 3.14)
意思是 314 万。
【讨论】:
Racket 已经有built in support for this, kind of,通过科学记数法:
1e6 ; 1000000.0 ("1M")
2e7 ; 20000000.0
【讨论】: