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.
Related
From the documentation of equals? in Racket:
Equal? recursively compares the contents of pairs, vectors, and strings, applying eqv? on other objects such as numbers and symbols. A rule of thumb is that objects are generally equal? if they print the same. Equal? may fail to terminate if its arguments are circular data structures.
(equal? 'a 'a) ===> #t`
(equal? '(a) '(a)) ===> #t`
What exactly is a vector in scheme? For example, is (1. 2) a vector? Is (1 2) a vector? Is (1 2 3 4) a vector? etc.
The docs list the mention of vector and vector? etc, but I'm wondering if they just use vector to mean "list" or something else: https://people.csail.mit.edu/jaffer/r5rs/Disjointness-of-types.html#Disjointness-of-types
A vector is, well, a vector. It's a different data structure, similar to what we normally call an "array" in other programming languages. It comes in two flavors, mutable and immutable - for example:
(vector 1 2 3) ; creates a mutable vector via a procedure call
=> '#(1 2 3)
'#(1 2 3) ; an immutable vector literal
=> '#(1 2 3)
(vector-length '#(1 2 3))
=> 3
(vector-ref '#(1 2 3) 1)
=> 2
(define vec (vector 1 2 3))
(vector-set! vec 1 'x)
vec
=> '#(1 x 3)
You might be asking yourself what are the advantages of a vector, compared to a good old list. Well, that mutable vectors can be modified it in place, and accessing an element given its index is a constant O(1) operation, whereas in a list the same operation is O(n). Similarly for the length operation. The disadvantage is that you cannot grow a vector's size after it's created (unlike Python lists); if you need to add more elements you have to create a new vector.
There are lots of vector-specific operations that mirror the procedures available for lists; we have vector-map, vector-filter and so on, but we also have things like vector-map! that modify the vector in-place, without creating a new one. Take a look at the documentation for more details!
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 need to find the leaves in a list in Scheme.
For example, if I have (1 (2 3) (4 (5) (7 (8) (10 11 12)))))), my leaves are (8) and (10 11 12). So my function will return (1 (2 3) (4 (5) (7 leaf1 leaf2))))).
Definition: a leaf is an element with the deepest nesting possible.
Examples: In (1 (2 (3))) the element (3) is a leaf.
In ((1 2) (3 4)) the elements (1 2) and (3 4) are leaves.
I tried to use the map function, that will check if the list is composed from lists. If it is - so I call the function again, and if not, I break and change the lists to symbols of leafs. It doesn't work.
I have been stuck on it for 2 days. I am trying to find an idea, not an implementation. Thanks.
This is a little tricky to get right. Here are a few suggestions on one way to do it:
As stated, the problem is to walk the list, finding the most deeply nested lists that don't contain any other lists (these are sometimes called "lists of atoms"), and replacing them with something else. It's possible to do this in one recursive function, but I think it's clearer to break it up into parts:
First, we need a predicate (a function that returns a boolean #t or #f) to determine whether a list is a list of atoms. (This is sometimes called lat?). You can write this as a simple recursive function, or you could use the library functions any and list?
Then we need a function (define (max-lat-depth lst) ...) to find how deeply nested the most-deeply-nested list of atoms in its argument is. This will be a doubly recursive function -- it needs to check the first of each list as well as all the elements in the rest. We can define max-lat-depth as follows:
a. If the argument is a lat itself, the maximum depth is zero, so (max-lat-depth '(1 2 3)) == 0
b. If the first element of the argument isn't a list, it can't affect the maximum nesting depth overall. So in this case the max-lat-depth of the whole argument will be the same as the max-lat-depth of the rest (cdr) of the list: (max-lat-depth '(1 (2 (3 4))) == (max-lat-depth '((2 (3 4))) == 2
c. The tricky case: if the first element of the argument is a list (as in ((1 2) (3 4))), we'll need to recur on both the first (or car) and rest (or cdr) of lst, returning the maximum of these values. However, we need to add 1 to one of these two results before taking the maximum. I'll let you figure out which one and why.
Finally, we write a function (define (replace-lats-at-depth depth lst r) ...) that will take a nesting depth as returned from max-lat-depth, a list lst, and a replacement r. It will return a copy of lst where all the lists-of-atoms at depth depth have been replaced by r. For example:
(replace-lats-at-depth 0 '(1 2 3) '*) == '*
(replace-lats-at-depth 1 '(1 (2) 3) '*) == '(1 * 3).
Like max-lat-depth, replace-lats-at-depth recurs on both the first and rest of lst. It will call cons on the result of its recursive calls to construct a new tree structure. Also like max-lat-depth, it has several cases, and it will need to subtract 1 from depth in one of its recursive calls.
Once you have replace-lats-at-depth working to replace the nested lists with a constant value r, it shouldn't be too hard to improve it with a function that produces leaf1, leaf2, etc. as in your original example.
I hope that's helpful without saying too much. Let me know if not and I can try to clarify.
In Structure and Interpretation of Computer Programs (SICP) Section 2.2.3 several functions are defined using:
(accumulate cons nil
(filter pred
(map op sequence)))
Two examples that make use of this operate on a list of the fibonacci numbers, even-fibs and list-fib-squares.
The accumulate, filter and map functions are defined in section 2.2 as well. The part that's confusing me is why the authors included the accumulate here. accumulate takes 3 parameters:
A binary function to be applied
An initial value, used as the rightmost parameter to the function
A list to which the function will be applied
An example of applying accumulate to a list using the definition in the book:
(accumulate cons nil (list 1 2 3))
=> (cons 1 (cons 2 (cons 3 nil)))
=> (1 2 3)
Since the third parameter is a list, (accumulate cons nil some-list) will just return some-list, and in this case the result of (filter pred (map op sequence)) is a list.
Is there a reason for this use of accumulate other than consistency with other similarly structured functions in the section?
I'm certain that those two uses of accumulate are merely illustrative of the fact that "consing elements to construct a list" can be treated as an accumulative process in the same way that "multiplying numbers to obtain a product" or "summing numbers to obtain a total" can. You're correct that the accumulation is effectively a no-op.
(Aside: Note that this could obviously be a more useful operation if the output of filter and input of accumulate was not a list; for example, if it represented a lazily generated sequence.)
I realize this is a total n00b question, but I'm curious and I thought I might get a better explanation here than anywhere else. Here's a list (I'm using Dr. Scheme)
> (list 1 2 3)
(1 2 3)
Which I think is just sugar for this:
> (cons 1 (cons 2 (cons 3 null)))
(1 2 3)
This, on the other hand, does something else:
> (cons 1 (cons 2 3))
(1 2 . 3)
My questions is, why is that different? What's the point of requiring the null at the end of the list?
The definition of a list is recursive.
1. The null list (empty list) is a list
2. A list is made up of an item cons a list
So these are lists:
1. null => () --read as empty list
2. cons 3 null => (3)
3. cons2 (cons 3 null) => (2, 3)
The last example you gave cons 2 3 does not conform to this list definition so its not a list. That is cons accepts an item and a list. 3 is not a list.
cons adds a new element to the beginning of a list, so what you're doing when you write:
(cons 1 (cons 2 (cons 3 null)))
is recursively adding items to an ever-growing list, starting with null, which is defined to be the empty-list (). When you call (cons 2 3) you're not starting with the empty list to begin with, so are not constructing a list by appending 2 to its beginning.
Lisps, including Scheme, are dynamically typed, and 'the lisp way' is to have many functions over a single data structure rather than different data structures for different tasks.
So the question "What's the point of requiring the null at the end of the list?" isn't quite the right one to ask.
The cons function does not require you to give a cons object or nil as its second argument. If the second argument is not a cons object or nil, then you get a pair rather than a list, and the runtime doesn't print it using list notation but with a dot.
So if you want to construct something which is shaped like a list, then give cons a list as its second argument. If you want to construct something else, then give cons something else as its second argument.
Pairs are useful if you want a data structure that has exactly two values in it. With a pair, you don't need the nil at the end to mark its length, so it's a bit more efficient. A list of pairs is a simple implementation of a map of key to value; common lisp has functions to support such property lists as part of its standard library.
So the real question is "why can you construct both pairs and lists with the same cons function?", and the answer is "why have two data structures when you only need one?"
A cons statement is used to allocate a pair whose car is obj1 and whose cdr is obj2
(cons obj1 obj2)
Therefore, it is necessary to end a cons statement with a null so that we know we are the end of the list.
> (cons 1 (cons 2 3))
(1 2 . 3)
In that example, the cdr would be a pair <2,3> where 2 is the car and 3 is the cdr. Not the same as:
(list 1 2 3)