【问题标题】:Sort a list of structures in Racket by more than one key通过多个键对 Racket 中的结构列表进行排序
【发布时间】:2015-11-11 11:01:14
【问题描述】:

我有list-of-cars,一个car 类型的结构列表,其中包含字段“制造商”、“模型”和“年份”。使用常规 Racket sort 功能,我可以按一键排序(例如“制造商”)。但是我怎样才能同时按制造商和型号进行排序,并得出一个等于示例中输出的sort-by-maker-and-model 的列表?

这不是学校作业,我试图用数据做一个比我需要处理的实际数据更无聊的例子。昂贵的汽车对我来说似乎并不太无聊。

享受我狡猾的例子吧!祝你有美好的一天!

#lang racket/base

(define-struct car (maker model year) #:transparent)

(define list-of-cars (list (car "Ferrari" "250 Europa GT" "1954")
                           (car "Bugatti" "Type 2" "1900")
                           (car "Lamborghini" "Flying Star II" "1966")
                           (car "Bugatti" "Type 10" "1908")
                           (car "Ferrari" "166 Inter" "1949")
                           (car "Bugatti" "Type 5" "1903")
                           (car "Maserati" "A6 1500" "1946")
                           (car "Ferrari" "340 America" "1951")
                           (car "Maserati" "5000 GT" "1959")
                           (car "Maserati" "Quattroporte" "1963")
                           (car "Lamborghini" "Egoista" "2013")))

(define (sort-by-maker lst)
  (sort lst
        string<?
        #:key car-maker))

(sort-by-maker list-of-cars)
; =>
(list
 (car "Bugatti" "Type 2" "1900")
 (car "Bugatti" "Type 10" "1908")
 (car "Bugatti" "Type 5" "1903")
 (car "Ferrari" "250 Europa GT" "1954")
 (car "Ferrari" "166 Inter" "1949")
 (car "Ferrari" "340 America" "1951")
 (car "Lamborghini" "Flying Star II" "1966")
 (car "Lamborghini" "Egoista" "2013")
 (car "Maserati" "A6 1500" "1946")
 (car "Maserati" "5000 GT" "1959")
 (car "Maserati" "Quattroporte" "1963"))

(define (sort-by-maker-and-model lst)
  ; ???
  #f)

(sort-by-maker-and-model list-of-cars)
; =>
(list
 (car "Bugatti" "Type 2" "1900")
 (car "Bugatti" "Type 5" "1903")
 (car "Bugatti" "Type 10" "1908")
 (car "Ferrari" "166 Inter" "1949")
 (car "Ferrari" "250 Europa GT" "1954")
 (car "Ferrari" "340 America" "1951")
 (car "Lamborghini" "Egoista" "2013")
 (car "Lamborghini" "Flying Star II" "1966")
 (car "Maserati" "5000 GT" "1959")
 (car "Maserati" "A6 1500" "1946")
 (car "Maserati" "Quattroporte" "1963"))

【问题讨论】:

  • 经过一些喃喃自语,在这个例子中使用car作为结构名称是一个糟糕的想法......即使它工作它重新定义了该语言的正常汽车功能。但无论如何......

标签: sorting struct scheme racket


【解决方案1】:

你需要创建自己的less-than?比较函数:

(define (sort-by-maker-and-model lst)
  (sort lst
        (lambda (e1 e2)
          (or (string<? (car-maker e1) (car-maker e2))
              (and (string=? (car-maker e1) (car-maker e2))
                   (string<? (car-model e1) (car-model e2)))))))

或者,您可以创建一个连接两个字段的key 过程:

(define (sort-by-maker-and-model lst)
  (sort lst
        string<?
        #:key (lambda (e) (string-append (car-maker e) " " (car-model e)))))

这应该在这里工作,但前者是一种更通用的方法。任何方式:

> (sort-by-maker-and-model list-of-cars)
(list
 (car "Bugatti" "Type 10" "1908")
 (car "Bugatti" "Type 2" "1900")
 (car "Bugatti" "Type 5" "1903")
 (car "Ferrari" "166 Inter" "1949")
 (car "Ferrari" "250 Europa GT" "1954")
 (car "Ferrari" "340 America" "1951")
 (car "Lamborghini" "Egoista" "2013")
 (car "Lamborghini" "Flying Star II" "1966")
 (car "Maserati" "5000 GT" "1959")
 (car "Maserati" "A6 1500" "1946")
 (car "Maserati" "Quattroporte" "1963"))

【讨论】:

  • 更通用的方法很好,但在我的具体数据的具体情况下,我认为第二个会更快,我可以将它应用于两个以上的领域......完美,非常感谢。
【解决方案2】:

Racket 的sort 是稳定的,所以另一种选择是调用sort 两次。当然,这需要两次通过,但这可能取决于您的目的。 (请注意,string&lt;? 不会在model 列中产生您想要的结果,因为"Type 10" 在字典上是第一位的。)

(define (sort-by-maker-and-model lst)
  (sort
   (sort lst string<? #:key car-model)
   string<? #:key car-maker))

(require rackunit)
(check-equal?
 (sort-by-maker-and-model list-of-cars)
 (list
  (car "Bugatti" "Type 10" "1908")
  (car "Bugatti" "Type 2" "1900")
  (car "Bugatti" "Type 5" "1903")
  (car "Ferrari" "166 Inter" "1949")
  (car "Ferrari" "250 Europa GT" "1954")
  (car "Ferrari" "340 America" "1951")
  (car "Lamborghini" "Egoista" "2013")
  (car "Lamborghini" "Flying Star II" "1966")
  (car "Maserati" "5000 GT" "1959")
  (car "Maserati" "A6 1500" "1946")
  (car "Maserati" "Quattroporte" "1963")))

更新:添加一些计时数据

(define (sort-by-maker-and-model lst)
  (sort
   (sort lst string<? #:key car-model)
   string<? #:key car-maker))
(define (sort-by-maker-and-model2 lst)
  (sort lst
        (lambda (e1 e2)
          (or (string<? (car-maker e1) (car-maker e2))
              (and (string=? (car-maker e1) (car-maker e2))
                   (string<? (car-model e1) (car-model e2)))))))
(define (sort-by-maker-and-model3 lst)
  (sort lst
        string<?
        #:key (lambda (e) (string-append (car-maker e) " " (car-model e)))))

(define (random-string)
  (define len (+ 4 (random 6)))
  (apply string (map integer->char (build-list len (λ _ (+ (random 26) 65))))))
(define (random-car . xs)
  (car (random-string) (random-string) (number->string (+ (random 9000) 1000))))
(let ([cars (build-list 1000000 random-car)])
  (collect-garbage)
  (collect-garbage)
  (collect-garbage)
  (void (time (sort-by-maker-and-model cars)))
  (collect-garbage)
  (collect-garbage)
  (collect-garbage)
  (void (time (sort-by-maker-and-model2 cars)))
  (collect-garbage)
  (collect-garbage)
  (collect-garbage)
  (void (time (sort-by-maker-and-model3 cars))))

自定义小于是最快的,然后是双重排序,然后是字符串附加键,这是我猜到的:

$ racket sort-cars.rkt
cpu time: 5008 real time: 5015 gc time: 76
cpu time: 1960 real time: 1967 gc time: 0
cpu time: 6633 real time: 6643 gc time: 1588

【讨论】:

  • 有趣,保存在我的示例 sn-ps 集合中以备后用。我肯定有一天会需要这个,非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-30
  • 2017-09-09
相关资源
最近更新 更多