我需要将字符串转换为26-ary,然后才能将它们转换回来。
我目前的代码是:
(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
我添加1到acc开头,并删除结尾的“b”。这是因为将"a" - 97
乘以26仍为0。
这已经很丑了,但它甚至都不起作用。当“z”位于第一个位置(26^2
)时,“z”被记录为“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))))
这是我必须使用的丑陋边缘案件处理代码。
答案 0 :(得分:1)
老实说,我并不完全确定你的代码在做什么,但无论哪种方式,它都要比它需要的复杂得多。通过使用一些高阶构造,将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
函数来简化实现。
逆转换实际上使用纯粹的功能构造变得更加优雅,但是使用额外的辅助函数变得极其微不足道了#34;爆炸&#34;任何基数中的整数。
; 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))))