I am attempting to write a threading macro using failjure/ok->> (https://github.com/adambard/failjure#ok--and-ok-), with the final function in the thread requiring a condition to execute. The code looks something like this:
(f/ok->> (function1 param)
(function2 param1 param 2)
...
({conditional function here}))
where if the conditional is not hit, the threading macro returns the result of the penultimate function call.
I attempted to write a cond function that checked for the necessary condition and then either returned the function if the condition passed, or just the result of the previous function, but the threading macro seems to not pass the result to the function within the cond, but only the cond itself. the (incorrect) code looked like this:
(f/ok->> (function1 param)
(function2 param1 param 2)
...
(cond (condition?)
(function_if_passes_condition)
#(%))
I am wondering if there is a clean way to do this correctly. I imagine it is possible to write a brand new threading macro with such functionality, but so far all of my attempts at doing that haven't worked (I have not written a defmacro that implements a threading macro before, and it has been quite difficult as I am fairly new at clojure with 3 months experience).
Your problem statement seems a little vague, so I'll solve a simplified version of the problem.
Keep in mind that a macro is a code-translation mechanism. That is, it translates the code that you wish you could write into something that is acceptable by the compiler. In this way, it is best to think of the result as a compiler extension. Writing a macro is complicated and almost always unnecessary. So, don't do it unless you really need it.
Let's write a helper predicate and unit test:
(ns tst.demo.core
(:use tupelo.core tupelo.test) ; <= *** convenience functions! ***
(:require [clojure.pprint :as pprint]))
(defn century? [x] (zero? (mod x 100)))
(dotest
(isnt (century? 1399))
(is (century? 1300)))
Suppose we want to translate this code:
(check-> 10
(+ 3)
(* 100)
(century?) )
into this:
(-> 10
(+ 3)
(* 100)
(if (century) ; <= arg goes here
:pass
:fail))
Re-write the goal a little:
(let [x (-> 10 ; add a temp variable `x`
(+ 3)
(* 100))]
(if (century? x) ; <= use it here
:pass
:fail))
Now start on the -impl function. Write just a little, with some print statements. Notice carefully the pattern to use:
(defn check->-impl
[args] ; no `&`
(spyx args) ; <= will print variable name and value to output
))
(defmacro check->
[& args] ; notice `&`
(check->-impl args)) ; DO NOT use syntax-quote here
and drive it with a unit test. Be sure to follow the pattern of wrapping the args in a quoted vector. This simulates what [& args] does in the defmacro expression.
(dotest
(pprint/pprint
(check->-impl '[10
(+ 3)
(* 100)
(century?)])
))
with result:
args => [10 (+ 3) (* 100) (century?)] ; 1 (from spyx)
[10 (+ 3) (* 100) (century?)] ; 2 (from pprint)
So we see the result printed in (1), then the impl function returns the (unmodified) code in (2). This is key. The macro returns modified code. The compiler then compiles the modified code in place of the original.
Write some more code with more prints:
(defn check->-impl
[args] ; no `&`
(let [all-but-last (butlast args)
last-arg (last args) ]
(spyx all-but-last) ; (1)
(spyx last-arg) ; (2)
))
with result
all-but-last => (10 (+ 3) (* 100)) ; from (1)
last-arg => (century?) ; from (2)
(century?) ; from pprint
Notice what happened. We see our modified variables, but the output has changed as well. Write some more code:
(defn check->-impl
[args] ; no `&`
(let [all-but-last (butlast args)
last-arg (last args)
cond-expr (append last-arg 'x)] ; from tupelo.core
(spyx cond-expr)
))
cond-expr => [century? x] ; oops! need a list, not a vector
Oops! The append function always returns a vector. Just use ->list to convert it into a list. You could also type (apply list ...).
cond-expr => (century? x) ; better
Now we can use the syntax-quote to create our output template code:
(defn check->-impl
[args] ; no `&`
(let [all-but-last (butlast args)
last-arg (last args)
cond-expr (->list (append last-arg 'x))]
; template for output code
`(let [x (-> ~#all-but-last)] ; Note using `~#` eval-splicing
(if ~cond-expr
:pass
:fail))))
with result:
(clojure.core/let
[tst.demo.core/x (clojure.core/-> 10 (+ 3) (* 100))]
(if (century? x) :pass :fail))
See the tst.demo.core/x part? That is a problem. We need to re-write:
(defn check->-impl
[args] ; no `&`
(let [all-but-last (butlast args)
last-arg (last args)]
; template for output code. Note all 'let' variables need a `#` suffix for gensym
`(let [x# (-> ~#all-but-last) ; re-use pre-existing threading macro
pred-result# (-> x# ~last-arg)] ; simplest way of getting x# into `last-arg`
(if pred-result#
:pass
:fail))))
NOTE: It is important to use ~ (eval) and ~# (eval-splicing) correctly. Easy to get wrong. Now we get
(clojure.core/let
[x__20331__auto__ (clojure.core/-> 10 (+ 3) (* 100))
pred-result__20332__auto__ (clojure.core/-> x__20331__auto__ (century?))]
(if pred-expr__20333__auto__
:pass
:fail))
Try it out for real. Unwrap the args from the quoted vector, and call the macro instead of the impl function:
(spyx-pretty :final-result
(check-> 10
(+ 3)
(* 100)
(century?)))
with output:
:final-result
(check-> 10 (+ 3) (* 100) (century?)) =>
:pass
and write some unit tests:
(dotest
(is= :pass (check-> 10
(+ 3)
(* 100)
(century?)))
(is= :fail (check-> 10
(+ 3)
(* 101)
(century?))))
with result:
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
Ran 3 tests containing 4 assertions.
0 failures, 0 errors.
You may also be interested in this book: Mastering Clojure Macros
Related
Lisp noob here, inclined to believe I am certainly misunderstanding something as this is a problem from page 35 of a rather long book on its second edition. I have been reading up on let binding, so let me make sure the author's* intent is clear by quoting him:
In the following code, the symbol pos serves as a temporary storage
or variable that has as its value the result of the position
function expression. Finally, how will we accumulate the results? The recursive call should give us a list of the remaining positions, so the first one found should just be put on the front. We already have a function to do this, the cons function
This is the code:
(defun all-pos (item seq start)
(let ((pos (position item seq :start start))
(if pos
(cons pos
(all-pos item seq (+ 1 pos)))
nil))))
and this is the error:
Ch1_Notes.lisp:27:5:
error:
The LET binding spec (IF POS
(CONS POS (ALL-POS ITEM SEQ (+ 1 POS)))
NIL) is malformed.
Compilation failed.
Is this self-evidently wrong? Is there some preceding code I should be including as well?
*The author, Ira J. Kalet, is dead, so I can't ask him.
Often it is useful to re-indent the code if there is such an error. One typically can do it in the editor with a key-command.
But Lisp also comes in with built in code formatting, called pretty printing:
CL-USER > (let ((*print-right-margin* 60))
(pprint '
; your code follows:
(defun all-pos (item seq start)
(let ((pos (position item seq :start start))
(if pos
(cons pos
(all-pos item seq (+ 1 pos)))
nil))))
))
The output looks like this:
(DEFUN ALL-POS (ITEM SEQ START)
(LET ((POS (POSITION ITEM SEQ :START START))
(IF POS (CONS POS (ALL-POS ITEM SEQ (+ 1 POS)))
NIL))))
This makes it easier to see that the if is on the same indentation level like the pos variable binding. This can't be right!
To format your code with Lisp you just need:
(let ((*print-right-margin* 60))
(pprint '
; here goes your code
))
and Lisp will format it for you...
As the answerers before me mentioned, a simple parenthesis problem:
;; correct version:
(defun all-pos (item seq start)
(let ((pos (position item seq :start start)))
(if pos
(cons pos
(all-pos item seq (+ 1 pos)))
nil)))
;; e.g.
(all-pos '3 '(1 2 3 4 3 5 4) 0)
;; returns (2 4) - correctly
You seem to have the unbalanced parantheses problem going on. A way to see that is to look at the closing parenthesis after NIL. That closes the IF statement. After that one there are two more closing ones that would close the LET and DEFUN. However, you still have one more after that. That is what is causing the issue.
This question already has answers here:
Common lisp error: "should be lambda expression"
(4 answers)
Closed 5 years ago.
I'm trying to make a function that changes infix input to prefix eg : (x + 1) as input outputted as (+ x 1).
So here is my code for the moment :
(setq x '(Y + 1))
(if (listp x ) (list (second x) (first x) (first (last x))) x)
so it returns (+ Y 1) if I input a list and the user input if it's not a list.
However, the problem is that I can't get this code working in a function :
(defun prefixToInfix (x)(
(if (listp x ) (list (second x) (first x) (first (last x))) x)
)
)
the function is indeed created but when I call it
(prefixtoinfix '(Y + 1))
I get an error
Error: Illegal function object: (IF (LISTP X) (LIST # # #) X).
[condition type: TYPE-ERROR]
I don't know why my if statement works in the main program but doesn't when I run it from my function.
What you are missing is that in Lisp parentheses are meaningful.
In C/Java/Python &c, the following expressions are the same:
a+b
(a+b)
(a)+(b)
(((a)+(b)))
(((((a)+(b)))))
In Lisp, the following expressions are very different:
a --- a symbol
(a) --- a list with a single element, which is the symbol a
(1 (2)) --- a list of two elements:
number 1
list of of length 1, containing number 2
In your case, function (note indentation and paren placement!)
(defun prefixToInfix (x)
((if (listp x) (list (second x) (first x) (first (last x))) x)))
has extra parens around if (and this causes the whole if form to be interpreted as a function, with disastrous results), and should be (note line breaks and indentation - lispers do not count parens, they look at indentation to understand the code, see http://lisp-lang.org/style-guide/)
(defun prefix-to-infix (x)
(if (listp x)
(list (second x)
(first x)
(first (last x)))
x))
PS. See also recommendations in want to learn common lisp.
DrRacket user.
I'm struggling to understand how this program works.I wrote it myself and it does what it must but I can't understand how.
I define while loops as:
(define (while test body)
(if (test)
(begin
(body)
(while test body))
(void)))
Now I need to write a program that applies the given procedure to each element of a mutable list.
Here what I wrote:
(define (mlist-map-while f x)
(while (lambda() (not (null? x)))
(lambda ()
(set-mcar! x (f (mcar x)))
(set! x (mcdr x))))
(void))
So, defining
list1 (mlist 1 2 3)
and applying
(mlist-map-while (lambda (x) (+ x 1)) list1)
we get '(2 3 4).
The thing that I don't understand is how the first element of the list stays in it, because if it's done how I wrote here
(set! x (mcdr x))
the first procedure that sets -mcar! must be useless and be overlapped with the second. Like in this example:
(define list1 (mlist 1 2 3))
(set-mcar! list1 9)
(set-mcdr! list1 (mcdr list!))
and we lack the first element, but this program somehow leaves it in and gives the desired output. I would like to know how it works and whether there is another way of traversing the given list.
There is a big difference between set-cdr! abd set!. The first alters the pair's cdr pointer, while the latter alters the binding, thus what the name should point to.
In your mlist-map-while the variable x alters the car, then changes what x represents, to be the cdr of x. It never changes the cdr so your binding list1 always points to the first pair while x points to the first, then second, etc...
Thus it's more like this:
(define list1 (mlist 1 2 3))
(define list1-ref list1) ; variable pointing to the same value as list1
(set-mcar! list1-ref 9) ; set-car! changes the pair
(set! list1-ref (mcdr list)) ; set! updates what list1-ref points to
list1 ; ==> (9 2 3)
list-ref ; ==> (2 3)
You can iterate over a list in the same fashion without using set!, with recursion:
(define (fold function init lst)
(if (null? lst)
init
(fold function
(function (car lst) init)
(cdr lst))))
(fold + 0 '(1 2 3)
; ==> 6
(fold cons '() '(1 2 3))
; ==> (3 2 1)
Notice that here we recurse and change what lst is, to be the cdr. Every recursion has its own lst, which is not to be confused with the caller's own. It ends up doing the same as set! in your example, without mutation.
I know what I want to do, I am having trouble getting there. I am looking for some guidance. I am more or less forcing what I want done and there has to be a better way than the way I am trying to create this function. I currently get an unbound variable error right where I call (set! nadj) and (set! count).
I am trying to make a function where the user inputs a sentence. If more than 25% of that sentence consists of adjectives the function returns false.
This is what I have so far:
(define OK
(lambda (x)
(cond
((adj? (car x))
(set! count (+ count 1)))
((not (adj? (car x))
(set! nadj (+ nadj 1))))
((not (null? (OK (cdr x)))))
((null? x)
(set! sum (+ nadj count)))
;;(set! div (/ count sum))
;;(* 100 div)
;;(< div 25))
((else #f)))))
What I am trying to do is make a counter for the words that are an adjective and a counter for the words that are not. Then I am trying to add all of the words up and divide them by the amount of words that were adjectives. I then want to multiply that by 100 and return true if it is less than 25%. I am not looking for an answer, more or less I just want some guidance.
Here is the adj? function if you need to see it.
(define adjectives '(black brown fast hairy hot quick red slow))
(define adj?
(lambda(x)
(if ( member x adjectives) #t #f)))
I am sure this isn't normal Scheme notation. I program a lot in C++ and Java and I am having a hard time transitioning into Scheme.
You're correct in stating that your solution is not idiomatic Scheme - we try really hard to avoid mutating variables, all those set! operations are frowned upon: we don't really need them. A more idiomatic solution would be to pass along the counters as parameters, as demonstrated in #uselpa's answer. His solution uses explicit recursion via a named let.
We can go one step further, though - the true spirit of functional programming is to reuse existing higher-order procedures and compose them in such a way that they solve our problems. I don't know which Scheme interpreter you're using, but in Racket the OK procedure can be expressed as simply as this:
(define (OK x) ; assuming a non-empty list
(< (/ (count adj? x) ; count the number of adjectives
(length x)) ; divide by the total number of words
0.25)) ; is it less than 25%?
If your Scheme interpreter doesn't provide a count procedure import it from SRFI-1; also it's very easy to implement your own - again, this is in the spirit of functional programming: we want to build generic procedures that are useful in their own right, and easily reused and composed in other contexts:
(define (count pred lst)
(let loop ((lst lst) (counter 0))
(cond ((null? lst) counter)
((pred (car lst)) (loop (cdr lst) (+ 1 counter)))
(else (loop (cdr lst) counter)))))
Playing Devil's advocate it's possible to fix your function using an imperative style, as long as we define the variables first (by the way, that was causing the "unbound variable" error) - for example, place a let before the looping function: think of it as a variable declaration that happens before the recursion starts. Also notice that the empty list case must appear first, to avoid accessing an element in an empty list, and don't forget to advance the recursion at each step. This is ugly, but should work:
(define (OK x) ; assuming a non-empty list
; declare the counters outside the function
(let ((adj 0) (nadj 0))
; looping function
(let loop ((x x))
(cond
; is the list empty?
((null? x)
; is the number of adjectives less than 25%?
(< (/ adj (+ adj nadj)) 0.25))
; is current element an adjective?
((adj? (car x))
; increment adj counter
(set! adj (+ adj 1))
; always advance recursion
(loop (cdr x)))
; is current element anything other than an adjective?
(else
; increment nadj counter
(set! nadj (+ nadj 1))
; always advance recursion
(loop (cdr x)))))))
I don't know if you are familiar with the named let, but this comes in handy here:
(define (OK x)
(let loop ((x x) (adj 0) (nadj 0)) ; named let
(cond
((null? x) (< (/ adj (+ adj nadj)) 0.25))
((adj? (car x)) (loop (cdr x) (+ 1 adj) nadj))
(else (loop (cdr x) adj (+ 1 nadj))))))
This is a convenient notation for the following, equivalent code:
(define (OK x)
(define (loop x adj nadj)
(cond
((null? x) (< (/ adj (+ adj nadj)) 0.25))
((adj? (car x)) (loop (cdr x) (+ 1 adj) nadj))
(else (loop (cdr x) adj (+ 1 nadj)))))
(loop x 0 0))
so basically we define an internal function, and what is a loop in a language such as C++ and Java becomes a recursive call (and to add to the confusion, the procedure that gets called recursively is sometimes called loop, as in my example). Since the call is done in tail position, this is just as efficient in Scheme as a classic loop in the languages you mentioned.
Variable assignments are replaced by modifying the parameters of the recursive call, i.e. you usually find no set! procedures in such a simple case.
EDIT an example implementation using set!:
(define OK
(let ((adj 0) (nadj 0))
(lambda (x)
(cond
((null? x) (< (/ adj (+ adj nadj)) 0.25))
(else (if (adj? (car x))
(set! adj (+ 1 adj))
(set! nadj (+ 1 nadj)))
(OK (cdr x)))))))
You can't set an unbound variable, even a global one. Variables refer to locations; setting a variable that doesn't exist anywhere is impossible:
(set! a 1)
;Unbound variable: a ; a doesn't refer to any location yet
(define a)
;Value: a
(list a)
;Unassigned variable: a ; now it does, but it hasn't been assigned a value yet
(set! a 1)
;Value: a
(list a)
;Value: (1)
(set! a 2)
;Value: 1
(list a)
;Value: (2)
There's nothing wrong with localized and encapsulated mutation. Setting a global variable is by definition not localized.
You should have created local bindings (locations) for the variables you intended to use. The basic iteration built-in form do does it for you:
(define (OK x)
(do ((adj 0) (nadj 0))
((null? x) ; loop termination condition
(< (/ adj (+ adj nadj))
0.25)) ; return value form
; loop body
(if (adj? (car x))
(set! adj (+ adj 1))
; else
(set! nadj (+ nadj 1)))
; some other statements maybe...
))
Just another option that sometimes might come handy. Of course the most idiomatic Scheme code is using named let construct. It will also force you to refactor a spaghetti code that you might otherwise write using do. Don't. :)
A function that returns how many times it has been called in Scheme would look like
(define count
(let ((P 0))
(lambda ()
(set! P (+ 1 P))
P)))
(list (count) (count) (count) (count)) ==> (list 1 2 3 4)
But suppose that we have an expression that looks like this
(map ______ lst)
and we want that to evaluate to
(list 1 2 3 ... n)
where n = (length list)
The problem requires we use a lambda statement in the blank, and we cannot use any auxiliary definitions like (count) in the blank, so
(lambda (x) (count))
is not allowed. Simply replacing (count) with the previous definition like this:
(map
(lambda (x)
((let ((P 0))
(lambda ()
(set! P (+ 1 P))
P))))
L)
doesn't work either.
Any suggestions?
You're very, very close to a correct solution! in the code in the question just do this:
The outermost lambda is erroneous, delete that line and the corresponding closing parenthesis
The innermost lambda is the one that will eventually get passed to the map procedure, so it needs to receive a parameter (even though it's not actually used)
Delete the outermost parenthesis surrounding the let form
It all boils down to this: the lambda that gets passed to map receives a parameter, but also encloses the P variable. The let form defines P only once in the context of the passed lambda, and from that point on the lambda "remembers" the value of P, because for each of the elements in the list the same P is used.
You're 90% of the way there. Use the right-hand-side of your count definition in the blank, and add an (ignored) argument to the function.
(define add-stat-var
(let ( (P '()) )
(lambda (x1)
(if (equal? x1 "ResetStatVar") (set! P '()) (set! P (cons x1 P)))
P
) ;lambda
) ;let
) ;define
(define (test-add-stat-var x)
(let* ( (result '()) )
(set! result (add-stat-var 12))
(set! result (add-stat-var 14))
(set! result (add-stat-var 16))
(display (add-stat-var x)) (newline)
(set! result (add-stat-var "ResetStatVar"))
(display (cdr (add-stat-var x))) (newline)
)
)