fix-point combinators in clojure - clojure

One of my favorite ways to test the power of a language I'm learning is to try and implement various fixed-point combinators. Since I'm learning Clojure (though I'm not new to lisps), I did the same for it.
First, a little "testable" code, factorial:
(def !'
"un-fixed factorial function"
(fn [f]
(fn [n]
(if (zero? n)
1
(* n (f (dec n)))))))
(defn !
"factorial"
[n]
(if (zero? n)
1
(apply * (map inc (range n)))))
For any combinator c I implement, I want to verify that ((c !') n) is equal to (! n).
We start with the traditional Y:
(defn Y
"pure lazy Y combinator => stack overflow"
[f]
(let [A (fn [x] (f (x x)))]
(A A)))
But of course Clojure is not nearly so lazy as that, so we pivot to Z:
(defn Z
"strict fixed-point combinator"
[f]
(let [A (fn [x] (f (fn [v] ((x x) v))))]
(A A)))
And indeed, it holds that (= ((Z !') n) (! n)).
Now comes my issue: I cannot get either of U or the Turing combinator (theta-v) to work correctly. I suspect with U it's a language limit, while with theta-v I'm more inclined to believe it's a misread of Wikipedia's notation:
(defn U
"the U combinator => broken???"
[f]
(f f))
(defn theta-v
"Turing fixed-point combinator by-value"
[f]
(let [A (fn [x] (fn [y] (y (fn [z] ((x x) y) z))))]
(A A)))
A sample REPL experience:
((U !') 5)
;=> Execution error (ClassCastException) at fix/!'$fn (fix.clj:55).
;=> fix$_BANG__SINGLEQUOTE_$fn__180 cannot be cast to java.lang.Number
((theta-v !') 5)
;=> Execution error (ClassCastException) at fix/theta-v$A$fn (fix.clj:36).
;=> java.lang.Long cannot be cast to clojure.lang.IFn
Can anyone explain
Why these implementations of U and theta-v are not working; and
How to fix them?

Your definition of theta-v is wrong for two reasons. The first is pretty obvious: you accept f as a parameter and then ignore it. A more faithful translation would be to use def style, as you have for your other functions:
(def theta-v
"Turing fixed-point combinator by-value"
(let [A (fn [x] (fn [y] (y (fn [z] ((x x) y) z))))]
(A A)))
The second reason is a bit sneakier: you translated λz.xxyz to (fn [z] ((x x) y) z), remembering that lisps need more parentheses to denote function calls that are implicit in lambda-calculus notation. However, you missed one set: just as x x y z would have meant "evaluate x twice, then y once, then finally return z", what you wrote means "evaluate ((x x) y), then throw away that result and return z". Adding the extra set of parentheses yields a working theta-v:
(def theta-v
"Turing fixed-point combinator by-value"
(let [A (fn [x] (fn [y] (y (fn [z] (((x x) y) z)))))]
(A A)))
and we can demonstrate that it works by calculating some factorials:
user> (map (theta-v !') (range 10))
(1 1 2 6 24 120 720 5040 40320 362880)
As for U: to use the U combinator, functions being combined must change how they self-call, meaning you would need to rewrite !' as well:
(def U [f] (f f))
(def ! (U (fn [f]
(fn [n]
(if (zero? n)
1
(* n ((f f) (dec n))))))))
Note that I have changed (f (dec n)) to ((f f) (dec n)).

Related

Clojure: succinctly forward optional values

I've written a probability function in Clojure that takes an optional hash-map of options:
(defn roll-lte
([n d] (/ n d))
([n d options]
(let [p (/ n d)
roll-type (:type options :normal)]
(cond
(= roll-type :advantage) (- (* p 2) (* p p))
(= roll-type :disadvantage) (* p p)
(= roll-type :normal) p
:else (throw (IllegalArgumentException. "Invalid roll type."))))))
This works as intended, but the idea is to write other functions that build off of this one -- for example:
(defn roll-gte
([n d] (roll-lte (- d n -1) d))
([n d options] (roll-lte (- d n -1) d options)))
The two arities in roll-lte make building off of the function awkward and repetitive, especially in cases like the above where options is simply being forwarded to roll-lte. Is there a more concise and less repetitive way to achieve this?
When I have functions with multiple arities, I usually try to have the lower-arity versions call the higher-arity versions with safe default arguments. The "main" implementation of the function usually ends up being the highest-arity body:
(defn roll-lte
([n d] (roll-lte n d nil))
([n d {:keys [type]
:or {type :normal}}]
(let [p (/ n d)]
(case type ;; used case instead of cond here
:advantage (- (* p 2) (* p p))
:disadvantage (* p p)
:normal p
(throw (IllegalArgumentException. "Invalid roll type."))))))
I also used :or in the options map destructuring above to set the default value for type, which allows the lower-arity functions to just pass a nil options map.
(defn roll-gte
([n d] (roll-gte n d nil))
([n d options] (roll-lte (- d n -1) d options)))
(roll-gte 3 4) ;=> 1/2
(roll-gte 3 4 {:type :advantage}) ;=> 3/4

repeatedly apply a function to a datastructure

I would like to apply a function some number of times to a datastructure and was wondering if there is a simpler way.
;; simple map and map-incrementing function
(def a {:a 1})
(defn incmap [x] (update-in x [:a] inc))
;; best I could come up with
(reduce (fn [m _] (incmap m)) a (range 10))
;; was hoping for something like this
(repeatedly-apply incmap a 10)
You are looking for iterate:
(iterate f x)
Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects
You just need to take the nth element:
(nth (iterate incmap a) 9)
Using the threading macro:
(-> (iterate incmap a)
(nth 9))

factorial function for Church numerals

I'm trying to implement the factorial lambda expression as described in the book Lambda-calculus, Combinators and Functional Programming
The way it's described there is :
fact = (Y)λf.λn.(((is-zero)n)one)((multiply)n)(f)(predecessor)n
Y = λy.(λx.(y)(x)x)λx.(y)(x)x
where
(x)y is equivalent to (x y) and
(x)(y)z is equivalent to (x (y x)) and
λx.x is equivalent to (fn [x] x)
and is-zero, one, multiply and predecessor are defined for the standard church numerals. Actual definitions here.
I translated that to the following
(defn Y-mine [y] ; (defn Y-rosetta [y]
((fn [x] (y (x x))) ; ((fn [f] (f f))
(fn [x] ; (fn [f]
(y ; (y (fn [& args]
(x x))))) ; (apply (f f) args))))))
and
(def fac-mine ; (def fac-rosetta
(fn [f] ; (fn [f]
(fn [n] ; (fn [n]
(is-zero n ; (if (zero? n)
one ; 1
(multiply n (f (predecessor n))))))) ; (* n (f (dec n)))))))
The commented out versions are the equivalent fac and Y functions from Rosetta code.
Question 1:
I understand from reading up elsewhere that the Y-rosetta β-reduces to Y-mine. In which case why is it preferable to use that one over the other?
Question 2:
Even if I use Y-rosetta. I get a StackOverflowError when I try
((Y-rosetta fac-mine) two)
while
((Y-rosetta fac-rosetta) 2)
works fine.
Where is the unguarded recursion happening?
I suspect it's something to do with how the if form works in clojure that's not completely equivalent to my is-zero implementation. But I haven't been able to find the error myself.
Thanks.
Update:
Taking into consideration #amalloy's answer, I changed fac-mine slightly to take lazy arguments. I'm not very familiar with clojure so, this is probably not the right way to do it. But, basically, I made is-zero take anonymous zero argument functions and evaluate whatever it returns.
(def lazy-one (fn [] one))
(defn lazy-next-term [n f]
(fn []
(multiply n (f (predecessor n)))))
(def fac-mine
(fn [f]
(fn [n]
((is-zero n
lazy-one
(lazy-next-term n f))))))
I now get an error saying:
=> ((Y-rosetta fac-mine) two)
ArityException Wrong number of args (1) passed to: core$lazy-next-term$fn clojure.lang.AFn.throwArity (AFn.java:437)
Which seems really strange considering that lazy-next-term is always called with n and f
The body of fac-mine looks wrong: it's calling (is-zero n one) for side effects, and then unconditionally calling (multiply n (f (predecessor n))). Presumably you wanted a conditional choice here (though I don't see why this doesn't throw an arity exception, given your definition of is-zero).

Why am I getting a cast error when trying to use Simpson's rule in Clojure?

I'm trying to work through some of the exercises in SICP using Clojure, but am getting an error with my current method of executing Simpson's rule (ex. 1-29). Does this have to do with lazy/eager evalution? Any ideas on how to fix this? Error and code are below:
java.lang.ClassCastException: user$simpson$h__1445 cannot be cast to java.lang.Number
at clojure.lang.Numbers.divide (Numbers.java:139)
Here is the code:
(defn simpson [f a b n]
(defn h [] (/ (- b a) n))
(defn simpson-term [k]
(defn y [] (f (+ a (* k h))))
(cond
(= k 0) y
(= k n) y
(even? k) (* 2 y)
:else (* 4 y)))
(* (/ h 3)
(sum simpson-term 0 inc n)))
You define h as a function of no arguments, and then try to use it as though it were a number. I'm also not sure what you're getting at with (sum simpson-term 0 inc n); I'll just assume that sum is some magic you got from SICP and that the arguments you're passing to it are right (I vaguely recall them defining a generic sum of some kind).
The other thing is, it's almost always a terrible idea to have a def or defn nested within a defn. You probably want either let (for something temporary or local) or another top-level defn.
Bearing in mind that I haven't written a simpson function for years, and haven't inspected this one for algorithmic correctness at all, here's a sketch that is closer to the "right shape" than yours:
(defn simpson [f a b n]
(let [h (/ (- b a) n)
simpson-term (fn [k]
(let [y (f (+ a (* k h)))]
(cond
(= k 0) y
(= k n) y
(even? k) (* 2 y)
:else (* 4 y))))]
(* (/ h 3)
(sum simpson-term 0 inc n))))

Project Euler #14 and memoization in Clojure

As a neophyte clojurian, it was recommended to me that I go through the Project Euler problems as a way to learn the language. Its definitely a great way to improve your skills and gain confidence. I just finished up my answer to problem #14. It works fine, but to get it running efficiently I had to implement some memoization. I couldn't use the prepackaged memoize function because of the way my code was structured, and I think it was a good experience to roll my own anyways. My question is if there is a good way to encapsulate my cache within the function itself, or if I have to define an external cache like I have done. Also, any tips to make my code more idiomatic would be appreciated.
(use 'clojure.test)
(def mem (atom {}))
(with-test
(defn chain-length
([x] (chain-length x x 0))
([start-val x c]
(if-let [e (last(find #mem x))]
(let [ret (+ c e)]
(swap! mem assoc start-val ret)
ret)
(if (<= x 1)
(let [ret (+ c 1)]
(swap! mem assoc start-val ret)
ret)
(if (even? x)
(recur start-val (/ x 2) (+ c 1))
(recur start-val (+ 1 (* x 3)) (+ c 1)))))))
(is (= 10 (chain-length 13))))
(with-test
(defn longest-chain
([] (longest-chain 2 0 0))
([c max start-num]
(if (>= c 1000000)
start-num
(let [l (chain-length c)]
(if (> l max)
(recur (+ 1 c) l c)
(recur (+ 1 c) max start-num))))))
(is (= 837799 (longest-chain))))
Since you want the cache to be shared between all invocations of chain-length, you would write chain-length as (let [mem (atom {})] (defn chain-length ...)) so that it would only be visible to chain-length.
In this case, since the longest chain is sufficiently small, you could define chain-length using the naive recursive method and use Clojure's builtin memoize function on that.
Here's an idiomatic(?) version using plain old memoize.
(def chain-length
(memoize
(fn [n]
(cond
(== n 1) 1
(even? n) (inc (chain-length (/ n 2)))
:else (inc (chain-length (inc (* 3 n))))))))
(defn longest-chain [start end]
(reduce (fn [x y]
(if (> (second x) (second y)) x y))
(for [n (range start (inc end))]
[n (chain-length n)])))
If you have an urge to use recur, consider map or reduce first. They often do what you want, and sometimes do it better/faster, since they take advantage of chunked seqs.
(inc x) is like (+ 1 x), but inc is about twice as fast.
You can capture the surrounding environment in a clojure :
(defn my-memoize [f]
(let [cache (atom {})]
(fn [x]
(let [cy (get #cache x)]
(if (nil? cy)
(let [fx (f x)]
(reset! cache (assoc #cache x fx)) fx) cy)))))
(defn mul2 [x] (do (print "Hello") (* 2 x)))
(def mmul2 (my-memoize mul2))
user=> (mmul2 2)
Hello4
user=> (mmul2 2)
4
You see the mul2 funciton is only called once.
So the 'cache' is captured by the clojure and can be used to store the values.