球拍:如何对点分隔的数字进行排序
Racket: how to sort dot-separated numbers
如何在 Racket 中用点分隔数字,例如软件的版本号?
例如
'("1.1.2" "1.0.0" "1.3.3" "1.0.7" "1.0.2")
分类为
'("1.0.0" "1.0.2" "1.0.7" "1.1.2" "1.3.3")
在 #\.
上拆分每个字符串并将其转换为数字列表,并根据该转换进行排序。使用 SRFI-67 比较列表的示例:
#lang racket
(require srfi/67)
(define versions '("1.1.2" "1.0.0" "1.12.1" "1.3.3" "1.0.7" "1.0.2"))
(define (sort-versions vlst)
(sort vlst (lambda (a b) (< (list-compare integer-compare a b) 0))
#:key (lambda (v) (map string->number (string-split v ".")))
#:cache-keys? #t))
(writeln (sort-versions versions))
从头开始的普通方案:
;; Convert a character into a number
;; Example: (char->number #) => 3
(define (char->number char)
(case char
((#[=10=]) 0)
((#) 1)
((#) 2)
((#) 3)
((#) 4)
((#) 5)
((#) 6)
((#) 7)
((#) 8)
((#) 9)))
;; Convert a list of characters into a decimal number.
;; Example: (list->decimal '(# # #)) => 123
(define (list->decimal digits)
(let loop ((digits digits)
(value 0))
(if (pair? digits)
(loop (cdr digits)
(+ (* value 10)
(char->number (car digits))))
value)))
;; Convert a version string into list of decimals.
;; Example: (version->list "1.2.3") => (1 2 3)
(define (version->list version)
(let loop ((chars (string->list version))
(fragment '())
(result '()))
(if (pair? chars)
(let ((char (car chars))
(chars (cdr chars)))
(if (char=? char #\.)
(loop chars
'()
(cons (list->decimal fragment)
result))
(loop chars
(cons char fragment)
result)))
(reverse (cons (list->decimal fragment)
result)))))
;; Convert a list of version numbers into a string.
;; Example: (list->version '(1 2 3)) => "1.2.3"
(define (list->version numbers)
(let loop ((numbers numbers)
(result "")
(delimiter ""))
(if (pair? numbers)
(loop (cdr numbers)
(string-append result
delimiter
(number->string (car numbers)))
".")
result)))
;; Check if a version is lower than the other.
;; Example: (version<? '(1 2 3) '(1 2)) => #f
(define (version<? v1 v2)
(if (pair? v1)
(if (pair? v2)
(let ((m1 (car v1))
(m2 (car v2)))
(cond
((< m1 m2) #t)
((> m1 m2) #f)
(else (version<? (cdr v1)
(cdr v2)))))
#f)
(if (pair? v2)
#t
#f)))
;; Sort versions.
(define (sort-versions versions)
(map list->version
(sort version<?
(map version->list versions))))
;; Example
(let ((unsorted '("1.1.2" "1.0.0" "1.3.3" "1.0.7" "1.0.2"))
(sorted '("1.0.0" "1.0.2" "1.0.7" "1.1.2" "1.3.3")))
(equal? (sort-versions unsorted)
sorted))
以上将所有版本字符串转换为小数列表,对列表进行排序并将小数列表转换回字符串。正如肖恩的回答中所解释的那样,可以避免最后一步。这使得在对版本进行排序时有必要保留原始版本字符串。这可以通过装箱版本的两个表示来实现。比较必须拆箱列表表示。结果是通过拆箱字符串表示创建的。下面显示了这个替代实现。
;; Convert a character into a number
;; Example: (char->number #) => 3
(define (char->number char)
(case char
((#[=11=]) 0)
((#) 1)
((#) 2)
((#) 3)
((#) 4)
((#) 5)
((#) 6)
((#) 7)
((#) 8)
((#) 9)))
;; Convert a list of characters into a decimal number.
;; Example: (list->decimal '(# # #)) => 123
(define (list->decimal digits)
(let loop ((digits digits)
(value 0))
(if (pair? digits)
(loop (cdr digits)
(+ (* value 10)
(char->number (car digits))))
value)))
;; Convert a version string into list of decimals.
;; Example: (version->list "1.2.3") => ("1.2.3" 1 2 3)
(define (version->list version)
(let loop ((chars (string->list version))
(fragment '())
(result '()))
(if (pair? chars)
(let ((char (car chars))
(chars (cdr chars)))
(if (char=? char #\.)
(loop chars
'()
(cons (list->decimal fragment)
result))
(loop chars
(cons char fragment)
result)))
(cons version
(reverse (cons (list->decimal fragment)
result))))))
;; Convert a list of version numbers into a string.
;; Example: (list->version '("1.2.3" 1 2 3)) => "1.2.3"
(define list->version car)
;; Check if a version is lower than the other.
;; Example: (version<? '("1.2.3" 1 2 3) '("1.2" 1 2)) => #f
(define (version<? v1 v2)
(let loop ((v1 (cdr v1))
(v2 (cdr v2)))
(if (pair? v1)
(if (pair? v2)
(let ((m1 (car v1))
(m2 (car v2)))
(cond
((< m1 m2) #t)
((> m1 m2) #f)
(else (loop (cdr v1)
(cdr v2)))))
#f)
(if (pair? v2)
#t
#f))))
;; Sort versions.
(define (sort-versions versions)
(map list->version
(sort version<?
(map version->list versions))))
;; Example
(let ((unsorted '("1.1.2" "1.0.0" "1.3.3" "1.0.7" "1.0.2"))
(sorted '("1.0.0" "1.0.2" "1.0.7" "1.1.2" "1.3.3")))
(equal? (sort-versions unsorted)
sorted))
您可以将每个字符串 "X.Y.Z"
转换为数字,对数字列表进行排序,然后将数字转换回字符串。您可以使用许多将 X.Y.Z 编码为数字的想法,例如 Goedel's encoding, chinese remainder theorem 等
如何在 Racket 中用点分隔数字,例如软件的版本号?
例如
'("1.1.2" "1.0.0" "1.3.3" "1.0.7" "1.0.2")
分类为
'("1.0.0" "1.0.2" "1.0.7" "1.1.2" "1.3.3")
在 #\.
上拆分每个字符串并将其转换为数字列表,并根据该转换进行排序。使用 SRFI-67 比较列表的示例:
#lang racket
(require srfi/67)
(define versions '("1.1.2" "1.0.0" "1.12.1" "1.3.3" "1.0.7" "1.0.2"))
(define (sort-versions vlst)
(sort vlst (lambda (a b) (< (list-compare integer-compare a b) 0))
#:key (lambda (v) (map string->number (string-split v ".")))
#:cache-keys? #t))
(writeln (sort-versions versions))
从头开始的普通方案:
;; Convert a character into a number
;; Example: (char->number #) => 3
(define (char->number char)
(case char
((#[=10=]) 0)
((#) 1)
((#) 2)
((#) 3)
((#) 4)
((#) 5)
((#) 6)
((#) 7)
((#) 8)
((#) 9)))
;; Convert a list of characters into a decimal number.
;; Example: (list->decimal '(# # #)) => 123
(define (list->decimal digits)
(let loop ((digits digits)
(value 0))
(if (pair? digits)
(loop (cdr digits)
(+ (* value 10)
(char->number (car digits))))
value)))
;; Convert a version string into list of decimals.
;; Example: (version->list "1.2.3") => (1 2 3)
(define (version->list version)
(let loop ((chars (string->list version))
(fragment '())
(result '()))
(if (pair? chars)
(let ((char (car chars))
(chars (cdr chars)))
(if (char=? char #\.)
(loop chars
'()
(cons (list->decimal fragment)
result))
(loop chars
(cons char fragment)
result)))
(reverse (cons (list->decimal fragment)
result)))))
;; Convert a list of version numbers into a string.
;; Example: (list->version '(1 2 3)) => "1.2.3"
(define (list->version numbers)
(let loop ((numbers numbers)
(result "")
(delimiter ""))
(if (pair? numbers)
(loop (cdr numbers)
(string-append result
delimiter
(number->string (car numbers)))
".")
result)))
;; Check if a version is lower than the other.
;; Example: (version<? '(1 2 3) '(1 2)) => #f
(define (version<? v1 v2)
(if (pair? v1)
(if (pair? v2)
(let ((m1 (car v1))
(m2 (car v2)))
(cond
((< m1 m2) #t)
((> m1 m2) #f)
(else (version<? (cdr v1)
(cdr v2)))))
#f)
(if (pair? v2)
#t
#f)))
;; Sort versions.
(define (sort-versions versions)
(map list->version
(sort version<?
(map version->list versions))))
;; Example
(let ((unsorted '("1.1.2" "1.0.0" "1.3.3" "1.0.7" "1.0.2"))
(sorted '("1.0.0" "1.0.2" "1.0.7" "1.1.2" "1.3.3")))
(equal? (sort-versions unsorted)
sorted))
以上将所有版本字符串转换为小数列表,对列表进行排序并将小数列表转换回字符串。正如肖恩的回答中所解释的那样,可以避免最后一步。这使得在对版本进行排序时有必要保留原始版本字符串。这可以通过装箱版本的两个表示来实现。比较必须拆箱列表表示。结果是通过拆箱字符串表示创建的。下面显示了这个替代实现。
;; Convert a character into a number
;; Example: (char->number #) => 3
(define (char->number char)
(case char
((#[=11=]) 0)
((#) 1)
((#) 2)
((#) 3)
((#) 4)
((#) 5)
((#) 6)
((#) 7)
((#) 8)
((#) 9)))
;; Convert a list of characters into a decimal number.
;; Example: (list->decimal '(# # #)) => 123
(define (list->decimal digits)
(let loop ((digits digits)
(value 0))
(if (pair? digits)
(loop (cdr digits)
(+ (* value 10)
(char->number (car digits))))
value)))
;; Convert a version string into list of decimals.
;; Example: (version->list "1.2.3") => ("1.2.3" 1 2 3)
(define (version->list version)
(let loop ((chars (string->list version))
(fragment '())
(result '()))
(if (pair? chars)
(let ((char (car chars))
(chars (cdr chars)))
(if (char=? char #\.)
(loop chars
'()
(cons (list->decimal fragment)
result))
(loop chars
(cons char fragment)
result)))
(cons version
(reverse (cons (list->decimal fragment)
result))))))
;; Convert a list of version numbers into a string.
;; Example: (list->version '("1.2.3" 1 2 3)) => "1.2.3"
(define list->version car)
;; Check if a version is lower than the other.
;; Example: (version<? '("1.2.3" 1 2 3) '("1.2" 1 2)) => #f
(define (version<? v1 v2)
(let loop ((v1 (cdr v1))
(v2 (cdr v2)))
(if (pair? v1)
(if (pair? v2)
(let ((m1 (car v1))
(m2 (car v2)))
(cond
((< m1 m2) #t)
((> m1 m2) #f)
(else (loop (cdr v1)
(cdr v2)))))
#f)
(if (pair? v2)
#t
#f))))
;; Sort versions.
(define (sort-versions versions)
(map list->version
(sort version<?
(map version->list versions))))
;; Example
(let ((unsorted '("1.1.2" "1.0.0" "1.3.3" "1.0.7" "1.0.2"))
(sorted '("1.0.0" "1.0.2" "1.0.7" "1.1.2" "1.3.3")))
(equal? (sort-versions unsorted)
sorted))
您可以将每个字符串 "X.Y.Z"
转换为数字,对数字列表进行排序,然后将数字转换回字符串。您可以使用许多将 X.Y.Z 编码为数字的想法,例如 Goedel's encoding, chinese remainder theorem 等