empty? does not stop Racket from running expression - racket

I'm trying to have function get a number out of a structure that is inside a list. When the list is empty, the function should output +inf.0.
However, when I input an empty list like this: (function (list empty)), the +inf.0 part is ignored and the else expression is executed anyway leading to the error that struct-xyz gets an empty list or that first doesn't allow empty lists. What am I doing wrong?
(define (function datalist))
(cond
[(empty? datalist) +inf.0]
[else(struct-xyz (first datalist))]))
This is all in the Beginning Student Teaching Language.

You should call (function empty), not (function (list empty)). (function (list empty)) is calling your function on a list that is not empty, it contains one element (another list, which is empty).

Related

can anyone explain to me why this lisp code doesn't work?

it should count the elements of a list, but says "*** - +: NIL is not a number"
(setq A '(2 3 4 3 2 6 7 8 4 3 5 6))
(defun big (A)
(if (not (null (car A))) (+ 1 (big (cdr A))) ) ;if the first element is not null, add 1 to the count of the elements to the rest of the list
)
(print (big A))
Type error
An IF expression has either 2 or 3 arguments:
(if test something)
(if test something something-else)
When it only has 2 arguments, it is as-if the third argument, something-else, was NIL. That means that the IF expression evaluates to NIL when the test expression is false. In your case, you have 2 arguments:
(defun big (A)
(if (not (null (car A)))
;; "then" branch (when condition is true)
(+ 1 (big (cdr A)))
;; no "else" branch (when condition is false)
))
So you know that sometimes a call to big might return NIL.
But, you also write:
(+ 1 (big (cdr A)))
This expression looks like (+ 1 x) with x being a call to big, meaning that x might evaluate to NIL in some cases. That's the case you hit with the debugger.
If you make sure the if expression always return a number, by returning for example zero in the else branch, then you won't have the same error about trying to add a number to NIL.
Counting elements
But then, you would still have other bugs, since you say that the function big "should count the elements of a list". If you want to count the element of a list, you never need to look at the elements stored in the list, you only need to know that they exist.
When you write (car a), you are accessing the first element of the list. You then check if that value is non-nil, but it is perfectly valid to have a list filled with NIL values:
'(NIL NIL NIL)
That list has 3 elements, and at no point the fact that they are NIL should matter when counting them.
A recursive function working on a list typically needs to cover two cases, namely if the list is empty or not. You check if the current list is empty by calling (null list) or (endp list) (just doing (if list ... ...) works too since NIL is the only false value).
The test for null car doesn’t do any good, cdr will return nil before car does.
You need a base case where you find you’re done and return something instead of recursing. Right now you don’t have that. Look at examples of simple recursive functions and see how they have a base case.
To count the elements in a list there are two cases:
The base case where the list is empty (return 0)
The recursive case where the list isn’t empty (return 1 + the count of the cdr of the passed in list)

Using Lisp: define a function that takes a list and a number and returns true if the number occurs in the list

I am new to lisp and I have a problem, I'm trying to find the number in the list but it is not working. I haven't made the return statement yet
(defun num (x 'y)
(if (member x '(y)) 't nil))
(write (num 10 '(5 10 15 20)))
My output just outputs the nil instead of doing the function and I'm confused of what I am doing wrong.
Solution
(defun member-p (element list)
"Return T if the object is present in the list"
(not (null (member element list))))
The not/null pattern is equivalent to (if (member element list) t nil) but is more common.
In fact, you do not really need this separate function,
member is good enough.
The -p suffix stands for predicate, cf. integerp and upper-case-p.
Your code
You cannot quote lambda list elements, so you need to replace defun num (x 'y) with defun num (x y)
You need not quote t
Quoting '(y) makes no sense, replace it with y.
You do not need to write the function call, the REPL will do it for you.
See also
When to use ' (or quote) in Lisp?
Can you program without REPL on Lisp?
You are almost certainly expected to not just use member, but to write a function which does what you need (obviously in real life you would just use member because that's what it's for).
So. To know if an object is in a list:
if the list is empty it's not;
if the head of the list is equal to the object it is;
otherwise it is in the list if it's in the tail of the list.
And you turn this into a function very straightforwardly:
(defun num-in-list-p (n l)
;; is N in L: N is assumed to be a number, L a list of numbers
(cond ((null l)
nil)
((= n (first l))
t)
(t
(num-in-list-p n (rest l)))))
You could use the built in position function which will return the index of the number if it is in the list:
(position 1 '(5 4 3 2 1))
If you want to define your own function:
CL-USER> (defun our-member(obj lst)
(if(zerop (length lst))
nil
(if(equal(car lst)obj)
T
(our-member obj (cdr lst)))))
OUR-MEMBER
CL-USER> (our-member 1 '(5 4 3 2 1))
T
CL-USER> (our-member 99 '(1 2 3 4 5))
NIL
We can create a function called "our-member" that will take an object (in your case a number) and a list (in your case a list of numbers) as an argument. In this situation our "base-case" will be whether or not the length of the list is equal to zero. If it is and we still haven't found a match, we will return nil. Otherwise, we will check to see if the car of the list (the first element in the list) is equal to the obj that we passed. If so, we will return T (true). However, if it is not, we will call the function again passing the object and the cdr of the list (everything after the car of the list) to the function again, until there are no items left within the list. As you can see, The first example of a call to this function returns T, and the second example call returns NIL.
What makes this utility function a good example is that it essentially shows you the under workings of the member function as well and what is going on inside.

How do I create a version of filter using map (racket ISL)

I am Tasked (in exercise 8) with creating a function in Intermediate Student Language (Racket) that receives a list of numbers and a list of lists of numbers; each list is the same length. Name the first list breakpoints and the second LoR (list of rows). This function should be defined using map and should filter each row in LoR so that only numbers larger than that the n'th row in LoR only contains values larger than the n'th value in breakpoints-- here is an example for clarity:
(define breakpoints (list 7 2 15))
(define LoR (list (list 3 25 13)
(list 1 2 11)
(list 22 4 8)))
would output...
(list (list 25 13) (list 11) (list 22))
Doing this without using map would be fine and I understand the problem in that sense, but I am having trouble figuring out how to use map. I was thinking recursively in the sense that if the list of rows is not empty, I would cons the (filtered first row) to the (recursive call of the function using the rest of breakpoints and LoR) as so:
(define (parallel-filter breakpoints LoR)
(cond((empty? breakpoints) empty)
(else (cons ...
(parallel-filter (rest breakpoints) (rest LoR))))))
However, I'm not sure how to replace the ellipse with a map statement that would make the function work correctly-- as I understand, map's first parameter must be a one-parameter function and I'm not sure how to use map for this purpose. How do I proceed?
edited to correct output
We can solve this problem by combining map and filter, but first bear in mind this:
You can't use just map, because it will always return a list of the same length as the original. For excluding some elements we must use filter (or alternatively: use foldr to implement our own version of filter)
map can receive more than one list as parameter, as long as its function receives enough parameters - one from each list
Your sample output is wrong for the first sublist
With all of the above in mind, here's one possible solution:
(define (parallel-filter breakpoints LoR)
(map (lambda (brk lst) ; `brk` is from `breakpoints` and `lst` is from `LoR`
(filter (lambda (ele) (> ele brk)) ; filter each sublist from `LoR`
lst))
breakpoints
LoR))
It works as expected:
(parallel-filter breakpoints LoR)
=> (list (list 25 13) (list 11) (list 22))

Which object the function nth access to when applied to functional argument in Common Lisp?

I have some problem using accessor function nth. I pass a list to some function and make new binding to an element of the list in the function with nth, then when I call the list out of the function, it is modified, and it's not what I want! What happens?
Some examples
(defun test (x) (incf x)) => TEST
(setq a 1) => 1
(test a) => 2
a => 1
I understand what's going on above, but if we change everything to lists, something happens that I can't understand
(defun test (x) (incf (nth 0 x))) => TEST
(setq a '(1)) => (1)
(test a) => 2
a => (2)
I expected a to be (1), why it has been modified? I also tried other functions like car and first, result is the same.
PS, I tried it in Lispworks and SBCL, same result.
(defun test (x) (incf x)) => TEST
(setq a 1) => 1
(test a) => 2
a => 1
You pass 1 to test. Within test you modified the local variable x. a is not changed and can't be changed that way - we pass the value of a not a reference to a.
(defun test (x) (incf (nth 0 x))) => TEST
(setq a '(1)) => (1)
(test a) => 2
a => (2)
You pass the list (1) to test. The list is not copied. The local variable x points to the first cons cell in the list. You then modify the car of the first cons cell to 2. Since the list is not copied, you modify the passed list. a also points to the first cons cell of that list. So it is also (2).
If you don't want anything to be modified, don't use incf. Use 1+. The only reason to use incf is because you want its side-effect.
As to why this is happening, arguments are evaluated before being passed to a function. When you call test with a being 1, you are passing the value 1, which cannot be modified. When a resolves to a list, you are passing a list, which can be setf'd all over the shop, if you've chosen to use destructive functions.
See the documentation on nth. nth returns a place that setq can then operate on.
nth may be used to specify a place to setf. Specifically,
(setf (nth n list) new-object) == (setf (car (nthcdr n list)) new-object)

Treating the values from a list of slots and strings

I want to do a macro in common lisp which is supposed to take in one of its arguments a list made of slots and strings. Here is the prototype :
(defclass time-info ()
((name :initarg name)
(calls :initarg calls)
(second :initarg second)
(consing :initarg consing)
(gc-run-time :initarg gc-run-time)))
(defun print-table (output arg-list time-info-list) ())
The idea is to print a table based on the arg-list which defines its structure. Here is an example of a call to the function:
(print-table *trace-output*
'("|" name "||" calls "|" second "\")
my-time-info-list)
This print a table in ascII on the trace output. The problem, is that I don't know how to explicitely get the elements of the list to use them in the different parts of my macro.
I have no idea how to do this yet, but I'm sure it can be done. Maybe you can help me :)
I would base this on format. The idea is to build a format string
from your arg-list.
I define a helper function for that:
(defun make-format-string-and-args (arg-list)
(let ((symbols ()))
(values (apply #'concatenate 'string
(mapcar (lambda (arg)
(ctypecase arg
(string
(cl-ppcre:regex-replace-all "~" arg "~~"))
(symbol
(push arg symbols)
"~a")))
arg-list))
(nreverse symbols))))
Note that ~ must be doubled in format strings in order to escape them.
The printing macro itself then just produces a mapcar of format:
(defmacro print-table (stream arg-list time-info-list)
(let ((time-info (gensym)))
(multiple-value-bind (format-string arguments)
(make-format-string-and-args arg-list)
`(mapcar (lambda (,time-info)
(format ,stream ,format-string
,#(mapcar (lambda (arg)
(list arg time-info))
arguments)))
,time-info-list)))
You can then call it like this:
(print-table *trace-output*
("|" name "||" calls "|" second "\\")
my-time-info-list)
Please note the following errors in your code:
You need to escape \ in strings.
Second is already a function name exported from the common-lisp
package. You should not clobber that with a generic function.
You need to be more precise with your requirements. Macros and Functions are different things. Arrays and Lists are also different.
We need to iterate over the TIME-INFO-LIST. So that's the first DOLIST.
The table has a description for a line. Each item in the description is either a slot-name or a string. So we iterate over the description. That's the second DOLIST. A string is just printed. A symbol is a slot-name, where we retrieve the slot-value from the current time-info instance.
(defun print-table (stream line-format-description time-info-list)
(dolist (time-info time-info-list)
(terpri stream)
(dolist (slot-or-string line-format-description)
(princ (etypecase slot-or-string
(string slot-or-string)
(symbol (slot-value time-info slot-or-string)))
stream))))
Test:
> (print-table *standard-output*
'("|" name "||" calls "|" second "\\")
(list (make-instance 'time-info
:name "foo"
:calls 100
:second 10)
(make-instance 'time-info
:name "bar"
:calls 20
:second 20)))
|foo||100|10\
|bar||20|20\
First, you probably don't want the quote there, if you're using a macro (you do want it there if you're using a function, however). Second, do you want any padding between your separators and your values? Third, you're probably better off with a function, rather than a macro.
You also seem to be using "array" and "list" interchangeably. They're quite different things in Common Lisp. There are operations that work on generic sequences, but typically you would use one way of iterating over a list and another to iterate over an array.