I want to understand what and and or represent in Racket when used with lists. When I do something like this -
> (or `(1 2) `(1 3))
'(1 2)
What is the result representing? I thought when we use or with two lists we would get a union of the lists. That is clearly not what is happening here. So, I thought it is interpreted as boolean values and that is why `(1 2) is not a false value. Hence, the result is `(1 2). But what about this? -
> (and `(1 2) `(1 3))
'(1 3)
How can I justify this?
or is looking for the first truthy value, checking arguments from left to right. Every value other than #false is truthy. If any is found, it's returned. In your example, '(1 2) is the first truthy value from the left, so it's returned.
Or called with no arguments returns #false, because no truthy values were found:
> (or)
#f
And checks whether all values are truthy, so it has to examine all of them, going from left to the right, and it can return #false (if any #false is found) or, if all values are truthy, last of them. In your example, '(1 3) is the last truthy value, so it's returned.
And called with no arguments returns #true, because no #false was found:
> (and)
#t
Read also the docs about or and and.
By the way, there is a difference between ' and `. First one is quote, the second one is quasiquote. In this very example, it doesn't matter, but you should know the difference between them.
And if you were really looking for union function, check racket/set library:
(require racket/set)
(set-union (set 1 2) (set 1 3))
=> (set 1 3 2)
Related
I was a bit surprised by this racket code printing nay when I expected yeah:
(define five 5)
(case 5
[(five) "yeah"]
[else "nay"])
Looking at the racket documentation for case makes it clearer:
The selected clause is the first one with a datum whose quoted form is equal? to the result of val-expr.
So it's about quotation. I'm pretty sure that I did not yet fully grasp what quotation in lisps can buy me. I understand it in the viewpoint of macros and AST transformation. However I'm confused why is it helpful in the case of case for instance..?
I'm also curious, with this specification of case, can I use it to achieve what I wanted to (compare the actual values, not the quoted value), or should I use another construct for that? (cond, while strictly more powerful, is more verbose for simple cases, since you must repeat the predicate at each condition).
The problem is that case introduces implicit quote forms, which cause your example to work for 'five (whose value is 'five), instead of five (whose value is 5).
I almost never use case because of exactly this problem. Instead I use racket's match form with the == pattern:
(define five 5)
(define (f x)
(match x
[(== five) "yeah"]
[_ "nay"]))
(f 5) ; "yeah"
(f 6) ; "nay"
This produces "yeah" on only the value 5, just like you expected. If you wanted it to return "yeah" when it's equal to either five or six, you can use an or pattern:
(define five 5)
(define six 6)
(define (f x)
(match x
[(or (== five) (== six)) "yeah"]
[_ "nay"]))
(f 5) ; "yeah"
(f 6) ; "yeah"
(f 7) ; "nay"
And if you really want to match against quoted datums, you can do that by writing an explicit quote form.
(define (f x)
(match x
[(or 'five 'six) "yeah"]
[_ "nay"]))
(f 5) ; "nay"
(f 6) ; "nay"
(f 7) ; "nay"
(f 'five) ; "yeah"
(f 'six) ; "yeah"
These quote forms are implicit and invisible when you use case, lurking there waiting to cause confusion.
The Racket documentation gives this grammar:
(case val-expr case-clause ...)
where
case-clause = [(datum ...) then-body ...+]
| [else then-body ...+]
Let's compare to your example:
(define five 5)
(case 5 ; (case val-expr
[(five) "yeah"] ; [(datum) then-body1]
[else "nay"]) ; [else then-body2])
We see that (five) is interpreted as (datum). This means that five is
a piece of data (here a symbol), not an expression (later to be evaluated).
Your example of case is evaluated like this:
First the expression 5 is evaluated. The result is the value 5.
Now we look at a clause at a time. The first clause is [(five) "yeah"].
Is the value 5 equal (in the sense of equal?) to one of the datums in (five)? No, so we look at the next clause: [else "nay"]. It is an else-clause so the expression "nay" is evaluated and the result is the value "nay".
The result of the case-expression is thus the value "nay".
Note 1: The left-hand sides of case-clauses are datums (think: they are implicitly quoted).
Note 2: The result of val-expr is compared to the clause datums using equal?. (This is in contrast to Scheme, which uses eqv?.
UPDATE
Why include case? Let's see how one can write the example using cond:
(define five 5)
(let ([val five])
(cond
[(member val '(five)) "yeah"]
[(member val '(six seven)) "yeah"] ; added
[else "nay"])
This shows that one could do without case and just use cond.
However - which version is easier to read?
For a case expression it is easy to see which datums the value is compared to.
Here one must look closely to find the datums. Also in the example we know beforehand that we are trying to find the value among a few list of datums. In general we need to examine a cond-expression more closely to see that's what's happening.
In short: having a case-expression increases readability of your code.
For the historically interested: https://groups.csail.mit.edu/mac/ftpdir/scheme-mail/HTML/rrrs-1986/msg00080.html disussed whether to use eqv? or equal? for case.
UPDATE 2
I'll attempt to given an answer to:
I'm still not clear on the quotation vs working simply on the values though.
I'm wondering specifically why doing the quotation, why working on datum instead
of working on values. Didn't get that bit yet.
Both approaches make sense.
Let's for the sake of argument look at the case where case uses expressions rather than datums in the left hand side of a clause. Also following the Scheme tradition, let's assume eqv? is used for the comparison. Let's call such a
case-expression for ecase (short for expression-case).
The grammar becomes:
(ecase val-expr ecase-clause ...)
where
ecase-clause = [(expr ...) then-body ...+]
| [else then-body ...+]
Your example now becomes:
(define five 5)
(ecase five
[('five) "yeah"]
[else "nay")
This doesn't look too bad and the result is what we are used to.
However consider this example:
(ecase '(3 4)
[('five (list 3 4) "yeah"]
[else "nay")
The result of this would be "nay". The two lists resulting from evaluating the expressions '(3 4) and (list 3 4) are not equal in the sense of eqv?.
This shows that if one chooses to use eqv? for comparisions, having expressions available on the left hand side won't be helpful. The only values that work with eqv? atomic values - and therefore one could just as well use implicit quotations and restrict the left hand side to datums.
Now if equal? was used it would make much more sense to use expressions on the left hand side. The original Racket version of case was the same as the one in Scheme (i.e. it used eq?) later on it was changed to used equal?. If case was designed from scratch, I think, expressions would be allowed rather than datums.
The only remaining issue: Why did the authors of Scheme choose eqv? over equal? for comparisons? My intuition is that the reason were performance (which back in the day was more important than now). The linked to post from the rrrs-authors mailing list gives two options. If you dig a little further you might be able to find responses.
I can't find a reference right now, but case statements use literal, unevaluated data in their different clauses because it is both a frequent use-case and more easily subject to efficient compilation.
You could probably write your own version of Clojure's condp macro or a custom conditional operator to handle your use case.
I am working on a scheme project and I encountered with a problem with quotes.
When I use (apply + '(1 2 '3)) racket gives an error, on the other hand (+ 1 2 '3) works fine. At some point in the project I need to be able to do something like (apply + '(1 2 '3)) since I am taking the values from a given file and I have no freedom to use (+ 1 2 '3). I tried to write an add function that adds all the values in the list but it didn't work either. How can I solve this problem?
Thanks.
'x is an abbreviation for (quote x). In early lisps it was a reader macro and today some print routines will actually print (quote x) as 'x for convenience. Even when the printer chooses to print it as an abbriavtion it still a list with two elements so (car ''x) is quote.
(+ '1 '2 '3) works fine because all arguments are being evaluated. + gets evaluated to a procedural object #<+> (just a procedural representation of an implementation of global variable +), '1 evaluates to it's data, the number 1 and so on. In the end application of the procedure happens on the arguments (1 2 3). Numbers are self evaluating so if you were not to quote them 1 would evaluate to 1 and so on and you get the same result. Not all values are self evaluating.
With (apply + '(1 2 3)) all 3 arguments get evaluate. apply and + both to procedural objects and '(1 2 3) to the list (1 2 3). The procedure #<+> gets applied with the arguments (1 2 3)
With (apply + '(1 2 '3)) all 3 arguments gets evaluated. apply and + both to procedural objects and '(1 2 '3) to the list (1 2 (quote 3)). The evaluator only looks at the outer quote so the 3 is still quoted and now it's part of the data. The procedure #<+> gets applied with the arguments (1 2 (quote 3)). It won't work since #<+> only knows how to add numbers and not lists. You can expect a contract violation, an error of some kind.
When evaluated the result is data. If you have more quotes besides the outer one it isn't a language command any more but a list with the symbol quote and a second value. It's not handled specially like qith outer quotes.
The problem here is the meaning of the quote '.
The expression ' means "produce a value that prints as ".
For example '3 produces the value 3 since the value 3 is printed as 3.
In '(1 b) you get a list whose first element is the number 1 and whose second element is the symbol b.
Now you need to know one more thing about the quote. The quote is a reader abbreviation. When '<something> is read, the reader produces the form (quote <something>). This means that'(1 'b)produces the list(1 (quote b)). This is a list whose second element is the list(quote b), wherequote` is the symbol quote.
In your example you tried to (apply + '(1 2 '3)). The value produced by '(1 2 '3) is the list (1 2 (quote 3)). Since + adds only numbers, you got an error message.
If you want to read in numbers from a file, then read them as numbers and everything ought to be fine.
try to figure out how to use "append" in Scheme
the concept of append that I can find like this:
----- part 1: understanding the concept of append in Scheme-----
1) append takes two or more lists and constructs a new list with all of their elements.
2) append requires that its arguments are lists, and makes a list whose elements are the elements of those lists. it concatenates the lists it is given. (It effectively conses the elements of the other lists onto the last list to create the result list.)
3) It only concatenates the top-level structure ==> [Q1] what does it mean "only concatenates the top-level"?
4) however--it doesn't "flatten" nested structures.
==> [Q2] what is "flatten" ? (I saw many places this "flatten" but I didn't figure out yet)
==> [Q3] why append does not "flatten" nested structures.
---------- Part 2: how to using append in Scheme --------------------------------
then I looked around to try to use "append" and I saw other discussion
based on the other discussion, I try this implementation
[code 1]
(define (tst-foldr-append lst)
(foldr
(lambda (element acc) (append acc (list element)))
lst
'())
)
it works, but I am struggling to understand that this part ...(append acc (list element)...
what exactly "append" is doing in code 1, to me, it just flipping.
then why it can't be used other logics e.g.
i) simply just flip or
iii).... cons (acc element).....
[Q4] why it have to be "append" in code 1??? Is that because of something to do with foldr ??
again, sorry for the long question, but I think it is all related.
Q1/2/3: What is this "flattening" thing?
Scheme/Lisp/Racket make it very very easy to use lists. Lists are easy to construct and easy to operate on. As a result, they are often nested. So, for instance
`(a b 34)
denotes a list of three elements: two symbols and a number. However,
`(a (b c) 34)
denotes a list of three elements: a symbol, a list, and a number.
The word "flatten" is used to refer to the operation that turns
`(3 ((b) c) (d (e f)))
into
`(3 b c d e f)
That is, the lists-within-lists are "flattened".
The 'append' function does not flatten lists; it just combines them. So, for instance,
(append `(3 (b c) d) `(a (9)))
would produce
`(3 (b c) d a (9))
Another way of saying it is this: if you apply 'append' to a list of length 3 and a list of length 2, the result will be of length 5.
Q4/5: Foldl really has nothing to do with append. I think I would ask a separate question about foldl if I were you.
Final advice: go check out htdp.org .
Q1: It means that sublists are not recursively appended, only the top-most elements are concatenated, for example:
(append '((1) (2)) '((3) (4)))
=> '((1) (2) (3) (4))
Q2: Related to the previous question, flattening a list gets rid of the sublists:
(flatten '((1) (2) (3) (4)))
=> '(1 2 3 4)
Q3: By design, because append only concatenates two lists, for flattening nested structures use flatten.
Q4: Please read the documentation before asking this kind of questions. append is simply a different procedure, not necessarily related to foldr, but they can be used together; it concatenates a list with an element (if the "element" is a list the result will be a proper list). cons just sticks together two things, no matter their type whereas append always returns a list (proper or improper) as output. For example, for appending one element at the end you can do this:
(append '(1 2) '(3))
=> '(1 2 3)
But these expressions will give different results (tested in Racket):
(append '(1 2) 3)
=> '(1 2 . 3)
(cons '(1 2) '(3))
=> '((1 2) 3)
(cons '(1 2) 3)
=> '((1 2) . 3)
Q5: No, cons will work fine here. You wouldn't be asking any of this if you simply tested each procedure to see how they work. Please understand what you're using by reading the documentation and writing little examples, it's the only way you'll ever learn how to program.
First time stackoverflow user but occasional lurker, hope you guys can help me out.
So the first part of my assignment is to drop all 'leading zeros' in a list.
ex: (0 0 0 0 0 1 0 1 0 1) -> (1 0 1 0 1)
To do this, I thought to use an IF statement to check whether the first element was a 0 or not, and to recursively call the rest of the list until the there were no more leading zeros. As I have basically no idea how to program in Scheme, through searching the internet, I came up with what you see below. However when I run it, DrRacket tells me there are no arguments- I assume this either a syntactical error.. or more likely, I have no idea what I'm doing. So, if you could help me out, I'd really appreciate it!
>(define zz
> (lambda (n)
> (if (= (car (n)) 0)
> (zz (cdr (n)))
> ((n)))))
>
>(remove '(0 0 0 0 1 0 1 0))
The error I get in DrRacket is:
"procedure application: expected procedure, given: (0 0 0 0 1 0 1 0) (no arguments)"
Again, thanks a lot! (P.S. Sorry if the formatting is a little odd...)
EDIT
Okay, changing up some stuff, I now get a "expects type as 1st argument, given: (0 0 0 0 0 1 0 1 0); other arguments were: 0" error flagged at my if statement.
>(define zz
> (lambda n
> (if (= (car n) 0) <----- here
> (zz(cdr n))
> (n))))
EDIT 2
>(define zz
> (lambda (n)
> (if (= (car n) 0)
> (zz (cdr n))
> n)))
It works, thank you very much!
(num) is not correct - you're trying to call 42 or whatever as a command. (Also, your if syntax is off; you may want to do read more code to get a better feel for the syntax).
This should compile:
(define remove
(lambda (num)
(if (= (car num) 0)
(remove (cdr num))
num)))
Parenthesis in Lisp are for calling functions, unless used in quotes.
Okay, the OP asked about a general rundown of the syntax for Scheme.
a - A symbol, which is looked up by the evaluator and substituted for its value. Some symbols (such as 42) evaluate to themselves.
'a - This "quotes" the symbol and transforms it into (quote a). quote prevents its argument from being evaluated - instead, the value a is returned. Not the string "a", not the result of looking up a, a itself. This also works for lists ('(1 2 3))
(if <expr> <true-value> <false-value>) - This evaluates <expr>, and sees if its value is truthy or not, and executes the corresponding value.
(cond (<expr> <true-value>)
...
(else <false-value>)) - This runs though its arguments, and evaluates the car of it to see if it is true. If it is, the value of evaluating the cdr is returned. Otherwise, it skips to the next value.
(define <name> <expr>) - Sets the value of evaluating the second argument to the name of the first argument.
(lambda <arg-list> <body>) - Creates a procedure which is the result of binding the arguments passed in to the names present in the second argument and evaluating the third argument.
(<func> <arg1> <arg2> ... <argn>) - If the evaluator finds out that none of the above patterns match, then it calls the car of the list as a function, with the arguments in the cdr.
Since (list 1 2 3) yields (1 2 3) and (quote (1 2 3)) yields (1 2 3), what is the rationale for having both?
Since Scheme is otherwise so spare, these must have some meaningful difference. What is that?
In the example you mentioned quote and list have the same result because numeric constants evaluate to themselves. If you use expressions that are not self-evaluating in the list (say variables or function calls), you'll see the difference:
(quote (a b c)) will give you a list that contains the symbols a, b and c while (list a b c) will give you a list containing the values of the variables a, b and c (or an error if the variables do not exist).
List creates a list, so (list 1 2 3) creates a three-element list.
Quote prevents evaluation. Without quote, the expression (1 2 3) would be evaluated as the function 1 called with arguments 2 and 3, which obviously makes no sense. Quote prevents evaluation and just returns the list, which is specified literally in its external printable form as (1 2 3).