Posts Tagged by Clojure

4Clojure 59 Juxtaposition

4clojure problem 59 Juxtapostion – here’s how I approached the problem (warts and all) in the hope that it helps others who are learning Clojure.

A reminder of what the juxt function does:

Takes a set of functions and returns a fn that is the juxtaposition
of those fns. The returned fn takes a variable number of args, and
returns a vector containing the result of applying each fn to the
args (left-to-right).
((juxt a b c) x) => [(a x) (b x) (c x)]

The problem was fairly easy, it just required the HOF (Higher Order Functions) abstraction of thinking of functions as arguments, like any other (numeric, string, …) argument.

First of all, I need a function that takes a variable number of arguments (0-n) – standard destructuring:

(defn myjuxt [& fs]
...

I don’t know what to do with these functions yet, so I’ll just loop through using for and return each function:

(defn myjuxt [& fs]
  (for [f fs] f))

And here you can see the returned list of function objects. Not very useful but a start:

(myjuxt + min max)
user => (#core_plus #core_min #core_max)

The question gives a good hint here: “return a new function that takes a variable number of arguments”. So do that, using the same destructuring approach for a variable number of arguments:

(defn myjuxt2 [& fs]
  (fn [& xs]
...

So I want to run each function against these arguments, so I’ll use for again:

(defn myjuxt2 [& fs]
  (ƒ [& xs]
   (for [f fs] (f xs))))
((myjuxt2 + min max) 1 2 3)
user=> (ClassCastException Cannot cast clojure.lang.ArraySeq
to java.lang.Number

Ouch! But the error message gives a hint – something is seeing an array instead of a number. My three functions want numbers and they’re getting arrays – I need apply to “strip out” the arguments:

(defn myjuxt2 [& fs]
  (fn [& xs]
   (for [f fs] (apply f xs))))
((myjuxt2 + min max) 1 2 3)
user=> (6 1 3)

And it works! Looking at the solutions I see that I could’ve used map instead of for, and #() instead of (fn), but they’re minor “golfing” details.

4clojure 100 Least Common Multiple

4clojure problem 100 – calculate the Least Common Multiple (LCM) of two or more numbers.

LCM Example

Here’s how I worked it out in the REPL, warts and all….

I first thought about using the meaning of LCM to solve this (as given in Wikipedia), ie for two numbers a and b (eg 4 and 6), find all the multiples of a (an infinite sequence), find all the multiples of b (an infinite sequence), and find the first match. But I was impatient and didn’t want to work out how to find the first match between lists, so I decided to use the GCD definition:

LCM from GCD

From there it was just a matter of balancing parentheses (thanks paredit) by breaking up the problem into stages, starting with calculating the GCD of two numbers:

(defn gcd [a b]
  (cond
   (= b 0) a
   (= a 0) b
   (> a b) (gcd b (mod a b))
   (> b a) (gcd a (mod b a))))
(gcd 15 5)
==> 5

Now get LCM working for two arguments:

(defn lcm [x y]
  (/ (* x y) (gcd x y)))
(lcm 4 6)
==> 12

Now get LCM working for more than two arguments. I remembered seeing multiple arities used to solve other 4clojure problems, so I thought I’d use the Arity-Reduce “pattern” (well I’m using apply, same idea):

(defn lcm2
  ([x y] (/ (* x y) (gcd x y)))
  ([x y & rest] (apply lcm2 (lcm2 x y) rest)))
(lcm2 5 3 7)
==> 105

4clojure doesn’t allow defn, so I then used letfn to nest my definition of GCD inside LCM:

(fn lcm3
  ([x y]
     (letfn [(gcd2 [a b]
               (cond
                (= b 0) a
                (> a b) (gcd2 b (mod a b))
                (> b a) (gcd2 a (mod b a))))]
       (/ (* x y) (gcd2 x y))))
  ([x y & rest] (apply lcm3 (lcm3 x y) rest)))
(lcm3 5 3 7)
==> 105

So my solution worked, but it wasn’t very elegant – Dacquiri wins the prize for that, again :-)

4clojure 97 Pascal’s Triangle

4clojure problem 97 Pascal’s Triangle – calculate the nth row:

Pascal's Triangle

My solution:

(defn pascal [n]
  (if (= n 1)
    [1]
    (map #(apply + %)
      (partition 2 1
        (concat [0] (pascal (- n 1)) [0])))))

(pascal 5)
user=> (1 4 6 4 1)

A negative – my solution doesn’t use tail recursion; on my laptop it works up to about n = 760:

(pascal 10e4)
user=> java.lang.StackOverflowError (NO_SOURCE_FILE:0) 

4clojure 63 Group a Sequence

My first attempt at writing up one of my 4clojure solutions. Problem 63 “Group a Sequence” requires you to re-implement group-by. As always looking at the source code is cheating :-)

Given a function f and a sequence s, write a function which returns a map. The keys should be the values of f applied to each item in s. The value at each key should be a vector of corresponding items in the order they appear in s.

(= (__ #(> % 5) [1 3 6 8]) {false [1 3], true [6 8]})

Here’s how I worked it out in the REPL, warts and all…

;; map seems a good start here

((map #(> % 5) [1 3 6 8])
;; (false false true true)

;; so I’m getting the values of fn here, I want the inputs too
;; I can’t think of a clever way to do this, I’ll just use for and list

(defn foo [f xs] (for [x xs :let [y (f x)]] (list y x)))
(foo #(> % 5) [1 3 6 8])
;; ((false 1) (false 3) (true 6) (true 8))

;; the for loop can be tightend up, I don’t need the :let

(defn foo [f xs] (for [x xs] (list (f x) x)))
(foo #(> % 5) [1 3 6 8])
;; ((false 1) (false 3) (true 6) (true 8))

;; I know I want my results as maps, so use a map instead

(defn foo [f xs] (for [x xs] {(f x) x}))
(foo #(> % 5) [1 3 6 8])
;; ({false 1} {false 3} {true 6} {true 8})

;; now I’m stuck; how do I moosh the results together?
;; I read thru all the map change fn’s in the clj cheatsheet
;; hmmm, merge-with looks promising

(defn foo [f xs] (merge-with concat (for [x xs] {(f x) x})))
(foo #(> % 5) [1 3 6 8])
;; ({false 1} {false 3} {true 6} {true 8})

;; WTF? same results. Re-RTFM on merge-with. Well, I’m going to
;; need vectors in my results to be able to concat them…

(defn foo [f xs] (merge-with concat (for [x xs] {(f x) [x]})))
(foo #(> % 5) [1 3 6 8])
;; ({false [1]} {false [3]} {true [6]} {true [8]})

;; WTF? still not mooshing together. Uh duh, fn signature takes
;; “maps” not a list. Use the “list stripper” – apply!

(defn foo [f xs] (apply merge-with concat (for [x xs] {(f x) [x]})))
(foo #(> % 5) [1 3 6 8])
;; {true (6 8), false (1 3)}

;; ha, sweetness and light! Remove the defn and submit:

(fn [f xs] (apply merge-with concat (for [x xs] {(f x) [x]})))

;; look at other’s solutions. Dacquiri as usual has a more elegant
;; solution, with a reader macro. What’s his golf handicap?!?!

#(apply merge-with concat (for [x %2] {(% x) [x]}))

Mooshing is an advanced programming technique, that can only be used after years of study and practice…

Emacs Paredit Notes for OSX

Some notes on Emacs’ Paredit mode. More so I can stop fighting with Paredit and do some Clojure programming than show any expertise – see Mudphone’s Paredit Preso for the real deal, as well as hagelb’s Paredit screencast notes and the Paredit Cheat Sheet.

  • M-(  wrap parens around something. Also for { [ “
  • M-s  remove parens from something
  • M-S-s  split sexpr into two
  • M-S-j  join sexprs
  • C-q ♦  just do what I say, dammit! force insert of paren or bracket ♦
  • C-u DEL  force delete paren or bracket

“Barfage and slurpage” ie moving parens left and right.  I had some trouble getting this going on OSX, as the terminal doesn’t seem to map control key sequences correctly. After modifying emacs (see below), I got these going:

  • C-<right>, C-<left>  move right paren right or left
  • ESC C-<right>, ESC C-<left> move left paren right or left

I wrote this elisp for my ~/.emacs.d/sonia.el (I’m using Technomancy’s Emacs Starter Kit):

(when (eq system-type 'darwin)
  (eval-after-load 'paredit
    '(progn
       ;; C-left
       (define-key paredit-mode-map (kbd "M-[ 5 d")
                   'paredit-forward-barf-sexp)
       ;; C-right
       (define-key paredit-mode-map (kbd "M-[ 5 c")
                   'paredit-forward-slurp-sexp)
       ;; ESC-C-left
       (define-key paredit-mode-map (kbd "ESC M-[ 5 d")
                   'paredit-backward-slurp-sexp)
       ;; ESC-C-right
       (define-key paredit-mode-map (kbd "ESC M-[ 5 c")
                   'paredit-backward-barf-sexp)
     )))

Lisp Parens

Next Page »