将字符串转换为 26 进制并返回时计算 "A" 的优雅方式?

Elegant Way Of Accounting For "A" When Converting Strings To 26-Ary And Back?

我需要将字符串转换为 26 进制,然后才能将它们转换回来。

我当前的代码是:

(define (26-ary-word s)
  (let ([len (string-length s)])
        (let f ([n 0]
                [acc (+
                      (- (char->integer (string-ref s 0)) 97)
                      1)]) ; adding 1 so that all strings start with 'b'
          (if (< n len)
              (f (add1 n) (+ (* acc 26) (- (char->integer (string-ref s n)) 97)))
              acc))))

(define (word-ary-26 n)
  (let f ([n (/ (- n (modulo n 26)) 26)]
          [acc (cons (integer->char (+ (modulo n 26) 97)) '())])
    (if (> n 0)
        (f (/ (- n (modulo n 26)) 26) (cons (integer->char (+ (modulo n 26) 97)) acc))
        (list->string (cdr acc))))) ; remove "b" from front of string

我在 acc 开头加 1,然后去掉末尾的 "b"。这是因为 "a" - 97 乘以 26 仍然是 0.

这已经很丑陋了,但它甚至不起作用。 "z" 在第一个位置 (26^2) 时被记录为“701”,被翻译回 "az".

我可以添加另一个 if 子句来检测第一个字母是否为 z,但这真的很难看。有什么办法可以避免这个问题吗?

          (if (and (= n 0) (= acc 26))
              (f (add1 n) 51)
              (f (add1 n) (+ (* acc 26) (- (char->integer (string-ref s n)) 97))))

这是我不得不使用的丑陋的边缘案例处理代码。

老实说,我不完全确定您的代码在做什么,但无论哪种方式,它都比需要的复杂得多。将 base-26 字符串转换为整数非常简单,只需使用一些高阶结构即可:

; (char-in #\a #\z) -> (integer-in 0 25)
(define (base-26-char->integer c)
  (- (char->integer c) (char->integer #\a)))

; #rx"[a-z]+" -> integer?
(define (base-26-string->integer s)
  (let ([digits (map base-26-char->integer (string->list s))])
    (for/fold ([sum 0])
              ([digit (in-list digits)])
      (+ (* sum 26) digit))))

通过将问题分解为两个函数,一个转换单个字符,一个转换整个字符串,我们可以轻松地利用 Racket 的 string->list 函数来简化实现。

使用纯函数结构进行逆向转换实际上有点棘手,但使用额外的辅助函数变得非常简单,"explodes" 将整数转换为任何基数的数字。

; integer? [integer?] -> (listof integer?)
(define (integer->digits i [base 10])
  (reverse
   (let loop ([i i])
     (if (zero? i) empty
         (let-values ([(q r) (quotient/remainder i base)])
           (cons r (loop q)))))))

那么字符串生成函数的实现就很明显了。

; (integer-in 0 25) -> (char-in #\a #\z)
(define (integer->base-26-char i)
  (integer->char (+ i (char->integer #\a))))

; integer? -> #rx"[a-z]+"
(define (integer->base-26-string i)
  (list->string (map integer->base-26-char (integer->digits i 26))))