Saturday, October 8, 2016

TIL: multiline comments

It is possible to form a multiline comment, with the comment function:


(comment

; won't be executed 
;      V
(defn somefunc ; ...
;...

)

There is a caveat, though: code inside the comment must be valid Clojure code!


Update: there is a way to work around the above issue: put the commented out stuff in quotes

(comment "

; won't be executed 
;      V
(defn somefunc ; ...
;...

")

map returns a lazy sequence

Consider the following code:


(defn assertFuzzyEquals [act exp]
  (do (println "asserting equality over " act exp)
  (let [inrange (<= (Math/abs (- act exp)) 1e-2)]
    (if (= inrange false)
      (println "abs(actual - expected) must be <= 1e-2. Expected was " exp " but got: " act))
    (is (= inrange true))))
)


(defn assertVectorsFuzzyEqual [act exp]
  (do (println "asserting fuzzy equality over" act exp)
  (map assertFuzzyEquals act exp))
)

(deftest a-test5
  (testing "Solve quadratic equations"
          (assertVectorsFuzzyEqual [] [])
          (assertVectorsFuzzyEqual [1] [1])
          (assertVectorsFuzzyEqual [1] [2])
          (assertVectorsFuzzyEqual [1] [1 1])
))

The output will be as follows:

asserting fuzzy equality over [] []
asserting fuzzy equality over [1] [1]
asserting fuzzy equality over [1] [2]
asserting fuzzy equality over [1] [1 1]

Ran 1 tests containing 0 assertions.
0 failures, 0 errors.

Why? Because, as the documentation of 'map' states, "[It] returns a lazy sequence consisting of the result of applying f to the set of first items of each coll, followed by applying f to the set of second items in each coll, until any one of the colls is exhausted." (Emphasis mine.)

What does this mean? Well, it means that in our case, assertFuzzyEquals was never called.

How can the realization of a lazy sequence be forced? Using doall. Let's modify the code accordingly:


; ...

(defn assertVectorsFuzzyEqual [act exp]
  (do (println "asserting fuzzy equality over" act exp)
  (doall (map assertFuzzyEquals act exp)))                ;  (1)
)

; ...


By using doall at (1), we force the sequence to be realized, and thus really execute the comparison.

The output is now as follows:

asserting fuzzy equality over [] []
asserting fuzzy equality over [1] [1]
asserting equality over  1 1
asserting fuzzy equality over [1] [2]
asserting equality over  1 2
abs(actual - expected) must be <= 1e-2. Expected was  2  but got:  1

lein test :only breaking.core-test/a-test5

FAIL in (a-test5) (core_test.clj:10)
Solve quadratic equations
expected: (= inrange true)
  actual: (not (= false true))
asserting fuzzy equality over [1] [1 1]
asserting equality over  1 1

Ran 1 tests containing 3 assertions.
1 failures, 0 errors.
Tests failed.

As we expected. (N.B.: It is another story, that two vectors with different lengths are not detected... yet!)

Thursday, January 28, 2016

Understanding lazy sequences

The book The Joy of Clojure, Second Edition shows the following quick-sort example for lazy sequences:
(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));
 }

}