Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top
(define (merge-sort lst (lt? <))
  (define (truthy=? a b)
    (eq? (not a) (not b)))

  (let sort ((lst lst)
             (size (length lst))
             (flip #f))
    (define (merge l r (res '()))
      (cond ((null? l) (append-reverse r res))
            ((null? r) (append-reverse l res))
            ((truthy=? (lt? (car r) (car l)) flip)
             (merge l (cdr r) (cons (car r) res)))
            (else
             (merge (cdr l) r (cons (car l) res)))))

    (if (<= size 1) lst
        (let*-values (((mid) (quotient size 2))
                      ((l r) (split-at lst mid)))
          (merge (sort l mid (not flip))
                 (sort r (- size mid) (not flip)))))))

Tested with Racket and Guile; requires SRFIs 1 and 11. (If you want to use this for Guile, you will need to adjust the syntax used for optional arguments.)

This version is tailored for Scheme in a number of ways:

  • The length of the list is calculated only once, at the beginning. At each split, the length of the split parts is passed to each recursive call.
  • The merge step uses a right-unfold rather than a left-unfold, to be tail-recursive. This results in a reversed list at each step.
  • Rather than reverse the list at each merge, I just keep a flag saying whether the lists are reversed. The merging step takes this flag into account and then does the right kind of merge to maintain sort stability.
share|improve this question
    
I'm not entirely certain of the protocol here, but are you actually asking a question, or do you just want a general critique? – Robert Harvey Jun 18 '12 at 19:53
    
@RobertHarvey I'm looking for general critique of style, as well as ways to make the code even more performant (algorithmically speaking, not so much micro-optimisation-wise). I actually do have a more performant version of this code, which I'll post as an answer. Soon. :-) – Chris Jester-Young Jun 18 '12 at 19:55
    
Since you bother to call out the gotcha with lt?, why not fix the code like so: (eq? (and (lt? (car r) (car l)) #t) flip) – Martin Neal Jul 11 '12 at 18:13
    
@MartinNeal Mostly because, if I'm going there, I may as well define an XNOR operation. Which I'm still considering doing. :-) But actually, I think I can adapt your idea to use not instead, so that instead of using (and foo #t), I'm just using (not foo) (and swapping the branches, of course). Edit coming up. – Chris Jester-Young Mar 23 '14 at 16:12
    
I ended up creating a truthy=? that uses not. It's like XNOR with a more readable name. :-D – Chris Jester-Young Mar 23 '14 at 16:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.