159 views

Skip to first unread message

Jun 4, 2009, 6:06:27 PM6/4/09

to clo...@googlegroups.com, Stephen C. Gilardi

Hello Stephen,

the following is a macro defining a function with keyword

arguments. It is called like this:

(defnk foo

[a b c :x 1 :z 2]

(println x z))

a, b and c denote positional arguments, 1 and 2 are default

values for x resp. z. This pattern came up several times now,

and there were always some "aha" effects. So it may be a

nice addition to c.c.def.

Here's the macro:

(defmacro defnk

"Define a function accepting keyword arguments. Symbols up to the

first

keyword in the parameter list are taken as positional arguments.

Then

an alternating sequence of keywords and defaults values is

expected. The

values of the keyword arguments are available in the function body by

virtue of the symbol corresponding to the keyword (cf. :keys

destructuring).

defnk accepts an optional docstring as well as an optional metadata

map."

[fn-name & fn-tail]

(let [[fn-name [args & body]] (name-with-attributes fn-name fn-tail)

[pos kw-vals] (split-with symbol? args)

kw-vals (apply hash-map kw-vals)

de-map {:keys (vec (keys kw-vals))

:or kw-vals}]

`(defn ~fn-name

[~@pos & options#]

(let [~de-map (apply hash-map options#)]

~@body))))

The docstring is a bit contorted but I'm too sleepy now,

to get that right...

Sincerely

Meikel

Jun 4, 2009, 6:22:42 PM6/4/09

to clo...@googlegroups.com

Hi again,

Am 05.06.2009 um 00:06 schrieb Meikel Brandmeyer:

> The docstring is a bit contorted but I'm too sleepy now,

> to get that right...

And of course I'm too sleepy to miss the keyword to

symbol conversion....

(defmacro defnk

"Define a function accepting keyword arguments. Symbols up to the

first

keyword in the parameter list are taken as positional arguments.

Then

an alternating sequence of keywords and defaults values is

expected. The

values of the keyword arguments are available in the function body by

virtue of the symbol corresponding to the keyword (cf. :keys

destructuring).

defnk accepts an optional docstring as well as an optional metadata

map."

[fn-name & fn-tail]

(let [[fn-name [args & body]] (name-with-attributes fn-name fn-tail)

[pos kw-vals] (split-with symbol? args)

syms (map #(-> % name symbol) (take-nth 2

kw-vals))

values (take-nth 2 (rest kw-vals))

sym-vals (apply hash-map (interleave syms

values))

de-map {:keys (vec syms)

:or sym-vals}]

`(defn ~fn-name

[~@pos & options#]

(let [~de-map (apply hash-map options#)]

~@body))))

Sincerely

Meikel

Jun 4, 2009, 6:54:57 PM6/4/09

to Clojure

Gut gemacht!

Absolutely amazing Meikel. Now get some well earned sleep.

Sean

> smime.p7s

> 5KViewDownload

Absolutely amazing Meikel. Now get some well earned sleep.

Sean

> 5KViewDownload

Jun 4, 2009, 7:02:43 PM6/4/09

to clo...@googlegroups.com

On Jun 4, 2009, at 6:54 PM, Sean Devlin wrote:

> Gut gemacht!

>

> Absolutely amazing Meikel. Now get some well earned sleep.

>

> Sean

I agree. It's a really beautiful piece of code, packed full of Clojure

goodness.

Nicely done!

I checked it into clojure.contrib.def:

http://code.google.com/p/clojure-contrib/source/detail?r=889

Thanks!

--Steve

Jun 5, 2009, 8:53:24 AM6/5/09

to Clojure

I'm writing a numerical methods library, and I already found a use for

you macro. Here's the old way:

(defn solve

[f & params]

(let [param-map (merge

{:start 1

:target 0

:diff-method :forward

:iter-method :newton

:epsilon 0.00000001

:max-iters 100}

(apply hash-map params))

diff-method (param-map :diff-method)

iter-method (param-map :iter-method)

start (param-map :start)

target (param-map :target)

max-iters (param-map :max-iters)

epsilon (param-map :epsilon)

f-point #(- (f %) target)

iter-methods {:newton (fn[x]

(- x (/ (f-point x) ((f-prime f-

point :diff-method diff-method) x))))}

iterator (iter-methods iter-method)]

(loop [x start

iter-count 0]

(let [next-x (iterator x)]

(cond

(< (java.lang.Math/abs (- (f-point x) (f-point next-x)))

epsilon) next-x

(> iter-count max-iters) (do

(println "Maximum number of

iterations reached")

nil)

true (recur next-x (inc iter-count)))))))

And here's the new way with your macro

(defnk solve-key

[f

:start 1

:target 0

:diff-method :forward

:iter-method :newton

:epsilon 0.00000001

:max-iters 100]

(let [f-point #(- (f %) target)

iter-methods {:newton (fn[x]

(- x

(/ (f-point x)

((f-prime f-point :diff-method diff-method) x))))}

iterator (iter-methods iter-method)]

(loop [x start

iter-count 0]

(let [next-x (iterator x)]

(cond

(< (java.lang.Math/abs (- (f-point x) (f-point next-x)))

epsilon) next-x

(> iter-count max-iters) (do

(println "Maximum number of iterations reached")

nil)

true (recur next-x (inc iter-count)))))))

So it's paying off already!

> smime.p7s

> 3KViewDownload

you macro. Here's the old way:

(defn solve

[f & params]

(let [param-map (merge

{:start 1

:target 0

:diff-method :forward

:iter-method :newton

:epsilon 0.00000001

:max-iters 100}

(apply hash-map params))

diff-method (param-map :diff-method)

iter-method (param-map :iter-method)

start (param-map :start)

target (param-map :target)

max-iters (param-map :max-iters)

epsilon (param-map :epsilon)

f-point #(- (f %) target)

iter-methods {:newton (fn[x]

(- x (/ (f-point x) ((f-prime f-

point :diff-method diff-method) x))))}

iterator (iter-methods iter-method)]

(loop [x start

iter-count 0]

(let [next-x (iterator x)]

(cond

(< (java.lang.Math/abs (- (f-point x) (f-point next-x)))

epsilon) next-x

(> iter-count max-iters) (do

(println "Maximum number of

iterations reached")

nil)

true (recur next-x (inc iter-count)))))))

And here's the new way with your macro

(defnk solve-key

[f

:start 1

:target 0

:diff-method :forward

:iter-method :newton

:epsilon 0.00000001

:max-iters 100]

(let [f-point #(- (f %) target)

iter-methods {:newton (fn[x]

(- x

(/ (f-point x)

((f-prime f-point :diff-method diff-method) x))))}

iterator (iter-methods iter-method)]

(loop [x start

iter-count 0]

(let [next-x (iterator x)]

(cond

(< (java.lang.Math/abs (- (f-point x) (f-point next-x)))

epsilon) next-x

(> iter-count max-iters) (do

(println "Maximum number of iterations reached")

nil)

true (recur next-x (inc iter-count)))))))

So it's paying off already!

> 3KViewDownload

Jun 6, 2009, 3:54:44 AM6/6/09

to clo...@googlegroups.com

Hi,

Am 05.06.2009 um 00:22 schrieb Meikel Brandmeyer:

> sym-vals (apply hash-map (interleave syms

> values))

Ah! I always forget about zipmap...

Clojure is just fun! :)

Sincerely

Meikel

Reply all

Reply to author

Forward

0 new messages

Search

Clear search

Close search

Google apps

Main menu