通过多个键对Racket中的结构列表进行排序

时间:2015-11-11 11:01:14

标签: sorting struct scheme racket

我有list-of-cars,类型为car的结构列表,其字段为“maker”,“model”和“year”。使用常规的Racket sort函数,我可以按一个键排序(例如“maker”)。但是,我如何按制造商和模型进行排序,并在示例中提出一个等于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"))

2 个答案:

答案 0 :(得分:4)

您需要创建自己的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)))))))

或者,你可以创建一个连接2个字段的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"))

答案 1 :(得分:2)

球拍的sort是稳定的,另一种选择是两次拨打sort。当然这需要两次通过,但根据您的目的可能会很好。 (请注意,string<?不会在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