(defn sort-parts [work]
(lazy-seq
(loop [[part & parts] work] ;; Pull apart work - note: work will be a list of lists.
(if-let [[pivot & xs] (seq part)] ;; This blows up unless work was a list of lists.
(let [smaller? #(< % pivot)] ;; define pivot comparison function.
(recur (list*
(filter smaller? xs) ;; work all < pivot
pivot ;; work pivot itself
(remove smaller? xs) ;; work all > pivot
parts))) ;; concat parts
(when-let [[x & parts] parts] ;; sort rest if more parts
(cons x (sort-parts parts)))))))
;; #'joy.q/sort-parts
(defn qsort [xs]
(sort-parts (list xs))) ;; The (list) function ensures that we pass sort-parts a list of lists.
In order to better understand, how lazy sequence works, I implemented an equivalent for myself in Java:
public interface NextValueProducer {
Object getNextValue();
}
import java.util.ArrayList;
import java.util.List;
public class LazySequence {
private NextValueProducer nvp;
private List cache;
public LazySequence(NextValueProducer nvp) {
this.nvp = nvp;
cache = new ArrayList<>();
}
public Object getNthElement(int n) {
if (n < 0) {
throw new IllegalArgumentException("n has to be positive");
}
for (int i = cache.size(); i <= n; ++i) {
Object produced = nvp.getNextValue();
if (produced == null) {
break;
}
else {
cache.add(produced);
}
}
if (cache.size() > n) {
return cache.get(n);
}
else {
return null;
}
}
}
import java.util.ArrayList;
import java.util.List;
public class QuickSortProducer implements NextValueProducer {
private List<Object> work;
public QuickSortProducer(List nums) {
work = new ArrayList<>();
List temp = new ArrayList<>(nums);
work.add(temp);
}
@Override
public Object getNextValue() {
List workCopy = new ArrayList<>(work);
while (true) {
List part = new ArrayList<>((List)workCopy.get(0));
List parts = new ArrayList<>(workCopy.subList(1, workCopy.size()));
if (part.size() > 0) {
int pivot = (int)part.get(0);
List xs = part.subList(1, part.size());
List smallers = new ArrayList<>();
List biggers = new ArrayList<>();
for (Object xObj : xs) {
int x = (int)xObj;
if (x < pivot) {
smallers.add(x);
}
else if (x > pivot) {
biggers.add(x);
}
}
workCopy = new ArrayList<>();
workCopy.add(smallers);
workCopy.add(pivot);
workCopy.add(biggers);
workCopy.addAll(parts);
}
else {
if (parts.size() > 0) {
work = new ArrayList<>(parts.subList(1, parts.size()));
return parts.get(0);
}
else {
return null;
}
}
}
}
}
Unit tests:
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class QuickSortProducerTest {
@Test
public void testQuickSortEmptyList() {
List numsToSort = new ArrayList<>();
LazySequence seq = new LazySequence(new QuickSortProducer(numsToSort));
Object elem = seq.getNthElement(0);
assertNull(elem);
}
@Test
public void testQuickSortOneElementList() {
List numsToSort = new ArrayList<>();
numsToSort.add(2);
LazySequence seq = new LazySequence(new QuickSortProducer(numsToSort));
assertEquals(2, seq.getNthElement(0));
assertEquals(null, seq.getNthElement(1));
}
@Test
public void testQuickSortUnsortedList() {
List numsToSort = new ArrayList<>();
numsToSort.add(2);
numsToSort.add(1);
numsToSort.add(4);
numsToSort.add(3);
LazySequence seq = new LazySequence(new QuickSortProducer(numsToSort));
assertEquals(1, seq.getNthElement(0));
assertEquals(2, seq.getNthElement(1));
assertEquals(3, seq.getNthElement(2));
assertEquals(4, seq.getNthElement(3));
assertEquals(null, seq.getNthElement(4));
}
}