How to 'display' multiple parameters in R5RS Scheme - scheme

In R5RS Scheme how do you display multiple parameters, with a single call? my implementation below works, but adds extra parentheses and spaces.
#!/usr/bin/env racket
#lang r5rs
(define (display-all . rest) (display rest))
(display-all "I " "have " "a " "lovely " "bunch " "of " "coconuts\n")
results in
owner#K53TA:~$ ./Template.ss
(I have a lovely bunch of coconuts
)

Simplest:
(define (display-all . vs)
(for-each display vs))
Note the use of for-each instead of map - for-each is the same thing but assumes you're only calling the function for side-effects, so instead of returning a list of results (using map with display would return a list of voids) it just returns void.
This can get annoying if you want to display non-string things and have spacing between them, for instance if you want (display-all 12 "bananas") to display the string "12 bananas" you have to manually turn the number into a string and add the space yourself. It would be easier to just add spaces in-between elements of the list:
(define (insert-between v xs)
(cond ((null? xs) xs)
((null? (cdr xs)) xs)
(else (cons (car xs)
(cons v (insert-between v (cdr xs)))))))
(define (display-all . vs)
(for-each display (insert-between " " vs)))
Now calling this:
(display-all "blah" 4 "bloo")
does what you'd expect. If you don't want the spaces inserted automatically, you can specify another argument as the separator object and use it however you need. Here's a version that accepts a separator object:
(define (display-all sep . vs)
(for-each display (insert-between sep vs)))
This approach would make more sense in a version of scheme that supports optional and keyword arguments however, so you could default it to either a space or the empty string and not interfere with the rest-args.

If all the arguments are string, just use apply and string-append. both are in r5rs
#!/usr/bin/env racket
#lang r5rs
(define (display-all . rest) (display (apply string-append rest)))
(display-all "I " "have " "a " "lovely " "bunch " "of " "coconuts\n")

Related

Common Lisp: custom predicate for conditional formatting

