我的输入数据是十六进制格式的字符串,不受长度限制。我需要单独处理字节。例如,对于"AABBCCDDEEFF“,我希望处理AA,然后处理BB、CC、DD、EE、FF。
使用Common,我们可以使用循环:
(loop for (a b) on list do [processing])在球拍计划中,我写了这个解决方案:
(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方案和更一般的Lisp中,执行这种操作的惯用方法是什么?
发布于 2019-11-20 11:19:40
在Racket中有很多方法可以做到这一点(与更一般的Scheme不同):您想要的概念是序列、流和生成器。
首先是一个函数,用于计算字符中的十六进制数字(这个函数可能存在于球拍中,但我太懒了,找不到它,这是可行的):
(define (char->hex-digit c)
;; Turn a character into a hex digit
(cond [(char<=? #\0 c #\9)
(- (char->integer c) (char->integer #\0))]
[(char<=? #\A c #\F)
(+ 10 (- (char->integer c) (char->integer #\A)))]
[(char<=? #\a c #\f)
(+ 10 (- (char->integer c) (char->integer #\a)))]
[else
(error 'char->hex-digit "~A is not a hex character" c)]))下面是一种简单的方法,可以将十六进制字符串转换为字节列表,方法是从字符串中创建两个序列,其中一个从字符串中选择高的序列,另一个选择低的数字:
(define (hex-string->byte-list hs)
(for/list ([h (in-string hs 0 #f 2)]
[l (in-string hs 1 #f 2)])
(+ (* (char->hex-digit h) 16) (char->hex-digit l))))显然,根据您使用的for变体,您可以构造不同的结果。
示例:
> (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]另一种方法是使用in-slice
(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 (in-hex-stream-bytes ...)]) b)了。
发布于 2019-11-20 09:32:13
请注意,Common版本将无法工作,因为输入是一个字符串;为了使用相同的方法,您应该首先将字符串转换为一个字符列表;其次,您需要添加一个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之外,您也可以跳过它,直接处理该值):
(loop
with string = "AABBCCDDEEFF"
with size = (length string)
initially (assert (evenp size))
for start from 0 by 2
for end from 2 by 2 upto size
collect (parse-integer string :start start :end end :radix 16))
=> (170 187 204 221 238 255)发布于 2019-11-20 12:38:36
一种惯用的方法是使用递归(保持与split-string相同的功能),如下所示:
(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 '()))for/list方法( in-slice on in-string sequence,简称这里)也是一种惯用方法。
请注意,我们还可以在字符串上使用一个小接口,如以下所示,以提高其可读性:
(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))))]))https://stackoverflow.com/questions/58950635
复制相似问题