Asked on discord: “How do i print list of lists as a matrix, instead of these really long lines?”
pretty-print
I have a procedure that produces a list of lists — one per each call, like a generator. How can I collect all these values into a list? I thought a for/list would do, but I end up iterating each value in the first list produced. (I don’t want to look inside the item produced at the iteration. I want to treat it as a whole.)
racket@api.rkt> (define port (make-port-test))
racket@api.rkt> port
#<input-port:string>
racket@api.rkt> ((make-payout-reader port))
'("amount" "note" "email" "id")
Here’s my attempt:
(define (make-items port)
(for/list ([item ((make-payout-reader port))])
item))
Here’s the result:
racket@api.rkt> (make-items (make-port-test))
'("amount" "note" "email" "id")
I thought item
would be the entire list of values, but it ends up being “amount”, “note”, “email”, “id”, in this order.
So the port contains more lines?
@anything I’m confused about something here. If your procedure produces a list of values, it is a list already, so you don’t need to do anything.
@soegaard2 Yes, it contains more lines. I’m reading a CSV file with reading-csv
.
Sorry my description was terrible. I think I fixed it now.
I think I just want to cons each list into an accumulator-list.
What’s the idiomatic way to do this?
Here’s what I want.
(define (make-items port)
(define row ((make-payout-reader port)))
(cond [(null? row) '()]
[else (cons row (make-items port))]))
racket@api.rkt> (make-items (make-port-test))
'(("amount" "note" "email" "id")
("1.01" "Thank you!" "user-1" "")
("1.02" "Thanks!" "user-2" "cx123abc"))
What’s the idiomatic way to do this?
I would make a loop. If the “end object” is reached the empty list is returned. Otherwise the current values is consed onto the result of reading the rest. #lang racket
(require csv-reading)
(define next-row
(make-csv-reader
(open-input-string "1,2\n3,4\n")
'((separator-chars #\,)
(strip-leading-whitespace? . #t)
(strip-trailing-whitespace? . #t))))
(define (all-rows)
(match (next-row)
['() '()]
[row (cons row (all-rows))]))
I think csv-reading
uses ’() when the end is reached - so the end object is ’().
But … if an empty line also produces ’() then you only get the lines before the empty line.
So in this particular case, I would probably use csv->list
.
It does — ’(). I see you guys use match
a lot. Interesting code. Thanks! (Yes, my code would have the same problem…) (Good catch.)
That’s just habit. Your make-items
is just as fine.
New question. Say I have a procedure called make-payee
such that (make-payee v1 v2 v3 v4)
produces a payee-structure. I have a list '(v1 v2 v3 v4)
. How can I make the call (make-payee ,@my-list)
work? It seems I need a quasiquote to splice? I don’t know how to do that.
(apply make-payee the-list)
Aha! Beautiful. Thank you!
Well, not quite. For small matrices, they will still be on one line. For really large matrices, each cell will be on its own line.
I know @samth knows this already, but just put this here in case other people find this information useful.
So if you want the matrix format, just write your own. It’s a good programming exercise
I’m probably atypical, but I like the more Scheme like “named let” for looping: (define lists '((1 2 3) (4 5 6) (7 8 9)))
(let loop ([ lists lists ][ result '() ])
(if (null? lists)
(reverse result)
(loop (cdr lists) (cons (car lists) result))))