as title, I have a list of lists
'((1 2) (3 4) (5 6))
And I follow the documentation to come up with this
(define (write-to-a-file l path)
(call-with-output-file path
(lambda (output-port)
(write l output-port))
#:exists 'replace
#:mode 'binary))
It helps write the list to csv file. However, the csv content is not separated, it seems like the entire list was dumped into the file (i.e, ((1 2) (3 4) (5 6)), not 1 2 (two cells) 3 4 (two cells) etc)
Is there any approach that can write a list of lists into csv in Scheme , like Python ?
The code in your question is for writing text to a file, nothing in it is specific for writing a CSV file. You'd need to parse the list contents, writing each sublist in a different line, separating the elements with commas.
But we don't need to reinvent the wheel, in Racket just install the csv-writing package - in case you can't find it, the raco command is in the bin folder where you installed Racket:
raco pkg install csv-writing
Now we can use it:
(require csv-writing)
(define (write-to-a-file lst path)
(call-with-output-file path
(lambda (output-port)
(display-table lst output-port))
#:exists 'replace))
Call your procedure like this:
(define lst '((1 2) (3 4) (5 6)))
(write-to-a-file lst "/path/to/file.csv")
The contents of file.csv will be:
1,2
3,4
5,6
Related
(defun ppl (list)
(loop for x in list
collect (cons x '(ppl))))
(ppl '(1 2 3))
=> ((1 ppl) (2 ppl) (3 ppl))
While still inside ppl, how do I remove the parenthesis so that the result becomes
=> (1 ppl 2 ppl 3 ppl)
I understand that my code fundamentally creates a list of sublists. Could use some help on flattening out the list. Perhaps if I could in some way get the list that collect returns?
Use nconc instead of collect:
(defun ppl (list)
(loop for x in list
nconc (list x 'ppl)))
(ppl '(1 2 3))
==> (1 PPL 2 PPL 3 PPL)
Please note that I replaced (cons x '(ppl)) with (list x 'ppl)) to avoid possible reuse of literal (ppl) which nconc could turn into circular lists. If you insist on having quoted literals in your code (bad idea!), use append instead of nconc.
I want to insert an element next to the last element of the list, but I only know how to insert an element next to the first element of the list, can someone help me further?
Example : (insert '5 '(1 3 2 7 8 9)) should output => (1 3 2 7 8 5 9)
(defun insert (item list)
(cons (first list)
(cons item
(rest list))))
//when I do (insert '5 '(1 3 2 7 8 9)) I get (1 5 3 2 7 8 9)
Lists are singly linked lists so only add to the front is possible without having to copy parts of the list. your attempt makes a copy of the first pair, the add your pair as the second element then share the rest of the original list with the argument.
In order to add as the last element you need to do this until list is empty and then return a list with the one element. None of the cons will be in common. So:
(insert 'x '()) ; ==> (list 'x)
(insert 'x '(1)) ; ==> (cons '1 (insert 'x (cdr '(1))))
Of course this can be done with append. Laziness is rewarded in programming:
(defun insert-last (item list)
(append list (list item)))
Know that if you have a recursive function or an iteration where you add like this for many elements depending on the arguments you are making a very bad algorithm. It is much better to either use loop to collect to the end or to make the list backwards and in the last step reverse it. Then you'll get n or 2n passes through the data instead of n^2.
I am trying to write an elisp function to read each word in a file into a pair. I want the first item of the pair to be the string sorted lexicographically, and the second item to be untouched.
Given the example file:
cat
cow
dog
I want the list to look like:
(act cat)
(cow cow)
(dgo dog)
My best crack at it is:
(defun get-file (filename)
(with-open-file (stream filename)
(loop for word = (read-line stream nil)
while word
collect ((sort word #'char-lessp) word))))
It compiles correctly in Emacs lisp interaction mode. However, when I try to
run it by executing
(get-file "~/test.txt")
I end up in the Emacs debugger, and it's not telling me anything useful . . .
Debugger entered--Lisp error: (void-function get-file)
(get-file "~/test.txt")
eval((get-file "~/test.txt") nil)
eval-last-sexp-1(t)
eval-last-sexp(t)
eval-print-last-sexp(nil)
call-interactively(eval-print-last-sexp nil nil)
command-execute(eval-print-last-sexp)
I am a lisp beginner, and have no idea what is wrong.
Thanks,
Justin
Vanilla Emacs
First, let's use Emacs's built-in functions only. There's no built-in function to sort strings in Emacs, so you first should convert a string to a list, sort, then convert the sorted list back to a string. This is how you convert a string to a list:
(append "cat" nil) ; => (99 97 116)
A string converted to a list becomes a list of characters, and characters are represented as numbers in Elisp. Then you sort the list and convert it to a string:
(concat (sort (append "cat" nil) '<)) ; => "act"
There's no built-in function to load file contents directly into a variable, but you can load them into a temporary buffer. Then you can return the entire temporary buffer as a string:
(with-temp-buffer
(insert-file-contents-literally "file.txt")
(buffer-substring-no-properties (point-min) (point-max))
This will return the string "cat\ncow\ndog\n", so you'll need to split it:
(split-string "cat\ncow\ndog\n") ; => ("cat" "cow" "dog")
Now you need to traverse this list and convert each item into a pair of sorted item and original item:
(mapcar (lambda (animal)
(list (concat (sort (append animal nil) '<)) animal))
'("cat" "cow" "dog"))
;; returns
;; (("act" "cat")
;; ("cow" "cow")
;; ("dgo" "dog"))
Full code:
(mapcar
(lambda (animal)
(list (concat (sort (append animal nil) '<)) animal))
(split-string
(with-temp-buffer
(insert-file-contents-literally "file.txt")
(buffer-substring-no-properties (point-min) (point-max)))))
Common Lisp Emulation
One of the Emacs built-in packages is cl.el, and there's no reason not to use it in your code. Therefore I lied, when I said there is no built-in functions to sort strings and the above is the only way to do the task using built-in functions. So let's use cl.el.
cl-sort a string (or any sequence):
(cl-sort "cat" '<) ; => "act"
cl-mapcar is more versatile than Emacs's built-in mapcar, but here you can use either of them.
There is a problem with cl-sort, it is destructive, meaning it modifies the argument in-place. We use local variable animal inside the anonymous function twice, and we don't want to garble the original animal. Therefore we should pass a copy of a sequence into it:
(lambda (animal)
(list (cl-sort (copy-sequence animal) '<) animal))
The resulting code becomes:
(cl-mapcar
(lambda (animal)
(list (cl-sort (copy-sequence animal) '<) animal))
(split-string
(with-temp-buffer
(insert-file-contents-literally "file.txt")
(buffer-substring-no-properties (point-min) (point-max)))))
seq.el
In Emacs 25 a new sequence manipulation library was added, seq.el. Alternative to mapcar is seq-map, alternative to CL's cl-sort is seq-sort. The full code becomes:
(seq-map
(lambda (animal)
(list (seq-sort animal '<) animal))
(split-string
(with-temp-buffer
(insert-file-contents-literally "file.txt")
(buffer-substring-no-properties (point-min) (point-max)))))
dash, s, f
Usually the best solution to work with sequences and files is to reach directly for these 3 third-party libraries:
dash for list manipulation
s for string manipulation
f for file manipulation.
Their Github pages explain how to install them (installation is very simple). However for this particular problem they are a bit suboptimal. For example, -sort from dash only sorts lists, so we would have to get back to our string->list->string conversion:
(concat (-sort '< (append "cat" nil))) ; => "act"
s-lines from s leaves empty strings in files. On GNU/Linux text files usually end with newline at the end, so splitting your file would look like:
(s-lines "cat\ncow\ndog\n") ; => ("cat" "cow" "dog" "")
s-split supports an optional argument to omit empty lines, but it's separator argument is a regex (note that you need both \n and \r for portability):
(s-split "[\n\r]" "cat\ncow\ndog\n" t) ; => ("cat" "cow" "dog")
Yet there are 2 functions which can simplify our code. -map is similar to mapcar:
(-map
(lambda (animal)
(list (cl-sort (copy-sequence animal) '<) animal))
'("cat" "cow" "dog"))
;; return
;; (("act" "cat")
;; ("cow" "cow")
;; ("dgo" "dog"))
However in dash there are anaphoric versions of functions that accept a function as an argument, such as -map. Anaphoric versions allow to use shorter syntax by exposing local variable as it and start with 2 dashes. E.g. the below are equivalent:
(-map (lambda (x) (+ x 1)) (1 2 3)) ; => (2 3 4)
(--map (+ it 1) (1 2 3)) ; => (2 3 4)
Another improvement is f-read-text from f, which simply returns contents of a file as a string:
(f-read-text "file.txt") ; => "cat\ncow\ndog\n"
Combine best of all worlds
(--map (list (cl-sort (copy-sequence it) '<) it)
(split-string (f-read-text "file.txt")))
On my emacs, either C-j or C-x C-e evaluates the form as you said. When I try to do the same with (get-file "test") the debugger complains about with-open-file being undefined. I cannot find with-open-file in cl-lib (or cl) emacs packages.
Did you require some other package? Also, I think the idiomatic way of opening file in Emacs is to temporary visit them in buffers.
Anyway, if the code was Common Lisp it would be ok except for collect ((sort ...) word), where you are not building a list but using (sort ...) in a function position. I'd use (list (sort ...) word) instead.
I need some help understanding the syntax on how to append a number into a list, i'm doing this from user input via the console so this elements have to be entered in recursively. So for ever number that is entered, the list must grow for each element (only numbers) added.
Here is the code I am working with, the problem lies in the second conditional. Right now this works but only creates an empty list of each number I put in, so the results would be
>12
>202
>30
()()()
zero input: stopping list
(define (inputlist)
(let ((applist list))
(let ((inpt (read)))
(cond
((= inpt 0)(newline) (display "zero input: stopping list"))
;;OLD((number? inpt) (cons inpt applist) (display (applist))(inputlist))
((number? inpt) (append (applist)(list inpt)) (display (applist))(inputlist))
(else
display "Not a number")))))
I understand why cons is not doing what I need it to be doing, but is there a similar functionality to appending each read in element to a pre-existing list?
EDIT: I've gotten closer what i've needed to do but still with the same results, i am now appending upon my applist with a list i create via every input, though it is still resulting in as many empty lists as I input.
SECOND EDIT: I've realized why it's printing multiple ()'s is because it's being called off the stack when 0 is entered, so i'm sure it's not working because the appending isn't working as intended, i've displayed the applist on the 0 conditional and it returns one null list.
A simple way to append an element to the end of a list while looping would be to do call append and update the reference to the list afterwards:
(set! applist (append applist (list inpt)))
Notice that you have several misplaced parentheses - in your code some are missing, some are unnecessary. In Scheme () means function application, and you have to be careful where you put those brackets.
Also, be aware that append doesn't modify the initial list, it creates a new one, and if you need to refer to it, you have to store it somewhere (that's why I'm doing a set! above).
There are more serious errors with you logic. The conditions are in the wrong order (you have to test if the input is a number before asking if it's zero), and you forgot to loop if something other than a number is entered. Also, if we pass along the list as a parameter to the loop, we won't have to do an ugly set!. Try this instead, it's closer to what you were aiming for:
(define (inputlist)
(let loop ((applist '()))
(let ((inpt (read)))
(cond ((not (number? inpt))
(display "not a number")
(newline)
(loop applist))
((zero? inpt)
(display "zero input: stopping list"))
(else
(let ((new-applist (append applist (list inpt))))
(display new-applist)
(newline)
(loop new-applist)))))))
As mentioned in the comments, bear in mind that appending at the end of a list inside a loop in general is a bad idea. It's ok for learning purposes, but in real-life code, you'd cons at the head of the list and reverse it at the end - this is more efficient.
Note that (cons x xs) where x is an element and xs is a list produces a new list which has x as its first element.
Here is one way to use cons to add an element in the end of a list:
Example:
Add 4 to (1 2 3)
1. Reverse the list: (3 2 1)
2. Add 4 to the front: (4 3 2 1)
3. Reverse: (1 2 3 4)
> (reverse (cons 4 (reverse (list 1 2 3)))
(1 2 3 4)
A function that uses this principle:
(define (cons-to-back x xs)
(reverse (cons x (reverse xs))))
> (cons-to-back 4 (list 1 2 3))
(1 2 3 4)
An alternative is to use append which appends the elements of two lists:
> (append '(1 2 3) '(4 5 6))
(1 2 3 4 5 6)
All we need to do, is to the the element into a list before using append:
> (append '(1 2 3) (list 4))
'(1 2 3 4)
Alternative definition of cons-to-back:
(define (cons-to-back x xs)
(append xs (list x)))
I have a question about Scheme. I am fairly new to the language, and my question is fairly general:
Is it possible, without defining a recursive function, to use Map (or something like it) to feed two lists of parameters into a function, producing a single new list from the outputs?
For instance, suppose I had:
(define lst1 (list 1 2 3 4 5))
(define lst2 (list 2 4 6 8 10))
And I wanted to then somehow map the + function, supplying each list as a parameter such that the output would be a new list, lst3:
>lst3
(3 6 9 12 15)
To state the question somewhat more succinctly: how might one most efficiently map a binary function when both parameters are lists?
Thanks for any and all help!
(map + lst1 lst2)
MAP can take any number of lists.
Example:
(map + lst1 lst2 lst1) => (4 8 12 16 20)