Generally in Scheme - syntax: (if test consequent alternate) .
I trying to do some operations within the consequent section , for example : if true then set sum1 on 44 and return 1 .
In Scheme code its -
(if #t (
(set! sum1 44)
(if #t 1)
))
The above code doesn't works out and prompts -
application: not a procedure;
expected a procedure that can be applied to arguments
given: #<void>
arguments...:
1
In Scheme, parentheses are always used for application; you can't add extra parentheses for grouping.
Your consequent here is:
((set! sum1 44) (if #t 1))
The outer pair of parentheses makes Scheme attempt to use the result of (set! sum1 44) as a procedure, applying it to the result of (if #t 1).
What (I think) you want is to evaluate the two expressions in sequence, and then return the result of the last one. The form to do this is begin, so it should be:
(begin (set! sum1 44) (if #t 1))
(if #t (begin
(set! sum1 44)
(if #t 1)))
Some interpreters (Racket, for instance) complain if you write an if without an else part. For dealing with this, and for avoiding the need to explicitly use a begin when more than one expression goes in the consequent part, it's better to use the when special form (if available, because it's non-standard):
(when #t
(set! sum1 44)
(when #t 1))
In general, this is the structure of a when and its sibling unless:
(when <condition> ; equivalent to (if <condition> (begin ...))
<exp1> ; consequent, no alternative
<exp2>)
(unless <condition> ; equivalent to (if (not <condition>) (begin ...))
<exp1> ; consequent, no alternative
<exp2>)
If both the consequent and the alternative have more than one expression, you could use if with a begin in each part:
(if <condition>
(begin ; consequent
<exp1>
<exp2>)
(begin ; alternative
<exp3>
<exp4>))
... But it'd be more practical to use a cond, which implicitly uses a begin in each of its clauses:
(cond (<condition>
<exp1> ; consequent
<exp2>)
(else ; alternative
<exp3>
<exp4>))
Related
To preface this, I'm very new to Clojure. I was wondering why I'm getting a ClassCastExpection on this piece of code. I've looked at it and syntactically and logically it looks correct?
Clojure Code:
; (load-file "cje842asg1.clj")
(def lstnum '(76 85 71))
; 2a
(defn sum [list] (
(if (empty? list)
0
(+ (first list (sum (rest list))))
)
)
)
; if empty return zero, else add the first num in list and pass in the
; rest.
(sum lstnum)
Any help is appreciated.
The code has 2 separate errors.
The first is calling (first list (sum (rest list))) which is calling first with 2 arguments when you meant to make the second part the second argument to +.
The other one leading to the actual exception is the extra () around the function body. Note the ((if ...)) which means you are trying to call whatever the if returns as a function. In this case it is returning a number, leading to the class java.lang.Long cannot be cast to class clojure.lang.IFn error.
The corrected version would be
(defn sum [list]
(if (empty? list)
0
(+ (first list)
(sum (rest list)))))
Moving the arguments to + into separate lines makes it easier to read in this case (IMHO).
For an homework, I am trying to iterate through the list and make an if statement to each of the elements I encounter in Common Lisp.
Most of my problems come from me not knowing the syntax very well though.
Here is what I am trying to do:
*lst* is for example (1 4 2 6 4 7 2 4)
(setq finalList())
(loop for x in lst
(if ((< x 5) && (not(equal(x nil)))) ; IF x < 5 and x != nil
(append lst x) ; THEN STATEMENT
(princ "Skip") ; ELSE STATMENT
)
That logically should have done it, however, I am possibly wrong with my syntax
(If I forgot extra colon here, judge if as pseudo code, thanks)
How to iterate through the list and make an if else statements to each of the elements I encounter?
Errors
Syntax: instead of infix &&, you need prefix and
loop requires the keyword do before the if form.
append is non-destructive, so if you did not assign its return value to a variable, you did nothing.
Lisp function call syntax is (func arg1 arg2 ...) and not (func (arg1 arg2 ...)) as you use in your equal call.
The order of conditionals matters: if x is nil, then (< x 5) will signal an error, so you need to check x before the comparison.
The long and complicated (not (equal x nil)) is actually equivalent to x.
Unlike in other languages (e.g., C and Python), in Lisp parens matter, e.g., foo, (foo), ((foo)) are three very different things and can never be used interchangeably.
Solutions
Use the Loop micro-language
The loop facility is perfect as it has the if and collect clauses:
(loop for x in lst
if (and x (< x 5))
then collect x
else (print x))
Note: this does not really look like Lisp, so you might want to steer clear of loop while you are learning the language.
Use Functional Programming
(remove-if-not (lambda (x) (and x (< x 5))) lst)
Note: this does not print the dropped elements, I am sure you had the print clause for debugging only anyway.
Use Iteration
(let ((res ()))
(dolist (x lst (nreverse res))
(when (and x (< x 5))
(push x res))))
Note: I use nreverse: since we construct the result list ourselves, the is no reason not to use the destructive version or reverse.
How would you replace the cond to an if else in this program?
#lang racket
(define (my-map f lst)
(cond
[(empty? lst) empty]
[else (cons (f (first lst))
(my-map f (rest lst)))]))
A cond expression with just one condition and a single expression as the consequent and an else part with a single expression as the alternative can be easily transformed into an if expression. For example, this:
(cond (<cond1> <exp1>)
(else <exp2>))
Is equivalent to this:
(if <cond1> ; condition
<exp1> ; consequent
<exp2>) ; alternative
In particular, your code can be rewritten as:
(define (my-map f lst)
(if (empty? lst)
empty
(cons (f (first lst))
(my-map f (rest lst)))))
Also notice that a cond is just a shorter way to express a series of nested if expressions. Think of it as a shorthand for IF-ELSE IF-ELSE IF-ELSE in other programming languages. In fact, for the general case many interpreters implement this:
(cond (<cond1> <exp1> <exp2>) ; we can have any number of conditions
(<cond2> <exp3> <exp4>) ; and any number of expressions after condition
(else <exp5> <exp 6>))
... As a syntactical transformation that (ignoring the => syntax) gets expanded into:
(if <cond1>
(begin
<exp1>
<exp2>)
(if <cond2>
(begin
<exp3>
<exp4>)
(begin ; the last expression is the `else` part
<exp5>
<exp6>)))
Notice that the expressions after each condition are inside a begin form, meaning that in a cond they're implicitly inside a begin - so you can write more than one! whereas in an if expression only a single expression can go in either the consequent or the alternative, and if more than one is needed a begin must be used. As always, refer to the documentation for more details.
Every cond:
(cond (predicate consequent)
(predicate2 consequent2-1 consequent2-2)
(else alternative))
Can be rewritten:
(if predicate
consequent
(if predicate2
(begin
consequent2-1
consequent2-2)
alternative))
Your example becomes particularly simple because there is not more than 2 clauses and the code does not make use of explicit begin.
Racket isn't really Scheme as, for example, Racket requires a different if syntax than Scheme. But, figuring that you are, in fact, learning Scheme you might check out the Scheme R7RS Specification; it provides a definition of cond in terms of if. Here is a definition that I've simplified for you by axing templates involving '=>':
(define-syntax cond
(syntax-rules (else)
((cond (else result1 result2 ...))
(begin result1 result2 ...))
((cond (test)) test)
((cond (test) clause1 clause2 ...)
(let ((temp test))
(if temp temp
(cond clause1 clause2 ...))))
((cond (test result1 result2 ...))
(if test (begin result1 result2 ...)))
((cond (test result1 result2 ...)
clause1 clause2 ...)
(if test
(begin result1 result2 ...)
(cond clause1 clause2 ...)))))
Beautiful, yes?
I'm new to Scheme and am trying to have an if-statement perform more than one action if its condition is true. I tried something like:
(if (char=? (string-ref str loc) #\a)
((+ count 1) (+ reference 1))
~else action, etc.~
And it complains about my action, saying
application: not a procedure
If I remove the parentheses, so that the action for a true condition is:
(+ count 1) (+ reference 1)
It complains
if: bad syntax
and fails to run at all. What am I missing?
There are two problems with the code. The first, an if form can't have more than one expression in the consequent and in the alternative. Two possible solutions for that - you can either use begin (not just a couple of parenthesis, that's for procedure application) to surround multiple expression:
(if (char=? (string-ref str loc) #\a)
; needs an explicit `begin` for more than one expression
(begin
(+ count 1)
(+ reference 1))
; needs an explicit `begin` for more than one expression
(begin
~else action, etc~))
... or use a cond, which is a better alternative because it already includes an implicit begin:
(cond ((char=? (string-ref str loc) #\a)
; already includes an implicit `begin`
(+ count 1)
(+ reference 1))
(else
; already includes an implicit `begin`
~else action, etc~))
The second problem is more subtle and serious, both of the expressions in the consequent part are probably not doing what you expect. This one: (+ count 1) is doing nothing at all, the incremented value is lost because you didn't use it after incrementing it. Same thing with the other one: (+ reference 1), but at least here the value is being returned as the result of the conditional expression. You should either pass both incremented values along to a procedure (probably as part of a recursion):
(cond ((char=? (string-ref str loc) #\a)
; let's say the procedure is called `loop`
(loop (+ count 1) (+ reference 1)))
(else
~else action, etc~))
Or directly update in the variables the result of the increments, although this is not the idiomatic way to write a solution in Scheme (it looks like a solution in C, Java, etc.):
(cond ((char=? (string-ref str loc) #\a)
; here we are mutating in-place the value of the variables
(set! count (+ count 1))
(set! reference (+ reference 1)))
(else
~else action, etc~))
If you're trying to perform multiple actions with side-effects in one arm of the if, then you'll need to put them into a begin, like this:
(if (char=? (string-ref str loc) #\a)
(begin (set! count (+ count 1))
(set! reference (+ reference 1)))
~else action, etc.~
If, instead of causing changes in variables, you want to return two values at once, then you'll either need to combine the expressions into a single object, like this:
(if (char=? (string-ref str loc) #\a)
(cons (+ count 1) (+ reference 1)))
~else expression~
in which case to pull out the count and reference, you'll need to apply car and cdr to the result of the if—or you could actually return multiple values, like this:
(if (char=? (string-ref str loc) #\a)
(values (+ count 1) (+ reference 1)))
~else expression~
in which case to pull out the count and reference, you'll need to bind the multiple values to variables somehow in the code that calls the if. One way to do that is with let-values, possibly something like this:
(define count&ref
(λ (str ch)
(let loop ([loc 0] [count 0] [reference 0])
; whatever other stuff you're doing
(if (char=? (string-ref str loc) ch)
(values (+ count 1) (+ reference 1)))
~else expression~ )))
(let-values ([(new-count new-ref) (count&ref "some stuff" #\u)])
;in here, new-count and new-ref are bound to whatever count&ref returned
)
On the other hand, if count and reference are variables that you're just keeping track of within a loop, the simplest way might be to call the next iteration of the loop inside the if, like this:
(let loop ([loc 0] [count 0] [reference 0])
; whatever other stuff you're doing
(if (char=? (string-ref str loc) #\a)
(loop (+ loc 1) (+ count 1) (+ reference 1))
~else expression~ ))
You can use begin to group a set of expressions to be executed one by one.
(if (char=? (string-ref str loc) #\a)
(begin (+ count 1) (+ reference 1))
~else action, etc.~
begin only returns the value of the last expression, that is (+ reference 1), so the value of (+ count 1) is not used.
I am trying to write a function using Common Lisp functions only that will count how many s-expressions are in an s-expression. For example:
((x = y)(z = 1)) ;; returns 2
and
((x - y)) ;; returns 1
Nested expressions are possible so:
((if x then (x = y)(z = w))) ;; returns 3
I wrote a function which finds the length and it works if no nested expressions are there. It is:
(define (length exp)
(cond
((null? exp) 0)
(#t (+ 1 (length (cdr exp))))))
Now I modified this in an attempt to support nested expressions to the following:
(define (length exp)
(cond
((null? exp) 0)
((list? (car exp)) (+ 1 (length (cdr exp))))
(#t (length (cdr exp)))))
This works for expressions with no nests, but is always 1 less than the answer for nested expressions. This is because taking the example above, ((if x then (x = y)(z = w))), this will look at if at first and which satisfies the third condition, returning the cdr (the rest of the expression as a list) into length. The same happens up until (x=y) is reached, at which point a +1 is returned. This means that the expression (if x then .... ) has not been counted.
In what ways would I be able to account for it? Adding +2 will over-count un-nested expressions.
I will need this to work in one function as nesting can happen anywhere, so:
((x = y) (if y then (z = w)))
At first sight, your code only recurses to the right (cdr-side) and not to the left (car-side), so that definitely is a problem there.
On second sight, this is even a little bit more tricky than that, because you are not exactly counting conses; you need to differentiate the case where a cons starts a proper list vs where it's the cdr of a list. If you were to recurse to the car and cdr, that information would be lost. We need to iterate over the sexp as a proper list,
(defun count-proper-list (sexp)
(cond ((atom sexp) 0)
(t (1+ (reduce #'+ (mapcar #'count-proper-list sexp))))))
But this will also count the top level list, therefor always return one more than what you seem to want. So perhaps,
(defun count-proper-sublist (sexp)
(1- (count-proper-list sexp)))