I have a list consisting of two different kind of elements:
String designators
Cons cells, whose car and cdr are both string designators
I would like this list to be printed with the following structure: all elements separated by commas, and each cons cell (a . b) is printed as a=b.
The real goal is to generate Tikz/LaTeX code using Common Lisp, and this specific part of the code is just there to generate "options" (to an environment, a package, a command ...)
Example:
>>> (format nil "<cool-format-string>" '(draw (color . red) dashed (fill . gray)))
[draw, color=red, dashed, fill=gray]
I know about iteration directives, conditional ones, etc, in format. The only problem is that I want to print my list elements differently according to their type, not according to their numerical value or their 'truth' (nil vs anything else)
Besides writing a custom directive with ~/, is there a way to use another construction which would do something akin to the conditional directive, but using a custom predicate ?
(defun dotted-pair-p (x)
(and (listp x) (not (listp (cdr x)))))
(defun print-fancy (lst)
(let ((res (mapcar (lambda (x) (if (dotted-pair-p x)
(format nil "~a=~a" (car x) (cdr x))
(format nil "~a" x)))
lst)))
;; add the ', ' inbetween the strings
;; and put the result in between "[]"
(format nil "[~{~a~^, ~}]" res)))
(print-fancy '(draw (color . red) dashed (fill . gray)))
;; "[DRAW, COLOR=RED, DASHED, FILL=GRAY]"

Standard way to handle quoted symbol in lisp macros in Scheme

For some code I was working I've needed to handle 'x inside macro. What is standard way of handling those values?
I have code like this:
(define (quoted-symbol? x)
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
(define-macro (test x)
(if (quoted-symbol? x)
`(begin
(display ',(cadr x))
(newline))))
(test 'hello) ;; 'hello will be expanded into list (quote hello)
Is this how this should be handled, or is just in macro you don't use quoted symbols?
NOTE: I'm not asking about hygienic macros (I'm asking about real lisp macros), so please no answers with hygienic macros.
EDIT:
My macro works correctly in Guile and BiwaScheme and in my own scheme like lisp in JavaScript. Here is better example:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(define (example arg)
(list arg (test 'world)))
(example 'hello)
the question was not about display, but about (cadr x).
EDIT2: You've asked so here you go, my macro:
(define-macro (--> expr . code)
"Helper macro that simplify calling methods on objects. It work with chaining
usage: (--> ($ \"body\")
(css \"color\" \"red\")
(on \"click\" (lambda () (print \"click\"))))
(--> document (querySelectorAll \"div\"))
(--> (fetch \"https://jcubic.pl\") (text) (match /<title>([^<]+)<\/title>/) 1)
(--> document (querySelectorAll \".cmd-prompt\") 0 \"innerText\")"
(let ((obj (gensym)))
`(let* ((,obj ,(if (and (symbol? expr) (not (null? (match /\./ (symbol->string expr)))))
`(.. ,expr)
`,expr)))
,#(map (lambda (code)
(let ((name (gensym))
(value (gensym)))
`(let* ((,name ,(cond ((quoted-symbol? code) (symbol->string (cadr code)))
((pair? code) (symbol->string (car code)))
(true code)))
(,value (. ,obj ,name)))
,(if (and (pair? code) (not (quoted-symbol? code)))
`(set! ,obj (,value ,#(cdr code)))
`(set! ,obj ,value)))))
code)
,obj)))
;; ---------------------------------------------------------------------------------------
(define (quoted-symbol? x)
"(quoted-symbol? code)
Helper function that test if value is quoted symbol. To be used in macros
that pass literal code that is transformed by parser.
usage:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(list 'hello (test 'world))"
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
the macro is used in my scheme like lisp in JavaScript, like the doc string suggest:
(--> document (querySelectorAll ".class") 0 "innerText")
I want to support:
(--> document (querySelectorAll ".class") 0 'innerText)
The code can be tested online at: https://jcubic.github.io/lips/ (You need to copy/paste the code since current version allow only method calls).
To get expansion you can use
(pprint (macroexpand (--> document (querySelector "x"))))
if it don't work (don't expand) it mean that macro is broken somehow.
dot is build in function that get property of an object and .. macro:
(define-macro (.. expr)
"(.. foo.bar.baz)
Macro that gets value from nested object where argument is comma separated symbol"
(if (not (symbol? expr))
expr
(let ((parts (split "." (symbol->string expr))))
(if (single parts)
expr
`(. ,(string->symbol (car parts)) ,#(cdr parts))))))
that can be use to get nested property like (.. document.body.innerHTML)
Scheme doesn't have "real lisp macros". Some implementations has something similar, but the forms have different names and uses. They are not portable at all.
The standard way of handling 'x is to handle it like an expression that gets evaluated in the expansion. Eg.
(define var 'x)
(test 'x)
(test var)
The two test forms should amount to the same even though the macro test gets (quote x) in the first and the symbol var in the second. At the time of the expansion var does not exist since the implementation can expand all the macros before starting.
You implementation of test will not work. Eg. the display might be run one or twice and then each time you call a procedure that uses it it will gfail since the expansion is the undefined value and it might not be fit for evaluation. eg.
(define (example arg)
(list arg (test 'w)))
When this is defined you get 'w or (quote w) printed with a newline and then the procedure it tries to store is:
(define (example arg)
(list arg #<undefined>))
Note that what constitutes the undefined value is chosen by the implementaion, but I know for sure that in many implementaions you cannot evaluate #<undefined>.

How do you display letters one by one in Scheme?

I am new to the Scheme language and am currently using the Simply Scheme textbook. I was experimenting with some procedures, and I noticed that when I do these functions (below), it prints words in a "spoken" fashion:
(define (display-all sep . vs)
(for-each display (insert-between sep vs)))
(define (insert-between v xs)
(cond ((null? xs) xs)
((null? (cdr xs)) xs)
(else (cons (car xs)
(cons v (insert-between v (cdr xs)))))))
(Code used from How to 'display' multiple parameters in R5RS Scheme)
Then commanding:
(display-all "" 'w 'o 'w " " 't 'h 'i 's " " 'i 's " " 'c 'o 'o 'l)
The letters are printed one by one as if someone was typing them. I was wondering if there was any way to make it easier for me to input these words to be spoken, instead of inputting letter by letter. I was planning to have something like this:
(define (speak . wds)
(...))
where wds would be a string. In the above example, I would like for it to be like this: (speak "wow this is cool") and return "wow this is cool" but each letter displayed one by one.
Thank you in advance for your help!
How about passing a string as input? there's no need to use variable arguments in this case, please try this:
(define (display-all sep vs)
(for-each display (insert-between sep (string->list vs))))
(display-all "" "wow this is cool")

Combining words list to a para in Racket

I have to combine a list of words to produce a para. I managed following:
(define (wordlist2para wl)
(define str " ")
(for ((w wl))
(set! str (string-append str w " ")))
(string-trim str))
(wordlist2para '("this" "is" "a" "test"))
Output:
"this is a test"
It works but it is not functional. How can I write functional code for this?
If I wanted to do it explicitly and not use string-join, I would recurse and use three cases:
The empty list produces the empty string
A one-element list produces its sole element (this avoids having a trailing separator)
Otherwise, append the car and a space to the recursion on the cdr.
Like this:
(define (wordlist2para ws)
(cond ((null? ws) "")
((null? (cdr ws)) (car ws))
(else (string-append (car ws) " " (wordlist2para (cdr ws))))))
No need of recursion or loop, there is the primitive function string-join for this (see the manual):
(define (wordlist2para wl)
(string-join wl " "))
(wordlist2para '("this" "is" "a" "test"))
;; -> "this is a test"
We have standard procedures that does this:
;; racket library or srfi/13
(string-join '("this" "is" "it")) ; ==> "this is it"
There is a way to always rewrite these that are quite simple. I'd like to step away from rackets great feature set and just focus on simple scheme with recursive procedures. Notice that in your loop you are changing 2 things wl gets smaller, str gets longer, so lets make that:
; all things that change as arguments
(define (wordlist2para-loop wl str)
(if (null? wl)
str
(wordlist2para-loop (cdr wl)
(string-append str (car wl) " "))))
Now for we just replace the loop:
(define (wordlist2para wl)
(wordlist2para-loop wl ""))
From here on you can move the helper to become local or perhaps make it a named let or any other refactoring, but it doesn't really change the resulting compiled result in an implementation much, just how it looks.
Notice I haven't fixed the bug where there is only one word. (wordlist2para '("this")) ; ==> "this " The result is actually exactly the same as in your, only that it's tail recursive and functional.
I am not sure if following can be called functional but it does use some higher order functions:
(define (wordlist2para wl)
(string-trim
(apply string-append
(map (lambda(x) (string-append x " ")) wl))))
(wordlist2para '("this" "is" "a" "test"))
Output:
"this is a test"

LISP clause for and clause let ¿why?,making a programming language in racket using ragg

I have long been trying to find the error, I'm doing a programming language and have the next code, using ragg, I have a syntax-object(resto ...) what has a bracket as data, I transform this syntax-object to a datum:
(let ([i (syntax->datum #'(resto ...))])
(display "Content i:")
(display i)
(if (eq? i (string->symbol "(})"))
(display "true")
(display "false")
)
)
and the output is:
Content: (})
false
But if I do this
(for ([i (syntax->datum #'(resto ...))])
(displayln "Content:")
(displayln i)
(if (eq? i (string->symbol "}"))
(display "true")
(display "false")
)
)
and the output is:
Content: }
true
MY QUESTION:
¿WHY THE IF OF CLAUSE LET IS FALSE?
¿AS I CAN COMPARE THESE TWO TYPES AND THAT THE RESULT IS TRUE WITHOUT THE FOR?
Documentation about functions:
syntax->datum
Each piece of code is doing a very different thing, I'll show you how to make each one work. The first one uses let to assign into a variable the whole list returned by syntax->datum, and afterwards you compare it against another list (better use equal? for testing equality, it's more general):
(let ([i (syntax->datum #'(|}|))]) ; `i` contains `(})`
(display "Content i: ")
(displayln i)
(if (equal? i '(|}|)) ; a list with a single element, the symbol `}`
(displayln "true")
(displayln "false")))
The second piece of code is using for to iterate over each element in a list, until it finds the symbol }:
(for ([i (syntax->datum #'(|}|))]) ; `i` contains `}`
(display "Content i: ")
(displayln i)
(if (equal? i '|}|) ; the symbol `}`
(displayln "true")
(displayln "false")))
As a side note, you have to be very careful with the way you're going to process all those curly brackets {} in your language, they're interpreted as normal parentheses () in Racket and they'll be tricky to handle, notice how I had to escape them by surrounding them with vertical bars.

Resources