I am using DrRacket, version 6.4, English to create a small application in Scheme.
I was wondering if there was a more efficient way to concatenate the following code.
[it works I am just not sure if it is the cleanest since I am new to Scheme]
(display "Rolling ")
(display (number->string (- 5 (length my-rolled-dice))))
(display " dice\n")
(display "You rolled\n")
(define my-roll (make-list-of-random-numbers (- 5 (length my-rolled-dice) ) 6))
(display my-roll)
(display "\n")
I am looking for the following output to the screen
Rolling 5 dice
You rolled
(3 1 3 6 6)
Is there a cleaner way to write this or is this as clean as it gets in Scheme?
Use printf, it's shorter:
(printf "Rolling ~a dice~n" (- 5 (length my-rolled-dice)))
(printf "You rolled~n~a" (make-list-of-random-numbers (- 5 (length my-rolled-dice)) 6))
In Racket, printf works, but in Scheme, like MIT-Scheme, there is no printf. In that case you can use map and display. For example:
(map display
(list "Rolling " (- 5 (length my-rolled-dice))
" dice\nYou rolled\n" my-roll "\n"))
Related
I'm trying to take in user input and add it to a list but I have not been able to get it working. I'm still new to scheme and have been browsing around to try to figure it out but I haven't had any luck.
(display "Continue to enter numbers until satisfied then enter e to end")
(newline)
(define (intlist number)
(define number(read-line))
(cond (number? number)
(cons lst (number))
(else
(display lst)
done')))
this is what I have so far. Any help or direction to where I can learn a bit more is appreciated.
Your solution is almost correct, but it doesn't work, because:
Variable lst doesn't exist and with this expression (number), you are calling some undefined function number.
done' is badly written 'done.
Function cons expects element as first argument and other element or list as second argument.
See these examples:
> (cons 1 2)
'(1 . 2)
> (cons 1 '())
'(1)
> (cons 1 (cons 2 (cons 3 '())))
'(1 2 3)
Last example is important here- your function will be recursive and it will return a cons cell in each step. If I will follow your solution, this can be enough:
(define (list-from-user)
(let ((number (read)))
(if (number? number)
(cons number (list-from-user))
'())))
(Note that I used read instead of read-line, because read-line returns string, and let instead of define.)
If you really want to wait for e, you must decide, what happens if user enters something that isn't number and isn't e- maybe just ignore it?
(define (list-from-user)
(let ((user-input (read)))
(cond ((number? user-input) (cons user-input (list-from-user)))
((eq? user-input 'e) '())
(else (list-from-user)))))
Then just add some wrapping function with output:
(define (my-fn)
(begin (display "Continue to enter numbers until satisfied then enter e to end")
(newline)
(list-from-user)))
and call it
> (my-fn)
Note that my function returns list with numbers, instead of some useless 'done, so I can use that function in other functions.
(define (sum-of-list)
(let ((lst (my-fn)))
(format "Sum of given list is ~a." (apply + lst))))
> (sum-of-list)
I've asked a few questions here about Scheme/SICP, and quite frequently the answers involve using the apply procedure, which I haven't seen in SICP, and in the book's Index, it only lists it one time, and it turns out to be a footnote.
Some examples of usage are basically every answer to this question: Going from Curry-0, 1, 2, to ...n.
I am interested in how apply works, and I wonder if some examples are available. How could the apply procedure be re-written into another function, such as rewriting map like this?
#lang sicp
(define (map func sequence)
(if (null? sequence) nil
(cons (func (car sequence)) (map func (cdr sequence)))))
It seems maybe it just does a function call with the first argument? Something like:
(apply list '(1 2 3 4 5)) ; --> (list 1 2 3 4 5)
(apply + '(1 2 3)) ; --> (+ 1 2 3)
So maybe something similar to this in Python?
>>> args=[1,2,3]
>>> func='max'
>>> getattr(__builtins__, func)(*args)
3
apply is used when you want to call a function with a dynamic number of arguments.
Your map function only allows you to call functions that take exactly one argument. You can use apply to map functions with different numbers of arguments, using a variable number of lists.
(define (map func . sequences)
(if (null? (car sequences))
'()
(cons (apply func (map car sequences))
(apply map func (map cdr sequences)))))
(map + '(1 2 3) '(4 5 6))
;; Output: (5 7 9)
You asked to see how apply could be coded, not how it can be used.
It can be coded as
#lang sicp
; (define (appl f xs) ; #lang racket
; (eval
; (cons f (map (lambda (x) (list 'quote x)) xs))))
(define (appl f xs) ; #lang r5rs, sicp
(eval
(cons f (map (lambda (x) (list 'quote x))
xs))
(null-environment 5)))
Trying it out in Racket under #lang sicp:
> (display (appl list '(1 2 3 4 5)))
(1 2 3 4 5)
> (display ( list 1 2 3 4 5 ))
(1 2 3 4 5)
> (appl + (list (+ 1 2) 3))
6
> ( + (+ 1 2) 3 )
6
> (display (appl map (cons list '((1 2 3) (10 20 30)))))
((1 10) (2 20) (3 30))
> (display ( map list '(1 2 3) '(10 20 30) ))
((1 10) (2 20) (3 30))
Here's the link to the docs about eval.
It requires an environment as the second argument, so we supply it with (null-environment 5) which just returns an empty environment, it looks like it. We don't actually need any environment here, as the evaluation of the arguments has already been done at that point.
I know that I can use format's ~:{ ~} operator to process a list of lists directly, e.g.
CL-USER> (format t "~:{<~A,~A> ~}~%" '((1 2) (3 4)))
<1,2> <3,4>
But now I have a list of conses, e.g. ((1 . 2) (3 . 4)) and the expression
(format t "~:{<~A,~A> ~}~%" '((1 . 2) (3 . 4)))
leads to SBCL complaining
The value
2
is not of type
LIST
Is there any format magic doing the trick without having to use an extra iteration with do or loop?
I see basically 4 options
Do not use format for the whole list
The straightforward solution is avoid the problem:
(loop
for (k . v) in '((1 . 2) (3 . 4))
do (format t "<~a,~a> " k v))
Custom format function
Alternatively, use Tilde Slash to call a function that prints cons-cells:
(defun cl-user::pp-cons (stream cons colonp atsignp)
(declare (ignore colonp atsignp))
(format stream "~a, ~a" (car cons) (cdr cons)))
(format nil "~/pp-cons/" (cons 1 2))
=> "1, 2"
Note that the function must be in the CL-USER package if you don't specify the package. If you want to customize how cells are printed, you need to pass the format through a special variable:
(defvar *fmt* "(~s . ~s)")
(defun cl-user::pp-cons (stream cons colonp atsignp)
(declare (ignore colonp atsignp))
(format stream *fmt* (car cons) (cdr cons)))
And then:
(let ((*fmt* "< ~a | ~a >"))
(format t "~/pp-cons/" (cons 1 2)))
=> < 1 | 2 >
Convert on printing
Build a fresh list, where improper lists are replaced by proper lists:
(format t
"~:{<~a,~a> ~}~%"
(series:collect 'list
(series:mapping (((k v) (series:scan-alist '((1 . 2) (3 . 4)))))
(list k v))))
The drawback is that the conversion needs to allocate memory, just for printing.
Change your data format
If proper lists are good for printing, maybe they are good for other operations too. A lot of standard functions expect proper lists. Note that the list ((1 2) (3 4)) is still an alist, the value is just wrapped in a cons-cell. If you decide to use this format from the start, you won't have to convert your lists.
I am new to Scheme so excuse me if I am using the wrong vocabulary. I want a function, I am calling it QandA, that will (among other things) display its raw or unprocessed argument. For example:
(QandA (+ 1 2)) should return the string "(+ 1 2) : 3"
(QandA (quote (+ 1 2))) should return "(quote (+ 1 2)) : (+ 1 2)"
What I first tried was:
(define (QandA x)
(display (quote x)) (display " : " ) (display x))
but then
(QandA (+ 1 2)) returns "x : 3"
I understand why this is wrong, but don't know where to look to find a solution.
So the question is, what do I replace (display (quote x)) with to get the behavior I require. Any hints welcome.
As #ymonad pointed out, this is a good scenario for using macros - because you need to defer the evaluation of the expression that's passed as parameter to QandA. Some of your previous questions were tagged racket, so here's an implementation for Racket using #lang racket (also works for #lang scheme) that returns a string, as requested (if you want to display the value replace format with printf):
(define-syntax-rule (QandA exp)
(format "~a : ~a" 'exp exp))
This is the output for the sample input given in the question:
(QandA (+ 1 2))
=> "(+ 1 2) : 3"
(QandA (quote (+ 1 2)))
=> "(quote (+ 1 2)) : (+ 1 2)"
As you see, you cannot achieve it using function since the argument is evaluated before it is passed to the function.
One solution is using macros, it can access to the unprocessed expression and create another expression.
Here's an example that works on guile
(define-syntax QandA
(syntax-rules ()
((QandA arg)
(begin (display (quote arg))(display " : ")(display arg)))))
(QandA ((+ 1 2))) ; (+ 1 2) : 3
(QandA (quote (+ 1 2))) ; (quote (+ 1 2)) : (+ 1 2)
The supported syntax of generating macro differs by interpreters, so you should check the document of interpreter which you are using.
However, define-syntax and syntax-rules should be able to use in interpreter which supports R5RS or R6RS.
(define (subtract-1 n)
(string-append "Number is: " (number->string n))
(cond
[(= n 0) "All done!"]
[else (subtract-1(- n 1))]))
I keep getting the error: define: expected only one expression for the function body, but found 1 extra part. I'm not understanding why I'm getting this.
NOTE TO SELF: When using DrRacket, Setting the language to BSL may make Racket commands error at compile time.
The language you're using (BSL) only allows a single expression inside the body of a procedure, if there's more than one expression, you need to pack them inside a begin.
Also notice that the string-append line is doing nothing, you should print it or accumulate it. Here's a possible solution with my recommendations in place:
(define (subtract-1 n)
(begin
(display (string-append "Number is: " (number->string n) "\n"))
(cond
[(= n 0) "All done!"]
[else (subtract-1 (- n 1))])))
Even better, use the printf procedure for simplicity's sake:
(define (subtract-1 n)
(begin
(printf "~a ~s~n" "Number is:" n)
(cond
[(= n 0) "All done!"]
[else (subtract-1 (- n 1))])))
Either way a sample execution looks like this:
(subtract-1 3)
=> Number is: 3
=> Number is: 2
=> Number is: 1
=> Number is: 0
=> "All done!"
Racket documentation (Sequencing) seems to suggest that you might need to use a begin expression for this to work, or it might be the missing space in (subtract-1(- n 1)) between the function name and the parameter.
Also, you probably want to output the result of
string-append as it's not really doing anything as it is. Example to cover off all these points:
(define (subtract-1 n)
(begin
(write (string-append "Number is: " (number->string n)))
(cond
[(= n 0) "All done!"]
[else (subtract-1 (- n 1))])))