(defun foo (mylist)
(with-open-file (str "out.txt"
:direction :output
:if-exists :append
:if-does-not-exist :create)
(if mylist
(progn
(format str (car mylist))
(foo (cdr mylist))))))
I have two questions. First one is that I can not write an element of a list by this expression (format str (car mylist)), Secondly it produces another error as below.
already points to file
"out.txt", opening the file again for :OUTPUT may produce
unexpected results
Open the file anyway
Error
This error is documented in the manual.
You are calling foo recursively and each call opens the file again.
This can have very bad consequences.
Re-open
You can fix it by moving the recursive call outside of with-open-file:
(defun write-list-to-file-reopen (mylist destination)
(when mylist
(with-open-file (str destination
:direction :output
:if-exists :append
:if-does-not-exist :create)
(prin1 (pop mylist) str)
(terpri str))
(write-list-to-file-reopen mylist)))
but this is quite inefficient because open is a relatively expensive operation.
A much better solution would be to iterate
(defun write-list-to-file-iterate (mylist destination)
(with-open-file (str destination
:direction :output
:if-exists :append
:if-does-not-exist :create)
(dolist (el mylist)
(format str "~S~%" el))))
or, if you are require to use recursion,
(defun write-list-to-file-recursion (mylist destination)
(with-open-file (str destination
:direction :output
:if-exists :append
:if-does-not-exist :create)
(labels ((write-list (list)
(when list
(prin1 (pop list) str)
(terpri str)
(write-list list))))
(write-list mylist))))
Format
Function format is relatively
heavy weight and intended for interactive pretty printing, you might
prefer to use simpler functions like write.
Your problem is because your (format str x) should be (format str "~S~%" x):
you forgot the format string.
Related
I need return True or False
True if at least one lowercase character
False no lowercase characters
I tried do it with loop and lambda function
Something like this
(defun check-lower-word (word)
(loop
for ch across word
((lambda (c) (if (lower-case-p c) return T) ch)
)
)
I need False if never worked "if"
With a predefined function, you could use some (manual):
CL-USER> (some #'lower-case-p "AbC")
T
CL-USER> (some #'lower-case-p "ABC")
NIL
There is a similar operation for the loop syntax (manual):
CL-USER> (loop for x across "AbC" thereis (lower-case-p x))
T
CL-USER> (loop for x across "ABC" thereis (lower-case-p x))
NIL
Finally, note that loop always returns nil when the iteration terminates without producing a result, so a less concise use of loop could be:
CL-USER> (loop for x across "AbC" if (lower-case-p x) do (return t))
T
CL-USER> (loop for x across "ABC" if (lower-case-p x) do (return t))
NIL
Code errors
You code is not balanced with respect to parentheses, there is a missing closing parenthesis at the end:
(defun check-lower-word (word)
(loop
for ch across word
((lambda (c) (if (lower-case-p c) return T) ch)
)
) ; <-- closes "(loop"
The syntax error in your loop should have raised an error, it does not make sense to write an expression EXPR directly in (loop for c across w EXPR), there should be a preceding do.
The literal ((lambda (c) E) ch) can be directly written as E where every occurence of the variable c is substituted by ch, namely:
(if (lower-case-p ch) return T)
The use of an intermediate literal lambda brings nothing here.
Also, the above reads as: if ch is lowercase, the value of the if is the value bound to the return variable, otherwise it is T. You are indeed missing parens around (return T). A "one-armed" (if T X) is best written as (when T X).
Another approach
You already have an example with some and loop, you can also use a short-circuiting map:
(defun check-lower-word (word)
(block nil
(map ()
(lambda (c)
(when (lower-case-p c)
(return t)))
word)))
A call to MAP with nil as a first argument means the sequence is iterated for effects and returns nil. For each character in the sequence (list or vector), when the character is lower-case, return T. The return exits the iteration early up to the NIL block.
I did it as
(defun check-lower-word (word)
(block outer
(loop
for ch across word do
(if (lower-case-p ch) (return-from outer T))
)
)
)
How to check if an element is present in a list, both taken as input from the function call, without using the lambda? I was trying member? but could not get it.
(define (find-string (lst lst str ua)
(cond ((member? ua lst) #t)
(else #f))
The use of member would work it's just that you are adding extra "?" in front of the function none is required
(member 2 (list 1 2 3 4)) [1]
would return true
another way around is writing ones own recursive function
(define (is-in-list list value)
(cond
[(empty? list) false]
[(= (first list) value) true]
[else (is-in-list (rest list) value)]))
[1]https://docs.racket-lang.org/reference/pairs.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._member%29%29
First, the way with lambda and ormap (for testing):
; ismember? :: String List-of-Strings -> Bool
(define (ismember1? str strs) (ormap [lambda (s) (string=? s str)] strs) )
Second way, with for/or, without lambda:
(define (ismember2? str strs)
(for/or ([s (in-list strs)])
(string=? s str) ) )
Third way, with member, without lambda:
(define (ismember3? str strs) (if [member str strs] #t #f) )
Refer to the official Racket documentation for member.
Notice that the last version is actually the worst in terms of performance.
I have to write a program that changes a string's vowels, consonants and other symbols into C, V respectively 0. I've done this but I wonder if there is a more efficient and elegant way to do it. Would appreciate input.
(defun string-to-list (string)
(loop for char across string collect char))
(defun is-vowel (char) (find char "aeiou" :test #'char-equal))
(defun is-consonant (char) (find char "bcdfghjklmnpqrstvwxyz" :test #'char-equal))
(defun letter-type (char)
(if (is-vowel char) "V"
(if (is-consonant char) "C"
"0")))
(defun analyze-word (word-string)
(loop for char across word-string collect (letter-type char)))
Moreover, I would like to make it a string, how could I do that? Should I define a function that would iterate through the list and make it a string or is it an easier way to do it?
(defun letter-type (char)
(cond ((find char "aeiou" :test #'char-equal) #\V)
((alpha-char-p char) #\C)
(t #\0)))
CL-USER> (map 'string #'letter-type "analyze-word")
"VCVCCCV0CVCC"
Just for the sake of the idea:
(defun multi-replace-if (sequence function &rest more-functions)
(map (type-of sequence)
(lambda (x)
(loop for f in (cons function more-functions)
for result = (funcall f x)
while (eql x result)
finally (return result)))
sequence))
(multi-replace-if "bcdfghjklmnpqrstvwxyz"
(lambda (x) (if (find x "aeiouy") #\v x))
(lambda (y) (declare (ignore y)) #\c))
"cccccccccccccccccccvc"
Is there a way to convert symbol to string only if it is not already a string in lisp?
It should work like this:
(only-if-convertion 'ABC) => "ABC"
(only-if-convertion "ABC") => "ABC"
Use the function STRING.
CL-USER > (string "FOO")
"FOO"
CL-USER > (string 'FOO)
"FOO"
You can use the format function to do the conversion. Granted it's slower than the other options listed, but it can work on other data types, controls upcase/downcase, etc. So for development, or non-inner-loop portions of the code, this could be useful for you:
CL-USER>
(format nil "~a" "str")
"str"
CL-USER>
(format nil "~a" 'str)
"STR"
CL-USER>
(format nil "~(~a~)" 'str)
"str"
CL-USER>
(format nil "~(~a~)" "str")
"str"
CL-USER>
~
CL-USER> (defun symbol-or-string-to-string (x)
(typecase x
(symbol (symbol-name x))
(string x)
(otherwise (error "Wrong type"))))
SYMBOL-OR-STRING-TO-STRING
CL-USER> (symbol-or-string-to-string "foo")
"foo"
CL-USER> (symbol-or-string-to-string 'foo)
"FOO"
CL-USER> (symbol-or-string-to-string #())
; Evaluation aborted.
CL-USER>
But the idea of converting it repetitively sounds odd. Can you show why are you needing to do it?
I'm just trying to convert to a string and compare to the reverse
(defn is-palindrome? [num]
(= (str num) (reverse (str num))))
Something like
(is-palindrome 1221)
Is returning false
Try this instead:
(defn is-palindrome? [num]
(= (str num) (apply str (reverse (str num)))))
In your code, the expression (reverse (str 1221)) returns the list of characters (\1 \2 \2 \1), which needs to be turned back into a string for the comparison to work. Alternatively, you could convert both numbers to character lists and perform a list comparison instead:
(defn is-palindrome? [num]
(= (seq (str num)) (reverse (str num))))
(defn palindrome? [num]
(= (seq (str num)) (clojure.string/reverse (str num))))
Your code returns false because it is comparing a string with a sequence, which can never be equal.
You can make it work by explicitly converting the string into a seq as follows:
(defn is-palindrome? [num]
(let [digit-sequence (seq (str num))]
(= digit-sequence (reverse digit-sequence))))
It turns out the the overhead of manipulating collections of characters dominates, so it's actually faster to compare the original string to a reversed version even though it seems like you're comparing twice as many characters as necessary. Make sure you use clojure.string/reverse, not clojure.core/reverse. The usual Clojure convention is to end a predicate with a question mark, but not to use the "is" prefix.
(require 'clojure.string)
(defn palindrome? [s] (= s (clojure.string/reverse s)))
(defn palindrome-num? [n] (palindrome? (str n)))
(reverse (str 1221))
returns a List of characters
(\1 \2 \2 \1)
but (str 1221) is a java String