But where is the paper that argues that fexprs are sometimes better than macros because they are more flexible and powerful than macros? Or easier to use, learn, etc? It sounds like Mr. Majorinc is confident enough that he should be able to set about writing that paper. I'm looking forward to it!
;============================
I define macro (at-least-two arg1 ... argn) that returns true if and only if at least two of args evaluate to the true. Macro is needed because we want lazy evaluation of arg1 to argn, like in OR or AND.
Newlisp (17 tokens, very simple)
(define-macro (at-least-two) (let (c) (doargs (i (= c 2)) (if (eval i) (inc c))) (>= c 2)))
It has 38 tokens, and I think it is significantly more complicated (and not the first class.)
------------------------------------------------
(B) Fexprs are frequently described as "dangerous." Practice of Newlispers seems to be different: there are no complains. Probably because Newlispers typically use lexical / static scope constructs named "contexts."
However, even without lexical scope, I believe that fexprs are pretty safe.
(B1) Is anyone able to find the expression that breaks at-least-two macro?
(B2) Is anyone able to find the expression that breaks at-least-two macro after trivial name mangling, for example, replacing i with at-least-two-i.
(B3) Is anyone able to find the expression that breaks at-least-two macro after single application of my function protect2 from my Newlisp library www.instprog.com, explained at my blog kazimirmajorinc.blogspot.com, post "Don't fear Dynamic Scope (2)"
------------------------------------------------ (C) I define more general macro at-least that should accept expressions of the following kind:
> But where is the paper that argues that fexprs are sometimes > better than macros because they are more flexible and > powerful than macros? Or easier to use, learn, etc? It sounds > like Mr. Majorinc is confident enough that he should be > able to set about writing that paper. I'm looking forward to it!
> ;============================
> I define macro (at-least-two arg1 ... argn) > that returns true if and only if at least > two of args evaluate to the true. Macro is > needed because we want lazy evaluation > of arg1 to argn, like in OR or AND.
> Newlisp (17 tokens, very simple)
> (define-macro (at-least-two) > (let (c) > (doargs (i (= c 2)) > (if (eval i) > (inc c))) > (>= c 2)))
Even if Newlisp names it a macro, it is not a macro. It is a FEXPR. A Macro would do source transformation. Above does not do any source transformation.
From Wikipedia, Macro
A macro in computer science is a rule or pattern that specifies how a certain input sequence (often a sequence of characters) should be mapped to an output sequence (also often a sequence of characters) according to a defined procedure. The mapping process which instantiates a macro into a specific output sequence is known as macro expansion.
Your code does not map an input sequence to an output sequence, thus it is no macro and the form should be named DEFINE-FEXPR.
Well, your version iterates over all forms, counts the true values and then returns if the sum is equal or greater to 2. So, it does NOT stop when 2 is already reached.
Let me change my version so that it does the same:
(defmacro at-least (n &rest es &aux (c (gensym))) `(let ((,c 0)) ,@(loop for e in es collect `(when ,e (incf ,c))) (>= ,c ,n)))
On 2009-01-23, Majorinc Kazimir <fa...@email.address> wrote:
> ;============================
> Pascal:
> But where is the paper that argues that fexprs are sometimes > better than macros because they are more flexible and > powerful than macros? Or easier to use, learn, etc? It sounds > like Mr. Majorinc is confident enough that he should be > able to set about writing that paper. I'm looking forward to it!
Pascal didn't write that.
> Newlisp (17 tokens, very simple)
Well, we have to give him credit for counting tokens. J. Harrop counts characters:
[He can bore with Eff Sharp. He can snooze with Oh Camel. He measures code size at the character level.]
So, token count translates to flexiblity and power. Hmm!
> (define-macro (at-least-two) > (let (c)
You didn't initialize c to zero, and you're incrementing it. You mean nil is zero in newLISP? Or uninitialized variables are 0 instead of nil?
> (doargs (i (= c 2))
So trailing args are implicit, like "$@" in the Bourne shell.
> (if (eval i) > (inc c))) > (>= c 2)))
Why the double evaluation of the condition with respect to C?
(defun at-least-n (n-func &rest funcs) (loop with limit = (funcall n-func) with c = 0 for f in funcs when (funcall f) do (incf c) when (>= c limit) do (return t)))
There is no need to have functions that take unevaluated arguments, because you can make a special operator like LAMBDA-CALL that gives you syntactic sugar into turning forms into the bodies of simple functions, and passing the resulting functions to a function. The function then uses FUNCALL to ``evaluate'' these compiled bodies rather than EVAL. (Above, the function /is/ FUNCALL).
Though lambda-call isn't first class, it doesn't matter because it is one of a kind. You would be indirecting upon different functions, not upon different operators similar to LAMBDA-CALL.
> (B) Fexprs are frequently described as "dangerous." > Practice of Newlispers seems to be different: > there are no complains.
You could dunk a newlisper into a vat of shit up to just below the level of his nostrils and he wouldn't dare move, or open his mouth to complain. :)
> (B1) Is anyone able to find the expression that > breaks at-least-two macro?
You did, apparently, see below:
> (B3) Is anyone able to find the expression that > breaks at-least-two macro after single > application of my function protect2 from
Otherwise why would you need protect2. Is there going to be a protect3 to protect protect2? ;)
On 2009-01-23, Rainer Joswig <jos...@lisp.de> wrote:
> In article <gld9gr$m0...@ss408.t-com.hr>, > Majorinc Kazimir <fa...@email.address> wrote: >> (define-macro (at-least-two) >> (let (c) >> (doargs (i (= c 2)) >> (if (eval i) >> (inc c))) >> (>= c 2))) > Well, your version iterates over all forms, counts the true values > and then returns if the sum is equal or greater to 2. So, it does NOT > stop when 2 is already reached.
I thought that for a second too, but it looks as if the DOARGS form used in the newLISP ``macro'' definition takes the second form as an extra guard:
(doargs (i (= i 2) ...) (>= i 2)
I.e. `(doargs (,var ,until-expr) ...). So DOARGS has a double condition for terminating: implicitly running out of args, or the expression becoming true.
The code is slightly confusing because he it repeats the test again. After the execution of the loop, it's not clear whether it terminated because it ran out of arguments, or because i reached the maximum value.
It's like those silly C programs that do:
for (i = 0; i < N; i++) { /* ... */ if (found) break; }
> for (i = 0; i < N; i++) { > /* ... */ > if (found) > break; > }
> if (i == N) { > /* ooops not found */ > }
> :)
Duh... :) - It's.... just.... that today I wrote such code, for upcoming patch for our game, hehe...
Off course I know that i would be N if nothing was found, right after the loop, at least in most "C" compilers that would be the case (compilers that I know, used, etc), but at least for one such compiler and specific optimization options that wasn't the case - on it, i was equal to N-1 after the cycle (or N+1, can't remmember) - I think it was a SH3 CodeWarrior compiler that we used for early Dreamcast dev. But memory might play tricks on me.
Actually I don't think the "C" standard covers that case, it's implementation-specific (which means undefined, and UP-TO-YOU!), so generally it's bad code, and I wrote such code just yesterday (yet I'm relieved, as much of the codebase uses such style, so if it breaks, it'll break many places).
>> for (i = 0; i < N; i++) { >> /* ... */ >> if (found) >> break; >> }
>> if (i == N) { >> /* ooops not found */ >> }
>> :)
> Duh... :) - It's.... just.... that today I wrote such code, for upcoming > patch for our game, hehe...
> Off course I know that i would be N if nothing was found, right after > the loop, at least in most "C" compilers that would be the case > (compilers that I know, used, etc), but at least for one such compiler > and specific optimization options that wasn't the case - on it, i was > equal to N-1 after the cycle (or N+1, can't remmember) - I think it was > a SH3 CodeWarrior compiler that we used for early Dreamcast dev.
That's a ridiculous bug. I've had a few run-ins with Metrowerks warez. One one project I was helping some developers port some our code to a platform using some CW IDE. The stupid thing insisted on compiling everything with the same compiler, either C or C++. Global switch! It wouldn't look at the .cc and .c suffixes of the files. The target was in the mc68K (old PalmOS). The compiler wouldn't emit larger than 16 bit jumps between functions, so object files couldn't compile to more than 64K of code. A couple of our large C++ modules had to be split into separate .cc files. IIRC, I put my foot down about that; and had it implemented as multiple translation units in one source file with some #ifdefs and multiple compiler passes, to keep the file intact for the sake of merges and such.
> Actually I don't think the "C" standard covers that case, it's
When you speak about a programming language standard without having seen so much as its cover page, sorry, that's not thinking!
Given a loop like for (i = 0; i < N; i++), provided that the loop does not have some early break, and does not mess with the value of i; and provided that the value N is not out of the range of the type of i; after the execution of that loop, i must have the value N! The predicate i == N is one of the basic post-conditions of the loop, whose body keeps executing and incrementing i while i < N.
On the last iteration, i is N-1 and the body is executed, and then the increment step, which brings i to N. The guard is then evaluated (i < N) and found to be false because i is N; the loop then terminates without doing anything more to i.
> implementation-specific (which means undefined, and UP-TO-YOU!), so
Undefined does not mean up to you, it means up to your compiler implementors, or else up to pure luck. Definitely not you, the programmer.
> When you speak about a programming language standard without having seen > so much as its cover page, sorry, that's not thinking!
True! We did have some local copy of C++ standard, but even if I read it, it won't help us much - either the compiler wasn't following anything, or once you learn it - you'll be always having the feeling being a prick about why the compiler is stupid enough not to fix it. There are even C/C++ compilers that claim to be 100% ANSI C, but then again they either are not available, too obscure, or do not generate optimized code.
I've actually stopped liking "C", much more because of "C++" and the pressure it puts on people trying to grok it fully. (In fact I love "C", but at work, I'm more or less forced to use "C++" and a "C++" without exceptions and RTTI in the runtime (though we could use the latter only for tools).
> Given a loop like for (i = 0; i < N; i++), provided that the loop does not have > some early break, and does not mess with the value of i; and provided that the > value N is not out of the range of the type of i; after the execution of that > loop, i must have the value N! The predicate i == N is one of the basic > post-conditions of the loop, whose body keeps executing and incrementing i > while i < N.
If that's in the standard, then I guess I was blindly coding the correct stuff. Is there any reference online, where I can verify that (I'm simply curious).
> On the last iteration, i is N-1 and the body is executed, and then the > increment step, which brings i to N. The guard is then evaluated (i < N) and > found to be false because i is N; the loop then terminates without doing > anything more to i.
>> implementation-specific (which means undefined, and UP-TO-YOU!), so
That was really a joke (UP-TO-YOU) - it simply meant - do whatever you need to do, just make this code work for the platforms you are targeting (in our cases - that's mostly game consoles and PC).
> Undefined does not mean up to you, it means up to your compiler implementors, > or else up to pure luck. Definitely not you, the programmer.
Yup.
Btw, I've learned quite a lot from your latest posts!
> On 2009-01-23, Rainer Joswig <jos...@lisp.de> wrote:
> > In article <gld9gr$m0...@ss408.t-com.hr>, > > Majorinc Kazimir <fa...@email.address> wrote: > >> (define-macro (at-least-two) > >> (let (c) > >> (doargs (i (= c 2)) > >> (if (eval i) > >> (inc c))) > >> (>= c 2))) > > Well, your version iterates over all forms, counts the true values > > and then returns if the sum is equal or greater to 2. So, it does NOT > > stop when 2 is already reached.
> I thought that for a second too, but it looks as if the DOARGS form used in the > newLISP ``macro'' definition takes the second form as an extra guard:
> (doargs (i (= i 2) ...) > (>= i 2)
Ah, I see. Also, let me guess: if a form e1 of (atleast-two e1 e2 ...) sets c to 1 and evaluates to true, then it terminates already? Also: if e1 sets c to some non- number and evaluates to true then (inc c) fails.
> I.e. `(doargs (,var ,until-expr) ...). So DOARGS has a double condition for > terminating: implicitly running out of args, or the expression becoming true.
> The code is slightly confusing because he it repeats the test again. After > the execution of the loop, it's not clear whether it terminated because it ran > out of arguments, or because i reached the maximum value.
> It's like those silly C programs that do:
> for (i = 0; i < N; i++) { > /* ... */ > if (found) > break; > }
> But where is the paper that argues that fexprs are sometimes > better than macros because they are more flexible and > powerful than macros? Or easier to use, learn, etc? It sounds > like Mr. Majorinc is confident enough that he should be > able to set about writing that paper. I'm looking forward to it!
> ;============================
> I define macro (at-least-two arg1 ... argn) > that returns true if and only if at least > two of args evaluate to the true. Macro is > needed because we want lazy evaluation > of arg1 to argn, like in OR or AND.
> Newlisp (17 tokens, very simple)
> (define-macro (at-least-two) > (let (c) > (doargs (i (= c 2)) > (if (eval i) > (inc c))) > (>= c 2)))
It looks not very complicated, but I wonder what “(let (c)” means. Is this in CL like “(let ((c 0))”?
Will your code eval the first two args in any case? Even if only one is given? It seems to me that the (eval i) will take place in the case when at-least-two was called with exactly one argument.
Here I have a version in Clojure. It will return nil if less than two args were given. In the other case it will evaluate the first two arguments:
This version is even better than yours in my opinion. It also has 17 tokens. Your version needs 150% of the lines of code of my version, and my version is also shorter in character count: user> (count "(define-macro (at-least-two) (let (c) (doargs (i (= c 2)) (if (eval i) (inc c))) (>= c 2)))") 91
As you see, I did not count the leading spaces. So, your version does not fall too far behind. So, your version has nearly 10% more chars. But the best feature about my version is, in my opinion of course, that it is dramatically much more readable. There are no side effects. No counter c is increased. There is no need for a loop. I find it very clear. Take up to 2 args out of the given args. When 2 args were taken, then compile it into code (that’s the “do”). Perhaps you will agree with me. But I am open to hear that your opinion is different, and that you find your version easier and more readable.
> (define-macro (at-least n) > (let (c) > (doargs (i (= c n)) > (if (eval i) > (inc c))) > (>= c n)))
> Could you write simpler, more elegant one in > your favorite Lisp?
This is of course very objective. Although I think lots of programmers would agree that it will be easier to understand if the side effect of incrementing a not explicitly initialized counter and the loop could be eliminated. So I would just do the same as you:
(defmacro at-least-two [n & r] (let [c (take n r)] (when (= n (count c)) `(do ~@c))))
Again, I find my version simpler. Not much. But, even for this short code, far more elegant.
André -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/
> Well, your version iterates over all forms, counts the true values > and then returns if the sum is equal or greater to 2. So, it does NOT > stop when 2 is already reached.
You somehow made mistake. It does stop. For debugging purpose I added one print. ------------------- CODE:
(define-macro (at-least n) (let (c) (doargs (i (= c n)) (print i) ;;;;;; This one (if (eval i) (inc c))) (>= c n)))
(= 1 1)(= 2 2)(= 3 3)true ----------------- It works.
> Even if Newlisp names it a macro, it is not a macro. > It is a FEXPR. A Macro would do source transformation. > Above does not do any source transformation.
I agree. As it is terminology issue, I decided to stay with version that is used in two languages.
> (defmacro at-least (n &rest es &aux (c (gensym))) > `(let ((,c 0)) > (or ,@(loop for e in es collect `(and ,e (= (incf ,c) ,n))))))
> Again your FEXPR fails to show the generated code, > because it can't.
True, Newlisp macros (Lisp fexpr's) do not necessarily generate code. But they COULD generate the code if you really want it that way:
; Newlisp macro written in the Common Lisp style (define-macro (at-least n) (println (append '(let (c)) (list (cons 'or (map (lambda(x) (expand '(and x (inc c) (= c n)) 'x)) (args)))))))
This is fexpr that does exactly the same thing as your macro. It generates code and prints it - if you want that it evaluates it, just replace println with eval. It has 27 tokens. It is not natural to force generating whole code in fexprs, but it is possible.
It was my original one:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-macro (at-least n) (let (c) (doargs (i (= c n)) (if (eval i) (inc c))) (>= c n)))
As I myself wrote it, do you believe me that it was easier?
> (lambda-macro (a b) (set (eval a) b)) evaluates to (lambda (x) (* x x))
Yes, it is mistake. I'll report it, thanks. In essence, it evaluates to
> > But where is the paper that argues that fexprs are sometimes > > better than macros because they are more flexible and > > powerful than macros? Or easier to use, learn, etc? It sounds > > like Mr. Majorinc is confident enough that he should be > > able to set about writing that paper. I'm looking forward to it!
> > ;============================
> > I define macro (at-least-two arg1 ... argn) > > that returns true if and only if at least > > two of args evaluate to the true. Macro is > > needed because we want lazy evaluation > > of arg1 to argn, like in OR or AND.
> > Newlisp (17 tokens, very simple)
> > (define-macro (at-least-two) > > (let (c) > > (doargs (i (= c 2)) > > (if (eval i) > > (inc c))) > > (>= c 2)))
> It looks not very complicated, but I wonder what > “(let (c)” means. > Is this in CL like “(let ((c 0))”?
> Will your code eval the first two args in any case? > Even if only one is given? It seems to me that the (eval i) > will take place in the case when at-least-two was called with > exactly one argument.
> Here I have a version in Clojure. It will return nil if less than > two args were given. In the other case it will evaluate the first > two arguments:
The task, as I understand it, was not to check if there are two args and evaluate those. The task is to evaluate the all args until at least two have been evaluated to true.
> This version is even better than yours in my opinion. > It also has 17 tokens. Your version needs 150% of the lines of code of > my version, and my version is also shorter in character count:
That's not what his ``macro'' (frexp) does. It keeps evaluating expressions left to right until it runs out, or until it has encountered two that are true. It then stops evaluating, and returns true if two had been found true, otherwise false.
So you cannot simply take two expressions and ignore the rest.
Your macro also can't just not emit any code at all when there are fewer than two expressions. The expressions may have side effects. If there is only one expression, you know that the result will have to be false, but the effect of that expression still has to take place.
> This version is even better than yours in my opinion.
But it's not first class like a frexp. :)
> As you see, I did not count the leading spaces.
In general, a fairly good comparison for size of Lisp code is the number of atoms in the tree structure of the forms (say, including ()/NIL). (a (b c) d) has the same size as (a b c d). Same atom count, same cons count, different tree balance. (There are obvious ways to extend this to other languages; give them a very charitable, if informal, s-exp conversion and use that).
On Jan 24, 1:17 am, Majorinc Kazimir <fa...@email.address> wrote:
> > Well, your version iterates over all forms, counts the true values > > and then returns if the sum is equal or greater to 2. So, it does NOT > > stop when 2 is already reached.
> You somehow made mistake. It does stop. For debugging > purpose I added one print.
I did not look to close at DOARGS, my fault.
> > Even if Newlisp names it a macro, it is not a macro. > > It is a FEXPR. A Macro would do source transformation. > > Above does not do any source transformation.
> I agree. As it is terminology issue, I decided to stay > with version that is used in two languages.
> True, Newlisp macros (Lisp fexpr's) do not > necessarily generate code. But they COULD generate > the code if you really want it that way:
> ; Newlisp macro written in the Common Lisp style > (define-macro (at-least n) > (println (append '(let (c)) > (list > (cons 'or > (map (lambda(x) > (expand '(and x (inc c) (= c n)) > 'x)) > (args)))))))
> This is fexpr that does exactly the same thing as your macro. > It generates code and prints it - if you want that it evaluates it, > just replace println with eval. It has 27 tokens. It is not natural to > force generating whole code in fexprs, but it is possible.
No, doubt, you can not only morph code into data and evaluate the data, you can also morph code into data, generate more data out of that and evaluate that data. Common Lisp shows that none of that is necessary.
> It was my original one:
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > (define-macro (at-least n) > (let (c) > (doargs (i (= c n)) > (if (eval i) > (inc c))) > (>= c n)))
> As I myself wrote it, do you believe me that it was > easier?
It is not that much how easy it is, it is about how horrible it will be to debug any non-trivial amount of code using FEXPRs.
> Ah, I see. Also, let me guess: if a form e1 of (atleast-two e1 e2 ...) > sets c to 1 and evaluates > to true, then it terminates already? Also: if e1 sets c to some non- > number and evaluates to true > then (inc c) fails.
Right. That was (B1).
>> I.e. `(doargs (,var ,until-expr) ...). So DOARGS has a double condition for >> terminating: implicitly running out of args, or the expression becoming true.
>> The code is slightly confusing because he it repeats the test again. After >> the execution of the loop, it's not clear whether it terminated because it ran >> out of arguments, or because i reached the maximum value.
On Jan 24, 1:47 am, Majorinc Kazimir <fa...@email.address> wrote:
> > Ah, I see. Also, let me guess: if a form e1 of (atleast-two e1 e2 ...) > > sets c to 1 and evaluates > > to true, then it terminates already? Also: if e1 sets c to some non- > > number and evaluates to true > > then (inc c) fails.
> Right. That was (B1).
My Common Lisp version does not have that problem. Maybe you want to your increase token count a bit and show a safer version?
Let me guess: (let ((i 'something)) (at-least-two (eval i))) also does not work. doargs sets i to the list (eval i), and then evaluates it... which does what?
> >> I.e. `(doargs (,var ,until-expr) ...). So DOARGS has a double condition for > >> terminating: implicitly running out of args, or the expression becoming true.
> >> The code is slightly confusing because he it repeats the test again. After > >> the execution of the loop, it's not clear whether it terminated because it ran > >> out of arguments, or because i reached the maximum value.
Thanks to Rainer and Kaz for explanations. Maybe this one is right? Anyway, the fact that I didn’t understood (and still don’t understand it well) the NewLisp code means at least for me it was not easy enough to read. Perhaps I am just not trained enough in NewLisp. (and I also did not look at the CL example and I didn’t follow the discussion before)
filter will lazily feed eval with the args that were passed to the macro. Besides that, filter is like CLs remove-if-not.
This macro will eval all args passed to at-least-two until a maximum of two of these args evaled to true. (that is: not “nil” and not “false”). As Kaz suggested, now even those args that eval to nil or false will be run, so side effects take place.
Obviously I find this version even more elegant than the one before. And it is even shorter. @Rainer: of course, counting characters is a very idiotic thing. But it still is enjoyable for me to do it, because I feel a little bit like “beat ‘em with their own weapons”. I am not seriously drawing conclusions from these 5 liners. I just do it for the fun. Also Tamas complained in the past about it. I ask you two to just let me continue, for special friends. Mostly for guys like Jillian James :-)
On Jan 24, 2:13 am, André Thieme <address.good.until.
2009.may...@justmail.de> wrote: > Thanks to Rainer and Kaz for explanations. > Maybe this one is right? > Anyway, the fact that I didn’t understood (and still don’t understand > it well) the NewLisp code means at least for me it was not easy enough > to read. Perhaps I am just not trained enough in NewLisp. > (and I also did not look at the CL example and I didn’t follow the > discussion before)
> filter will lazily feed eval with the args that were passed to the macro. > Besides that, filter is like CLs remove-if-not.
> This macro will eval all args passed to at-least-two until a maximum of > two of these args evaled to true. (that is: not “nil” and not “false”). > As Kaz suggested, now even those args that eval to nil or false will be > run, so side effects take place.
> Obviously I find this version even more elegant than the one before. > And it is even shorter. > @Rainer: of course, counting characters is a very idiotic thing. But > it still is enjoyable for me to do it, because I feel a little bit like > “beat ‘em with their own weapons”. > I am not seriously drawing conclusions from these 5 liners. I just do it > for the fun. Also Tamas complained in the past about it. I ask you two > to just let me continue, for special friends. > Mostly for guys like Jillian James :-)
> On Jan 24, 2:13 am, André Thieme <address.good.until. > 2009.may...@justmail.de> wrote: >> Thanks to Rainer and Kaz for explanations. >> Maybe this one is right? >> Anyway, the fact that I didn’t understood (and still don’t understand >> it well) the NewLisp code means at least for me it was not easy enough >> to read. Perhaps I am just not trained enough in NewLisp. >> (and I also did not look at the CL example and I didn’t follow the >> discussion before)
Filter keeps only those elements which evaled to a different value than nil or false. If n args are giving filter can max return a list of n elements, like remove-if-not in CL. In such a case it is like (mapcar #'identity list). So, filter will return a list whose length is between 0 and n. Out of this list take tries to take as many elements as possible, but max 2. So, take can result in a list of the lengths 0, 1 or 2.
> Shouldn't it count the expressions that evaluate to true over all > items?
I don’t know. I am not sure what the original poster really wanted. As I don’t fully understand his code I have to guess.
> Will that see variables?
Good objection.
> In CL
> (let ((a t) (b nil) (c t)) > (at-least-two a b c))
> > On Jan 24, 2:13 am, André Thieme <address.good.until. > > 2009.may...@justmail.de> wrote: > >> Thanks to Rainer and Kaz for explanations. > >> Maybe this one is right? > >> Anyway, the fact that I didn’t understood (and still don’t understand > >> it well) the NewLisp code means at least for me it was not easy enough > >> to read. Perhaps I am just not trained enough in NewLisp. > >> (and I also did not look at the CL example and I didn’t follow the > >> discussion before)
> Filter keeps only those elements which evaled to a different value than > nil or false. > If n args are giving filter can max return a list of n elements, like > remove-if-not in CL. In such a case it is like (mapcar #'identity list). > So, filter will return a list whose length is between 0 and n. > Out of this list take tries to take as many elements as possible, but > max 2. So, take can result in a list of the lengths 0, 1 or 2.
Okay, looks good.
...
> > Will that see variables?
> Good objection.
> > In CL
> > (let ((a t) (b nil) (c t)) > > (at-least-two a b c))
> > ?
> No, my version can’t see them.
You may need to increase token and character count...
> On Jan 24, 2:13 am, André Thieme <address.good.until. > 2009.may...@justmail.de> wrote: >> Thanks to Rainer and Kaz for explanations. >> Maybe this one is right? >> Anyway, the fact that I didn’t understood (and still don’t understand >> it well) the NewLisp code means at least for me it was not easy enough >> to read. Perhaps I am just not trained enough in NewLisp. >> (and I also did not look at the CL example and I didn’t follow the >> discussion before)
> Shouldn't it count the expressions that evaluate to true over all > items?
Once TAKE 2 pulls out two, it is done. Pardon me, it must be COUNT that actually pulls out the items. (TAKE 2) does nothing, just produces a virtual sequence. If you don't use that value, no evaluation happens. But COUNT must walk the whole sequence until the end, so it kicks TAKE into action, which produces two items and then is done. In so doing, it must take at least two items from the virtual sequence produced by the FILTER. FILTER will only produce as many items as TAKE 2 asks for, i.e. at most two, but in so doing it may process and filter out many forms that evaluate false. Once it produces two truths though, it won't evaluate any more.
EVAL meets lazy sequences: a jam between psychedelic acid rock and progressive metal!
The change is what follows the “eval”. It was: '~x And now is: [~@x]
Explanation: x will be a list of all given args in the macro. Inside a backquote expression x is just the symbol x. To unquote it one uses “~” in Clojure, as “,” is just whitespace (for readability really nice, IMO). ~x would place the list of all given args behind the eval. In the case of (at-least-two a b c) this would be (a b c) But (filter eval (a b c)) does not make much sense typically, because the function a is not defined. So, a call like: (let [a nil, b nil, c false, d true] (at-least-two a b c d)) would result in a NullPointerException.
In my original version I did not think about variable capture, so a call like: (at-least-two 5 6 7) would result in (filter eval (5 6 7)) which also makes no sense, as Clojure can’t call 5.
That’s why I quoted the ~x in my macro: '~x Now (at-least-two 5 6 7) results in a (filter eval '(5 6 7)). This however can’t work if variables are in the game, because those would be treated literally, as symbols.
If Clojure would put the rest args into a vector instead of a list, then ~x would have been okay. But as this does not happen I say: [~x] which would be ==> [(5 6 7)] But [~@x] ==> [5 6 7] Now it is not quoted anymore, and also variables are treated properly:
user> (let [a nil, b nil, c false, d true] (at-least-two a b (println 5) d)) 5 false
Note that the false is the return value of the call to at-least-two. The 5 directly above it is what was printed. Note that println just prints and returns nil. So, unlike CLs print it won’t return 5.
(let [a nil, b nil, c false, d true] (at-least-two b a d c d)) ==> true
André -- Lisp is not dead. It’s just the URL that has changed: http://clojure.org/
> On 2009-01-24, jos...@corporate-world.lisp.de <jos...@corporate-world.lisp.de> > wrote: >> On Jan 24, 2:13 am, André Thieme <address.good.until. >> 2009.may...@justmail.de> wrote: >>> (defmacro at-least-two [& x] >>> `(= 2 (count (take 2 (filter eval '~x))))) >> It takes the first two and counts those?
>> Shouldn't it count the expressions that evaluate to true over all >> items?
> Once TAKE 2 pulls out two, it is done. Pardon me, it must be COUNT that > actually pulls out the items.
Yes, you are absolutely right. Did you know that because you already worked in Clojure? Or was this the result of your analysis?
> (TAKE 2) does nothing, just produces a virtual sequence.
Exactly.
> If you don't use that value, no evaluation happens. But COUNT must
> walk the whole sequence until the end, so it kicks TAKE into action, which > produces two items and then is done. In so doing, it must take at least two > items from the virtual sequence produced by the FILTER. FILTER will only > produce as many items as TAKE 2 asks for, i.e. at most two, but in so doing it > may process and filter out many forms that evaluate false. Once it produces two > truths though, it won't evaluate any more.
Perfect explanation. And thanks for your translation into CL.