Processing command-line-args-left? - elisp

I have a function in which I pass a filename followed by several integer parameters. The problem is that I now want to run my code as a Unix script, using command-line-args-left to pass parameters from the command line. When #1 calls process-args, a list is created with all of the values. In #2, a list of a list {eg. ((1 2 3)) } is created upon entry to process-args. What is the best way to keep my code generic so I can handle both cases #1 and #2 in the same function?
(defun process-args (filename &rest cols) ...)
(process-args filename 1 2 3); #1
(process-args (car command-line-args-left) (cdr command-line-args-left)); #2
Here is some working sample code with which I'm testing:
#!/usr/bin/emacs --script
(defun process-args (filename &rest cols)
(princ (concat "Script Name: " file "\n"))
(princ (concat "File parameter: " filename "\n"))
(princ "Other arg values: ")
(princ cols)
(princ "\nIs list: ")
(princ (listp cols))
(princ "\n----------\n")
(while cols
(princ (car cols))
(princ "...")
(setq cols (cdr cols)))
(princ "\n"))
(print "===== Version #1: Base case - becomes (1 2 3) =====")
(process-args (car command-line-args-left) 1 2 3)
(print "===== Version #2: Passing cdr of list as one string =====")
(process-args (car command-line-args-left) (mapconcat 'identity (cdr command-line-args-left) " "));
(print "===== Version #3: Test of list of list - becomes ((1 2 3)) =====")
(process-args (car command-line-args-left) '(1 2 3))

You could try using 'apply to flatten out the last argument (the last argument to 'apply' is a list of arguments, use'funcall` if you don't have the last bit in a list).
So, version #3 above would be handled so:
(apply 'process-args (car command-line-args-left) '(1 2 3))
(The other invocations wouldn't change.)

Related

How to use format's iteration ability on a list of conses

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.

Scheme concatenate strings to display

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"))

I need a function to display an 'unprocessed' expression

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.

using sed to convert file contents to scheme list

I would like to convert data.txt into scheme list using sed into the following format:
-Each every line with same starting number will be parsed and combined like so:
data.txt
1,{},344.233
1,{2},344.197
2,{16},290.281
2,{18},289.093
3,{1},220.896
foo.scm
(define v1 '(1 (() 344.233) ((2) 344.197))) ;; this is for first two lines starting with 1
(define v2 '(2 ((16) 290.281) ((18) 289.093))) ;; ... 2
(define v3 '(3 (() 237.558))) ;; ... 3
I know nothing about scheme, so I'd probably do this in awk rather than sed.
[ghoti#pc ~]$ cat data.txt
1,{},344.233
1,{2},344.197
2,{16},290.281
2,{18},289.093
3,{1},220.896
[ghoti#pc ~]$ cat doit.awk
#!/usr/bin/awk -f
BEGIN {
FS=",";
last1=1;
}
$1 != last1 {
printf("(define v%.0f '(%.0f %s))\n", last1, last1, substr(sect,2));
last1=$1; sect="";
}
{
gsub(/[^0-9]/,"",$2);
sect=sprintf("%s ((%s) %s)", sect, $2, $3);
}
END {
printf("(define v%.0f '(%.0f %s))\n", last1, last1, substr(sect,2));
}
[ghoti#pc ~]$ ./doit.awk data.txt
(define v1 '(1 (() 344.233) ((2) 344.197)))
(define v2 '(2 ((16) 290.281) ((18) 289.093)))
(define v3 '(3 ((1) 220.896)))
[ghoti#pc ~]$
It could certainly be written more tightly, but this gets the job done.
UPDATE: (per comments)
[ghoti#pc ~]$ tail -1 data.txt
3,{1,3,4},220.896
[ghoti#pc ~]$ diff -u doit.awk doitnew.awk
--- doit.awk 2012-05-30 00:38:34.549680376 -0400
+++ doitnew.awk 2012-05-30 00:38:52.893810815 -0400
## -10,8 +10,15 ##
last1=$1; sect="";
}
+$2 !~ /}$/ {
+ while ($2 !~ /}$/) {
+ pos=match($0, /,[0-9,]+}/);
+ $0=substr($0, 0, pos-1) " " substr($0, pos+1);
+ }
+}
+
{
- gsub(/[^0-9]/,"",$2);
+ gsub(/[^0-9 ]/,"",$2);
sect=sprintf("%s ((%s) %s)", sect, $2, $3);
}
[ghoti#pc ~]$ ./doitnew.awk data.txt
(define v1 '(1 (() 344.233) ((2) 344.197)))
(define v2 '(2 ((16) 290.281) ((18) 289.093)))
(define v3 '(3 ((1 3 4) 220.896)))
[ghoti#pc ~]$
What's going on here?
In the new block we're adding, test to see whether the second field ends in a }. If it doesn't, we'll loop until it does. For each run of the loop, we'll remove a comma before the }, replacing it with a space.
Sometimes, brute-force works. :-P
In racket (a.k.a. scheme):
#lang racket
;; parse a line (we will join them later)
(define (line-parse l)
(match (regexp-match #px"([0-9]+),\\{([0-9,]*)\\},([0-9.]+)" l)
[(list dc first-num bracket-nums rest)
(list (string->number first-num)
(match bracket-nums
["" empty]
[else (map string->number
(regexp-split #px"," bracket-nums))])
(string->number rest))]
[else
(error "unexpected line format in line: ~s\n" l)]))
;; join together lines that start with the same number
(define (join-lines lines)
(cond [(empty? lines) empty]
[else (join-lines-of-n (first (first lines))
lines
empty)]))
;; gather together lines starting with 'n':
(define (join-lines-of-n n lines accum)
(cond [(empty? lines)
(list (cons n (reverse accum)))]
[(equal? (first (first lines)) n)
(join-lines-of-n n (rest lines) (cons (rest (first lines))
accum))]
[else
(cons (cons n (reverse accum))
(join-lines lines))]))
(define (dress-up line)
(format "~a\n" `(define ,(format "v~s" (first line))
',line)))
(display
(apply
string-append
(map dress-up
(join-lines
(map line-parse
(sequence->list (in-port read-line)))))))
Save this as rewrite.rkt, run it like this:
oiseau:/tmp clements> racket ./rewrite.rkt < foo.txt
(define v1 (quote (1 (() 344.233) ((2) 344.197))))
(define v2 (quote (2 ((16) 290.281) ((18) 289.093))))
(define v3 (quote (3 ((1) 220.896) ((4 5) 2387.278))))
... note that I added a {4,5} line to the input example to test your extension.
also, note that the output uses (quote ...) rather than '(...). This "should work fine"; that is, Scheme readers produce the same output for these two forms, and the resulting file should work fine as scheme input.
If this were my code, I think I wouldn't do the (define v1 ...) dance, and just write the thing out as a big piece of data that a scheme/racket program can slurp in with a single "read", but that's your choice, not mine. Also, there's some ambiguity in your specification re: the uniqueness of the initial indexes; that is, you might "go back" to an earlier line number. For instance, what should be the output when given this input file:
3,{1},1.0
4,{1},1.0
3,{1},1.0
?
Also, note that I chopped out all of the test cases in order to make it look shorter/prettier :).
EDIT: OH! Gather the lines this way, instead. It'll actually be a bit slower, but it reads much more nicely:
#lang racket
;; parse a line (we will join them later)
(define (line-parse l)
(match (regexp-match #px"([0-9]+),\\{([0-9,]*)\\},([0-9.]+)" l)
[(list dc first-num bracket-nums rest)
(list (string->number first-num)
(match bracket-nums
["" empty]
[else (map string->number
(regexp-split #px"," bracket-nums))])
(string->number rest))]
[else
(error "unexpected line format in line: ~s\n" l)]))
;; does the line start with the number k?
(define ((starts-with k) l) (equal? (first l) k))
;; join together lines starting with the same thing:
(define (join-lines lines)
(for/list ([k (remove-duplicates (map first lines))])
(cons k (map rest (filter (starts-with k) lines)))))
(define (dress-up line)
(format "~a\n" `(define ,(format "v~s" (first line))
',line)))
(display
(apply
string-append
(map dress-up
(join-lines
(map line-parse
(sequence->list (in-port read-line)))))))
This might work for you (GNU sed):
sed ':a;$!N;s/^\(\([^,])*\).*\)\n\2/\1/;ta;h;x;s/\n.*//;s/,{\([^}]*\)},\([^,]\+\)/ ((\1) \2)/g;s/,/ /g;s/^\([^ ]*\).*/(define v\1 '\''(&)) ;;...\1/p;x;D' file
Explanation:
Reduce like values to a single line :a;$!N;s/^\(\([^,])*\).*\)\n\2/\1/;ta
Copy pattern space (PS) to hold space (HS). h
Swap to HS x
Chop off previous line. s/\n.*//
Formulate lists. s/,{\([^}]*\)},\([^,]\+\)/ ((\1) \2)/g
Replace any remaining ,'s with spaces. s/,/ /g
Surround lists with function definition and comments and print. s/^\([^ ]*\).*/(define v\1 '\''(&)) ;;...\1/p
Swap back to PS. x
Delete upto previous line and repeat. D

Delete only first appearance of element into list?

How to delete only first appearance of element into list (elisp) ?
The Common Lisp sequence-editing functions (remove and friends) take a :count keyword argument:
Count, if supplied, limits the number of elements removed or deleted; if more than count elements satisfy the test, then of these elements only the leftmost or rightmost, depending on from-end, are deleted or removed, as many as specified by count. If count is supplied and negative, the behavior is as if zero had been supplied instead. If count is nil, all matching items are affected.
For example:
ELISP> (require 'cl)
cl
ELISP> (remove* 1 '(1 2 1 3 1 4) :count 1)
(2 1 3 1 4)
ELISP> (remove* 1 '(1 2 1 3 1 4) :count 2)
(2 3 1 4)
ELISP> (remove* 1 '(1 2 1 3 1 4) :count 2 :from-end t)
(1 2 3 4)
(Note that Emacs already had its own function called remove, so the cl package has to use the name remove*.)
The noob's code for elisp. position can be found into cl-seq.el.
(defun remove-first (elem lst)
(interactive)
(if (equal (position elem lst) nil ) (progn (setq lst lst) )
(progn
(setq out1 (nthcdr (+ 1 (position elem lst)) lst))
(setq out2 (nbutlast lst (- (length lst) (position elem lst) ) ) )
(delq nil (append out2 out1))
))
)
To remove 3 from mylist, will be called as
>(setq mylist '(1 2 3 4 3 3))
>(setq mylist (remove-first 3 mylist))
(1 2 4 3 3)
You can use this elisp (which requires cl):
(defun remove-first (elt seq)
(let ((remove-first t))
(remove-if (lambda (e) (when (and remove-first (equal elt e))
(setq remove-first nil)
t))
seq)))
Note: this makes a copy of the original list. For one using side-effects, try this:
(defun remove-first* (elt seq)
(if (equal elt (car seq))
(cdr seq)
(while (cdr seq)
(if (equal elt (cadr seq))
(progn (setcdr seq (cddr seq))
(setq seq nil))
(setq seq (cdr seq))))
seq))
Note: when the first element is the one removed, just the cdr is returned, so as always with this type of operation, invoke it like so:
(setq mylist (remove-first* 3 mylist))

Resources