Monday, July 20, 2015

Implementing own comp function

In Chapter 5 of Clojure for the Brave and True, there is an exercise for implementing an own version of the comp function.
Here is my attempt:

Trying with some examples, it works the same way as the built-in one :)

((comp + - -) 1 2 3 4 5)
Result:
13


((my-comp + - -) 1 2 3 4 5)
Result:
13


((comp clojure.string/upper-case 
       clojure.string/lower-case
       (fn [somestr] (str "   " somestr "    "))
       clojure.string/trim)
              "heLLo")
Result:
"   HELLO    "


((my-comp clojure.string/upper-case
       clojure.string/lower-case
       (fn [somestr] (str "   " somestr "    "))
       clojure.string/trim)
              "heLLo")
Result:
"   HELLO    "



There is one special case, where my implementation behaves differently from the built-in one, and that is when there are zero functions:
((comp) 1 2 3)
Result:
ArityException Wrong number of args (3) passed to: 
core/identity  clojure.lang.AFn.throwArity (AFn.java:429)

((my-comp) 1 2 3)
Result:
NullPointerException   clojure.core/apply (core.clj:624)
So both versions leak some implementation details in this case, and cause an error...

Some final remarks:
  • the built-in version interprets the last function first; that's why I had to reverse the parameters
  • I encountered a problem, while processing the arguments: for the first function, they are in a form of a sequence, and can thus be applied; for all other functions, they are just raw values, and thus it is necessary to call the function directly (without apply)

1 comment:

  1. While redoing this chapter, I came up with another solution:
    (defn n-comp
    [& funs]
    (fn [& args]
    (loop [actargs args actfuns funs]
    (if (not (empty? actfuns))
    (recur [(apply (first actfuns) actargs)] (rest actfuns))))))

    Although it does not work exactly as the built in version, it solves the second problems (namely, sequence vs raw values) more easily: when recuring it puts the parameters into a vector!

    ReplyDelete