Scheme 在Racket方案或其他方案中解码十六进制格式字符串的惯用方法
我的输入数据是一个十六进制格式的字符串,长度没有限制。我需要单独处理字节。例如,对于“AABBCCDDEEFF”,我想处理AA,然后是BB、CC、DD、EE、FF 使用Common Lisp,我们可以使用循环:Scheme 在Racket方案或其他方案中解码十六进制格式字符串的惯用方法,scheme,lisp,common-lisp,racket,Scheme,Lisp,Common Lisp,Racket,我的输入数据是一个十六进制格式的字符串,长度没有限制。我需要单独处理字节。例如,对于“AABBCCDDEEFF”,我想处理AA,然后是BB、CC、DD、EE、FF 使用Common Lisp,我们可以使用循环: (loop for (a b) on list do [processing]) 在Racket Scheme中,我编写了以下解决方案: (define (split-string str) (let ((bit #t) (char-1 null)
(loop for (a b) on list do [processing])
在Racket Scheme中,我编写了以下解决方案:
(define (split-string str)
(let ((bit #t)
(char-1 null)
(char-2 null)
(result '()))
(for ((char str))
(if bit
(begin
(set! bit #f)
(set! char-1 char))
(begin
(set! bit #t)
(set! char-2 char)
(set! result (cons (~a char-1 char-2) result)))))
;; return
(reverse result)))
(split-string "AABBCCDDEEFF")
;; '("AA" "BB" "CC" "DD" "EE" "FF")
(define (split-string2 str)
(bytes->list (integer->integer-bytes (string->number str 16) 8 false)))
(split-string2 "AABBCCDDEEFF")
;; '(255 238 221 204 187 170 0 0)
我觉得这不是惯用的骗局或方案代码。我写了第二个解决方案:
(define (split-string str)
(let ((bit #t)
(char-1 null)
(char-2 null)
(result '()))
(for ((char str))
(if bit
(begin
(set! bit #f)
(set! char-1 char))
(begin
(set! bit #t)
(set! char-2 char)
(set! result (cons (~a char-1 char-2) result)))))
;; return
(reverse result)))
(split-string "AABBCCDDEEFF")
;; '("AA" "BB" "CC" "DD" "EE" "FF")
(define (split-string2 str)
(bytes->list (integer->integer-bytes (string->number str 16) 8 false)))
(split-string2 "AABBCCDDEEFF")
;; '(255 238 221 204 187 170 0 0)
在Racket Scheme和更普遍的Lisp中,执行此类操作的惯用方法是什么?请注意,由于输入是字符串,通用Lisp版本无法工作;为了使用相同的方法,您应该首先将字符串转换为字符列表;其次,您需要添加一个
by
指令,以通过cddr
超越列表,即跳过已读的b
。
最终看起来是这样的:
(loop
for (a b) on (coerce "AABBCCDDEEFF" 'list) by #'cddr
collect (parse-integer (coerce (vector a b) 'string)
:radix 16))
=> (170 187 204 221 238 255)
但是,这有点浪费,parse integer
允许:start
和:end
参数,因此您不需要分配任何中间列表或字符串(除了最后一个collect
;您也可以跳过它,直接处理值):
在Racket中有很多方法可以做到这一点(与更普遍的Scheme相反):你想要的概念是 首先是一个从字符中计算十六进制数字的函数(这可能存在于Racket中,但我太懒了,找不到它,这是可行的): 显然,根据所使用的的变体,您可以构造不同的结果 示例:
> (hex-string->byte-list "F00D")
'(240 13)
> (hex-string->byte-list "0102030405060708090a0b0C0D0F")
'(1 2 3 4 5 6 7 8 9 10 11 12 13 15)
> (hex-string->byte-list "01xa")
; char->hex-digit: x is not a hex character [,bt for context]
另一种方法是在切片中使用:
(define (hex-string->byte-list hs)
(for/list ([hl (in-slice 2 (in-string hs))])
(+ (* (char->hex-digit (first hl)) 16) (char->hex-digit (second hl)))))
还有很多其他方法可以做到这一点,包括创建您自己的序列或流类型,这样您就可以编写(for/list([b(十六进制流字节…])b)
其中一种惯用方法是使用递归(保留与拆分字符串相同的功能),如下所示:
(define (split-string-recur str)
(cond [(or (string=? str "") (string=? "" (substring str 1))) '()]
[else (cons (substring str 0 2) (split-string-recur (substring str 2)))]))
和一个尾部递归版本:
(define (split-string-trecur str)
(define (split-string-recur str acc)
(cond [(or (string=? str "") (string=? "" (substring str 1))) acc]
[else (split-string-recur (substring str 2) (append acc (list (substring str 0 2))))]))
(split-string-recur str '()))
使用in-slice
onin-string
序列的for/list
方法也是一种惯用方法
请注意,我们还可以在字符串上使用一个小型接口,如下所示,以使其更具可读性:
(module string-util typed/racket
(provide (all-defined-out))
(: empty-string? : (-> String Boolean))
(define (empty-string? s)
(string=? "" s))
(: string-first : (-> String String))
(define (string-first s)
(substring s 0 1))
(: string-last : (-> String String))
(define (string-last s)
(substring s (- (string-length s) 1) (string-length s)))
(: string-rest : (-> String String))
(define (string-rest s)
(substring s 1 (string-length s))))
(require 'string-util)
(define (split-string-recur str)
(cond [(or (empty-string? str) (empty-string? (string-rest str))) '()]
[else (cons (string-append (string-first str) (string-first (string-rest str)))
(split-string-recur (string-rest (string-rest str))))]))
我的问题与从十六进制到字节的转换无关。这是关于如何在Scheme中每2个十六进制字符迭代2个十六进制字符。我现在更清楚球拍的序列,它非常强大。非常有帮助,谢谢!对于任何想知道的人,您可以这样做十六进制数字到数字的转换:`(定义(字符->十六进制数字c)(字符串->数字(格式“#x~a”c)))`
(module string-util typed/racket
(provide (all-defined-out))
(: empty-string? : (-> String Boolean))
(define (empty-string? s)
(string=? "" s))
(: string-first : (-> String String))
(define (string-first s)
(substring s 0 1))
(: string-last : (-> String String))
(define (string-last s)
(substring s (- (string-length s) 1) (string-length s)))
(: string-rest : (-> String String))
(define (string-rest s)
(substring s 1 (string-length s))))
(require 'string-util)
(define (split-string-recur str)
(cond [(or (empty-string? str) (empty-string? (string-rest str))) '()]
[else (cons (string-append (string-first str) (string-first (string-rest str)))
(split-string-recur (string-rest (string-rest str))))]))
CL-USER 2 > (defun split-string (string)
(loop for i from 0 below (length string) by 2
collect (subseq string i (+ i 2))))
SPLIT-STRING
CL-USER 3 > (split-string "AABBCCDDEEFF")
("AA" "BB" "CC" "DD" "EE" "FF")
CL-USER 4 > (mapcar (lambda (s) (parse-integer s :radix 16)) *)
(170 187 204 221 238 255)