I wrote two functions like this, but as you see most part of them are identical, so I want to write a macro to simplify them.
I understand the simple macro examples in my textbook, but I don't know how to write my own.
Here's my code:
(defn load-dict
; "Load database from a txt file previous saved"
[fname]
(with-open [rdr (io/reader fname)]
(doseq [line (line-seq rdr)]
(println line)
(def vvv (clojure.string/split line #"\s"))
;(println (str "count of vvv is " (count vvv)))
(if (< 1 (count vvv))
(add- dict (gen-word (nth vvv 0) (nth vvv 2) (nth vvv 1))))
)))
(defn load-article
; "Load article from a txt file"
[fname]
(with-open [rdr (io/reader fname)]
(doseq [line (line-seq rdr)]
(println line)
(def vvv (clojure.string/split line #"\s"))
;(println (str "count of vvv is " (count vvv)))
(if (< 1 (count vvv))
(add- article vvv ))
)))
Should I write a macro like:
(defmacro load- [target fname &expr)
`(...
(add- ~target expr)))
I actually don't know how to write such a macro. I just hate duplicate code.
PS, the tow function work fine. I don't care about the variable this is part of the code.
I would use a let block instead of def. Using def will bind a var and define vvv in your namespace. A macro is not really needed. You could simplify your code like this:
(defn load-from
"Load database from a txt file previous saved"
[fname load-fn]
(with-open [rdr (io/reader fname)]
(doseq [line (line-seq rdr)]
(println line)
(let [vvv (clojure.string/split line #"\s")]
(when (< 1 (count vvv))
(load-fn vvv))))))
And invoke it like this
(load-from "myfile.txt" #(add- dict (apply gen-word (take 3 %))))
(load-from "myfile.txt" #(add- article %))
user1944838 is correct here in that you don't need a macro, and since macros make code ,which does not need them, slightly harder to work with in some contexts (you can't pass it to map or apply for instance), using functions is preferable in practice. Understanding how to write macros correctly is however very important.
I would write this as a template macro that binds a name you pass it to each word and then call the body you pass to the macro which would in turn use that word through the symbol name.
(defmacro with-loaded-article
[[name-for-line fname] & body]
`(with-open [rdr# (io/reader ~fname)]
(doseq [line# (line-seq rdr#)]
(println line#)
(let [~name-for-line (clojure.string/split line# #"\s")]
~#body))))
The [name-for-line fname] expression destructures the first argument into a single "binding form" that will be used to generate a symbol and the values that it will resolve to. This format is very common in "with-*" macros such as with-open except here I only take one binding form to keep the code smaller.
the rdr# and line# symbols inside the syntax-quote are a feature of syntax quote called "auto gensyms" that causes any symbol in side the syntax quote ending with # to be replaced by a unique, though consistent symbol in the resulting expression.
the ~# is the splicing-unquote feature of syntax-quotes that causes body in this case to be inserted without adding ( ) around it.
We can see how this expands with macroexpand-1 and pprint hint: (use 'clojure.pprint)
hello.core> (pprint (macroexpand-1
`(with-loaded-article [line "tmp.txt"] (println line))))
(clojure.core/with-open
[rdr__6337__auto__ (clojure.java.io/reader "tmp.txt")]
(clojure.core/doseq
[line__6338__auto__ (clojure.core/line-seq rdr__6337__auto__)]
(clojure.core/println line__6338__auto__)
(clojure.core/let
[hello.core/line (clojure.string/split line__6338__auto__ #"\s")]
(clojure.core/println hello.core/line))))
And when we run the resulting code we get the lines of the file as sequences:
hello.core> (with-loaded-article [line "tmp.txt"] (println line))
hello world
[hello world]
world hello
[world hello]
and internet hello as well
[and internet hello as well]
Related
I am generating emacs elisp code from a clojure function. I originally started off using a defmacro, but I realized since I'm going cross-platform and have to manually eval the code into the elisp environment anyway, I can just as easily use a standard clojure function. But basically what I'm doing is very macro-ish.
I am doing this because my goal is to create a DSL from which I will generate code in elisp, clojure/java, clojurescript/javascript, and maybe even haskell.
My "macro" looks like the following:
(defn vt-fun-3 []
(let [hlq "vt"]
(let [
f0 'list
f1 '(quote (defun vt-inc (n) (+ n 1)))
f2 '(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8))))]
`(~f0 ~f1 ~f2)
)))
This generates a list of two function definitions -- the generated elisp defun and a unit test:
(list (quote (defun vt-inc (n) (+ n 1))) (quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
Then from an emacs scratch buffer, I utilize clomacs https://github.com/clojure-emacs/clomacs to import into the elisp environment:
(clomacs-defun vt-fun-3 casc-gen.core/vt-fun-3)
(progn
(eval (nth 0 (eval (read (vt-fun-3)))))
(eval (nth 1 (eval (read (vt-fun-3))))))
From here I can then run the function and the unit test:
(vt-inc 4)
--> 5
(ert "vt-inc-test")
--> t
Note: like all macros, the syntax quoting and escaping is very fragile. It took me a while to figure out the proper way to get it eval properly in elisp (the whole "(quote (list..)" prefix thing).
Anyway, as suggested by the presences of the "hlq" (high-level-qualifier) on the first "let", I want to prefix any generated symbols with this hlq instead of hard-coding it.
Unfortunately, when I use standard quotes and escapes on the "f1" for instance:
f1 '(quote (defun ~hlq -inc (n) (+ n 1)))
This generates:
(list (quote (defun (clojure.core/unquote hlq) -inc (n) (+ n 1)))
(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
In other words it substitutes 'clojure.core/unquote' for "~" which is not what I want.
The clojure syntax back-quote:
f1 `(quote (defun ~hlq -inc (n) (+ n 1)))
doesn't have this problem:
(list (quote (casc-gen.core/defun vt casc-gen.core/-inc (casc-gen.core/n) (clojure.core/+ casc-gen.core/n 1))) (quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8)))))
It properly escapes and inserts "vt" as I want (I still have to work out to concat to the stem of the name, but I'm not worried about that).
Problem solved, right? Unfortunately syntax quote fully qualifies all the symbols, which I don't want since the code will be running under elisp.
Is there a way to turn off the qualifying of symbols when using the syntax quote (back tick)?
It also seems to me that the syntax quote is more "capable" than the standard quote. Is this true? Or can you, by trickery, always make the standard quote behave the same as the syntax quote? If you cannot turn off qualification with syntax quote, how could I get this working with the standard quote? Would I gain anything by trying to do this as a defmacro instead?
The worst case scenario is I have to run a regex on the generated elisp and manually remove any qualifications.
There is no way to "turn off" the qualifying of symbols when using syntax quote. You can do this however:
(let [hlq 'vt] `(~'quote (~'defun ~hlq ~'-inc (~'n) (~'+ ~'n 1))))
Which is admittedly pretty tedious. The equivalent without syntax quote is:
(let [hlq 'vt] (list 'quote (list 'defun hlq '-inc '(n) '(+ n 1))))
There is no way to get your desired output when using standard quote prefixing the entire form however.
As to the issue of using defmacro instead, as far as I understand your intentions, I don't think you would gain anything by using a macro.
Based on the input from justncon, here is my final solution. I had to do a little extra formatting to get the string concat on the function name right, but everything was pretty much like he recommended:
(defn vt-gen-4 []
(let [hlq 'vt]
(let [
f1 `(~'quote (~'defun ~(symbol (str hlq "-inc")) (~'n) (~'+ ~'n 1)))
f2 `(~'quote (~'defun ~(symbol (str hlq "-inc-test")) () (~'should (~'= (~(symbol (str hlq "-inc")) 7) 8))))
]
`(~'list ~f1 ~f2))))
What I learned:
syntax quote is the way to go, you just have to know how to control unquoting at the elemental level.
~' (tilde quote) is my friend here. Within a syntax quote expression, if you specify ~' before either a function or var it will be passed through to the caller as specified.
Take the expression (+ 1 1)
Here is a synopsis of how this expression will expand within a syntax quote expression based on various levels of escaping:
(defn vt-foo []
(println "(+ 1 1) -> " `(+ 1 1)) --> (clojure.core/+ 1 1)
(println "~(+ 1 1) -> " `~(+ 1 1)) --> 2
(println "~'(+ 1 1) -> " `~'(+ 1 1)) --> (+ 1 1)
)
The last line was what I wanted. The first line was what I was getting.
If you escape a function then do not escape any parameters you want escaped. For instance, here we want
to call the "str" function at macro expand time and to expand the variable "hlq" to it's value 'vt:
;; this works
f1 `(quote (defun ~(str hlq "-inc") ~hlq (n) (+ n 1)))
;; doesn't work if you escape the hlq:
f1 `(quote (defun ~(str ~hlq "-inc") ~hlq (n) (+ n 1)))
I guess an escape spans to everything in the unit your escaping. Typically you escape atoms (like strings or symbols), but if it's a list then everything in the list is automatically escaped as well, so don't double escape.
4) FWIW, I ended writing a regex solution before I got the final answer. It's definitely not as nice:
(defn vt-gen-3 []
(let [hlq "vt"]
(let
[
f0 'list
f1 `(quote (defun ~(symbol (str hlq "-inc")) (n) (+ n 1)))
f2 '(quote (ert-deftest vt-inc-test () (should (= (vt-inc 7) 8))))
]
`(~f0 ~f1 ~f2)
))
)
;; this strips out any qualifiers like "casc-gen.core/"
(defn vt-gen-3-regex []
(clojure.string/replace (str (vt-gen-3)) #"([\( ])([a-zA-Z0-9-\.]+\/)" "$1" ))
Macro expansion is very delicate and requires lots of practice.
I'm working through some clojure examples from braveclojure:
http://www.braveclojure.com/writing-macros/
Currently I am trying to execute this
(ns turtle (:use clojure.pprint))
(def criticisms {:good "good code:" :bad "bad code:"})
(defn criticize-code
[[critkey code]]
`(println (~critkey criticisms) (quote ~code)))
(defmacro code-critic
[code-evaluations]
`(do ~#(map criticize-code code-evaluations)))
(println "executed code critic")
(code-critic {:good (+ 1 1) :bad (1 + 1)})
(println "code critic expansion")
(pprint (macroexpand '(code-critic {:good (+ 1 1) :bad (1 + 1)})))
;why isn't this executing?
(println "criticize code expansion")
(criticize-code [:good '(+ 1 1)])
Basically, I can verify that criticize-code returns properly formatted code through println; but I cannot actually execute it...can someone please tell me what I'm doing wrong?
Thank you!
The function criticize-code is being invoked. The quasi-quote, `, in the body of the function is a reader macro for syntax-quote, which means the following println form, after its trip through the syntax-quote reader, will be returned as data structure rather than executed. The criticize-code function is semantically equivalent to
(defn criticize-code
[[critkey code]]
(list
'clojure.core/println
(list critkey 'turtle/criticisms)
(list 'quote code)))
If you want to treat the resulting data structure as code at the REPL, you can eval it directly.
turtle=> (criticize-code [:good '(+ 1 1)])
(clojure.core/println (:good turtle/criticisms) (quote (+ 1 1)))
turtle=> (eval (criticize-code [:good '(+ 1 1)]))
good code: (+ 1 1)
nil
So why would you want a function that works like this? As a helper to a macro, as here for code-critic. Macros deal with code-as-data-as-code. Therefore, if you stick in a helper function at the as-data stage, it will need to return its result as data. Otherwise, the code you want to be compiled is just executed at "compile" time with its return value (println returns nil) compiled instead.
I am trying define symbols a and b in following way
a + 1 1 b
2
I am trying to do this by using define-symbol-macro
(define-symbol-macro a '( )
(define-symbol-macro b ') )
but this way is not working.
What Lisp does with source code
Common Lisp is an incredibly flexible language, in part because its source code can be easily represented using the same data structures that are used in the language. The most common form of macro expansion transforms the these structures into other structures. These are the kind of macros that you can define with define-symbol-macro, define-compiler-macro, defmacro, and macrolet. Before any of those kind of macroexpansions can be performed, however, the system first needs to read the source from an input stream (typically a file, or an interactive prompt). That's the reader's responsibility. The reader also is capable of executing some special actions when it encounters certain characters, such ( and '. What you're trying to do probably needs to be happening down at the reader level, if you want to have, e.g., (read-from-string "a + 1 1 b") return the list (+ 1 1), which is what you want if you want (eval (read-from-string "a + 1 1 b")) to return 2. That said, you could also define a special custom language (like loop does) where a and b are treated specially.
Use set-macro-character, not define-symbol-macro
This isn't something that you would do using symbol-macros, but rather with macro characters. You can set macro characters using the aptly named set-macro-character. For instance, in the following, I set the macro character for % to be a function that reads a list, using read-delimited-list that should be terminated by ^. (Using the characters a and b here will prove very difficult, because you won't be able to write things like (set-macro-character ...) afterwards; it would be like writing (set-m(cro-ch(r(cter ...), which is not good.)
CL-USER> (set-macro-character #\% (lambda (stream ignore)
(declare (ignore ignore))
(read-delimited-list #\^ stream)))
T
CL-USER> % + 1 1 ^
2
The related set-syntax-from-char
There's a related function that almost does what you want here, set-syntax-from-char. You can use it to make one character behave like another. For instance, you can make % behave like (
CL-USER> (set-syntax-from-char #\% #\()
T
CL-USER> % + 1 1 )
2
However, since the macro character associated with ( isn't looking for a character that has the same syntax as ), but an actual ) character, you can't simply replace ) with ^ in the same way:
CL-USER> (set-syntax-from-char #\^ #\))
T
CL-USER> % + 1 1 ^
; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "unmatched close parenthesis" {1002C66031}>.
set-syntax-from-char is more useful when there's an existing character that, by itself does something that you want to imitate. For instance, if you wanted to make ! an additional quotation character:
CL-USER> (set-syntax-from-char #\! #\')
T
CL-USER> (list !a !(1 2 3))
(A (1 2 3))
or make % be a comment character, like it is in LaTeX:
CL-USER> (set-syntax-from-char #\% #\;)
T
CL-USER> (list 1 2 % 3 4
5 6)
(1 2 5 6)
But consider why you're doing this at allā¦
Now, even though you can do all of this, it seems like something that would be utterly surprising to anyone who ran into it. (Perhaps you're entering an obfuscated coding competition? ;)) For the reasons shown above, doing this with commonly used characters such as a and b will also make it very difficult to write any more source code. It's probably a better bet to define an entirely new readtable that does what you want, or even write a new parser. even though (Common) Lisp lets you redefine the language, there are still things that it probably makes sense to leaveĀ alone.
A symbol-macro is a symbol that stands for another form. Seems like you want to look at reader macros.
http://clhs.lisp.se/Body/f_set__1.htm
http://dorophone.blogspot.no/2008/03/common-lisp-reader-macros-simple.html
I would second Rainer's comment though, what are you trying to make?
Ok so I love your comment on the reason for this and now I know this is for 'Just because it's lisp' then I am totally on board!
Ok so you are right about lisp being great to use to make new languages because we only have to 'compile' to valid lisp code and it will run. So while we cant use the normal compiler to do the transformation of the symbols 'a and 'b to brackets we can write this ourselves.
Ok so lets get started!
(defun symbol-name-equal (a b)
(and (symbolp a) (symbolp b) (equal (symbol-name a) (symbol-name b))))
(defun find-matching-weird (start-pos open-symbol close-symbol code)
(unless (symbol-name-equal open-symbol (nth start-pos code))
(error "start-pos does not point to a weird open-symbol"))
(let ((nest-index 0))
(loop :for item :in (nthcdr start-pos code)
:for i :from start-pos :do
(cond ((symbol-name-equal item open-symbol) (incf nest-index 1))
((symbol-name-equal item close-symbol) (incf nest-index -1)))
(when (eql nest-index 0)
(return i))
:finally (return nil))))
(defun weird-forms (open-symbol close-symbol body)
(cond ((null body) nil)
((listp body)
(let ((open-pos (position open-symbol body :test #'symbol-name-equal)))
(if open-pos
(let ((close-pos (find-matching-weird open-pos open-symbol close-symbol body)))
(if close-pos
(weird-forms open-symbol close-symbol
`(,#(subseq body 0 open-pos)
(,#(subseq body (1+ open-pos) close-pos))
,#(subseq body (1+ close-pos))))
(error "unmatched weird brackets")))
(if (find close-symbol body :test #'symbol-name-equal)
(error "unmatched weird brackets")
(loop for item in body collect
(weird-forms open-symbol close-symbol item))))))
(t body)))
(defmacro with-weird-forms ((open-symbol close-symbol) &body body)
`(progn
,#(weird-forms open-symbol close-symbol body)))
So there are a few parts to this.
First we have (symbol-name-equal), this is a helper function because we are now using symbols and symbols belong to packages. symbol-name-equal gives us a way of checking if the symbols have the same name ignoring what package they reside in.
Second we have (find-matching-weird). This is a function that takes a list and and index to an opening weird bracket and returns the index to the closing weird bracket. This makes sure we get the correct bracket even with nesting
Next we have (weird-forms). This is the juicy bit and what it does is to recursively walk through the list passed as the 'body' argument and do the following:
If body is an empty list just return it
if body is a list then
find the positions of our open and close symbols.
if only one of them is found then we have unmatched brackets.
if we find both symbols then make a new list with the bit between the start and end positions inside a nested list.
we then call weird forms on this result in case there are more weird-symbol-forms inside.
there are no weird symbols then just loop over the items in the list and call weird-form on them to keep the search going.
OK so that function transforms a list. For example try:
(weird-forms 'a 'b '(1 2 3 a 4 5 b 6 7))
But we want this to be proper lisp code that executes so we need to use a simple macro.
(with-weird-forms) is a macro that takes calls the weird-forms function and puts the result into our source code to be compiled by lisp. So if we have this:
(with-weird-forms (a b)
(+ 1 2 3 a - a + 1 2 3 b 10 5 b 11 23))
Then it macroexpands into:
(PROGN (+ 1 2 3 (- (+ 1 2 3) 10 5) 11 23))
Which is totally valid lisp code, so it will run!
CL-USER> (with-weird-forms (a b)
(+ 1 2 3 a - a + 1 2 3 b 10 5 b 11 23))
31
Finally if you have settled on the 'a' and 'b' brackets you could write another little macro:
(defmacro ab-lang (&rest code)
`(with-weird-forms (a b) ,#code))
Now try this:
(ab-lang a let* a a d 1 b a e a * d 5 b b b a format t "this stupid test gives: ~a" e b b)
Cheers mate, this was great fun to write. Sorry for dismissing the problem earlier on.
This kind of coding is very important as ultimately this is a tiny compiler for our weird language where symbols can be punctuation. Compilers are awesome and no language makes it as effortless to write them as lisp does.
Peace!
I'm trying to run a simple query using Clojure+Korma to extract the number of records.
This is what I'm trying to do:
(defmacro number-of [ref & filter]
`(let [basetmp# (-> (kc/select* ~ref)
(kc/aggregate (count :*) :cnt))]
(if ~filter
(-> basetmp#
(kc/where ~filter))
basetmp#)))
However if I try to use this macro I get an error message saying:
Wrong number of args (2) passed to: core$count
The query would perfectly work if executed in a function but there's something wrong/missing
in the macro I cannot spot :(
Thanks,
Nico
As noted by ponzao you are picking up the wrong count.
Looking at the macroexpansion
(number-of 'foo) ;; expands to....
(clojure.core/let [basetmp__9167__auto__ (clojure.core/->
(korma.core/select* 'foo)
(korma.core/aggregate
(clojure.core/count :*)
:cnt))]
(if nil
(clojure.core/-> basetmp__9167__auto__ (korma.core/where nil))
basetmp__9167__auto__))
So you need to prevent the count in your macro being expanded as clojure.core/count, you can do this with a unquote/quote thus:
(defmacro number-of [ref & filter]
`(let [basetmp# (-> (kc/select* ~ref)
(kc/aggregate (~'count :*) :cnt))]
(if ~filter
(-> basetmp#
(kc/where ~filter))
basetmp#)))
Which then expands as expected...
(clojure.core/let [basetmp__9137__auto__ (clojure.core/->
(korma.core/select* 'foo)
(korma.core/aggregate
(count :*)
:cnt))]
(if nil
(clojure.core/-> basetmp__9137__auto__ (korma.core/where nil))
basetmp__9137__auto__))
The resultant SQL looks reasonable:
(kc/as-sql (number-of 'foo))
"SELECT COUNT(*) \"cnt\" FROM \"foo\""
UPDATE:
From the comments "What does the count actually represent?" - If you realize that kc/aggregate is also a macro and that the arguments are a 'SQL aggregate' DSL of sorts, then you can expand the kc/aggregate call too. You find there's a function, parse-aggregate in engine.clj where it eventually maps to korma.sql.fn/agg-count:
(clojure.core/let [q__2640__auto__ (kc/select* 'foo)]
(korma.sql.engine/bind-query
q__2640__auto__
(clojure.core/let [res__2641__auto__ (korma.core/fields
q__2640__auto__
[(clojure.core/->
q__2640__auto__
(korma.sql.fns/agg-count
:*))
:cnt])]
(if nil
(korma.core/group res__2641__auto__ nil)
res__2641__auto__))))
I want to do a macro in common lisp which is supposed to take in one of its arguments a list made of slots and strings. Here is the prototype :
(defclass time-info ()
((name :initarg name)
(calls :initarg calls)
(second :initarg second)
(consing :initarg consing)
(gc-run-time :initarg gc-run-time)))
(defun print-table (output arg-list time-info-list) ())
The idea is to print a table based on the arg-list which defines its structure. Here is an example of a call to the function:
(print-table *trace-output*
'("|" name "||" calls "|" second "\")
my-time-info-list)
This print a table in ascII on the trace output. The problem, is that I don't know how to explicitely get the elements of the list to use them in the different parts of my macro.
I have no idea how to do this yet, but I'm sure it can be done. Maybe you can help me :)
I would base this on format. The idea is to build a format string
from your arg-list.
I define a helper function for that:
(defun make-format-string-and-args (arg-list)
(let ((symbols ()))
(values (apply #'concatenate 'string
(mapcar (lambda (arg)
(ctypecase arg
(string
(cl-ppcre:regex-replace-all "~" arg "~~"))
(symbol
(push arg symbols)
"~a")))
arg-list))
(nreverse symbols))))
Note that ~ must be doubled in format strings in order to escape them.
The printing macro itself then just produces a mapcar of format:
(defmacro print-table (stream arg-list time-info-list)
(let ((time-info (gensym)))
(multiple-value-bind (format-string arguments)
(make-format-string-and-args arg-list)
`(mapcar (lambda (,time-info)
(format ,stream ,format-string
,#(mapcar (lambda (arg)
(list arg time-info))
arguments)))
,time-info-list)))
You can then call it like this:
(print-table *trace-output*
("|" name "||" calls "|" second "\\")
my-time-info-list)
Please note the following errors in your code:
You need to escape \ in strings.
Second is already a function name exported from the common-lisp
package. You should not clobber that with a generic function.
You need to be more precise with your requirements. Macros and Functions are different things. Arrays and Lists are also different.
We need to iterate over the TIME-INFO-LIST. So that's the first DOLIST.
The table has a description for a line. Each item in the description is either a slot-name or a string. So we iterate over the description. That's the second DOLIST. A string is just printed. A symbol is a slot-name, where we retrieve the slot-value from the current time-info instance.
(defun print-table (stream line-format-description time-info-list)
(dolist (time-info time-info-list)
(terpri stream)
(dolist (slot-or-string line-format-description)
(princ (etypecase slot-or-string
(string slot-or-string)
(symbol (slot-value time-info slot-or-string)))
stream))))
Test:
> (print-table *standard-output*
'("|" name "||" calls "|" second "\\")
(list (make-instance 'time-info
:name "foo"
:calls 100
:second 10)
(make-instance 'time-info
:name "bar"
:calls 20
:second 20)))
|foo||100|10\
|bar||20|20\
First, you probably don't want the quote there, if you're using a macro (you do want it there if you're using a function, however). Second, do you want any padding between your separators and your values? Third, you're probably better off with a function, rather than a macro.
You also seem to be using "array" and "list" interchangeably. They're quite different things in Common Lisp. There are operations that work on generic sequences, but typically you would use one way of iterating over a list and another to iterate over an array.