Scheme 在Racket方案或其他方案中解码十六进制格式字符串的惯用方法

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)

我的输入数据是一个十六进制格式的字符串,长度没有限制。我需要单独处理字节。例如,对于“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)
        (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
on
in-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)