将字符串转换为 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))))
我需要将字符串转换为 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))))