创建带有后缀的数字Racket

6

我正在尝试在Racket中进行实验,想要给数字添加后缀。

举个例子,我想把10000表示为10K,而把1000000表示为1M

有没有一种方法(使用宏或其他方式),可以将1M展开为:

(* 1 1000000)

或者类似的东西?
3个回答

7
在Racket中,像10K这样的东西是标识符,通常会指代变量。有两种方法可以将它们转换为数字:

1:重新定义“未定义”的标识符的含义

您可以通过定义一个#%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结尾的标识符或变量,它们可能会覆盖以该方式编写的任何数字。您需要小心,不要意外覆盖原本应该是数字的内容。
2:制作一个读取扩展,将它们转换为数字而不是标识符
这将需要更多时间,但更接近于正确的方法,因为它避免了当变量恰好以K结尾时发生冲突的情况。
扩展读取器的其中一种较简单的方法是使用 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 来检查标识符是否已经绑定 - 如果是,则可能要跳过转换。 - default.kramer
1
一个 identifier-binding 检查是不足以避免问题的,因为如果标识符已经被定义,它就不会在第一时间调用 #%top - Alex Knauth

4
最简单的方法是定义您需要的后缀。
(define K 1000)
(define M 1000000)

然后用 (* 3.14 M) 来表示 3.14 百万。

正如其他人所提到的,Racket 支持科学计数法, 3.14E6 也可以表示 3.14 百万。

另一个选择是定义函数 K, M 等等,例如:

(define (M x) (* x 1000000))

您可以随后编写以下代码

(M 3.14)

表示314万。


1
Racket已经通过科学计数法内置支持此功能, 有点类似。
1e6 ; 1000000.0 ("1M")
2e7 ; 20000000.0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接