I'm currently implementing a small parser in Clojure that takes an input string like:
aaa (bbb(ccc)ddd(eee)) fff (ggg) hhh
and returns the string without characters that are not in brackets, i.e.
(bbb(ccc)ddd(eee))(ggg)
I've written the following function:
(defn- parse-str [input]
(let [bracket (atom 0)
output (atom [])]
(doseq [ch (seq input)]
(case ch
\( (swap! bracket inc)
\) (swap! bracket dec)
nil)
(if (or (> #bracket 0) (= ch \)))
(swap! output conj ch)))
(apply str #output)))
which works for me:
(parse-str "aaa (bbb(ccc)ddd(eee)) fff (ggg) hhh")
"(bbb(ccc)ddd(eee))(ggg)"
I am however concerned that my approach is a too object oriented since it uses atoms as some kind of local variables to keep the current state of the parser.
Is it possible to write the same function from a more functional programming perspective? (avoiding the atoms?)
Any comments to improve my code are appreciated as well.
Two ways: You can use explicit recursion or reduce.
(defn parse-str [input]
(letfn [(parse [input bracket result]
(if (seq input)
(let [[ch & rest] input]
(case ch
\( (recur rest (inc bracket) (conj result ch))
\) (recur rest (dec bracket) (conj result ch))
(recur rest bracket (if (> bracket 0)
(conj result ch)
result))))
result))]
(clojure.string/join (parse input 0 []))))
(defn parse-str [input]
(clojure.string/join
(second (reduce (fn [acc ch]
(let [[bracket result] acc]
(case ch
\( [(inc bracket) (conj result ch)]
\) [(dec bracket) (conj result ch)]
[bracket (if (> bracket 0)
(conj result ch)
result)])))
[0 []]
input))))
In a lot of cases where you would use local variables, you just put any variable that changes as a parameter to loop, thereby using recursion instead of mutation.
(defn- parse-str [input]
;; Instead of using atoms to hold the state, use parameters in loop
(loop [output []
bracket 0
;; The [ch & tail] syntax is called destructuring,
;; it means let ch be the first element of (seq input),
;; and tail the rest of the elements
[ch & tail] (seq input)]
;; If there's no elements left, ch will be nil, which is logical false
(if ch
(let [bracket* (case ch
\( (inc bracket)
\) (dec bracket)
bracket)
output* (if (or (> bracket* 0) (= ch \)))
(conj output ch)
output)]
;; Recurse with the updated values
(recur output* bracket* tail))
;; If there's no characters left, apply str to the output
(apply str output))))
This is an iterative version of your function; but it's still functionally pure. I find having the code laid out like this makes it easy to read. Remember, when using recursion, always check your termination condition first.
(defn parse-str [s]
(loop [[x & xs] (seq s), acc [], depth 0]
(cond
(not x) (clojure.string/join acc)
(= x \() (recur xs (conj acc x) (inc depth))
(= x \)) (recur xs (conj acc x) (dec depth))
(<= depth 0) (recur xs acc depth)
:else (recur xs (conj acc x) depth))))
Related
I am trying to make a script work from both DrRacket and the repl, having this as my starting point: Racket calculator
Here is my current code:
#lang racket
(provide (all-defined-out))
(require parser-tools/lex
(prefix-in re: parser-tools/lex-sre)
parser-tools/yacc)
(define-tokens value-tokens (INT ANY))
(define-empty-tokens empty-tokens
(PLUS MINUS MULTIPLY DIVIDE NEWLINE EOF))
(define basic-lexer
(lexer
((re:+ numeric) (token-INT lexeme))
(#\+ (token-PLUS))
(#\- (token-MINUS))
(#\* (token-MULTIPLY))
(#\/ (token-DIVIDE))
((re:or #\tab #\space) (basic-lexer input-port))
(#\newline (token-NEWLINE))
((eof) (token-EOF))
(any-char (token-ANY lexeme))))
(define (display-plus expr)
(display "Result: ")
(let ((left (string->number (first expr)))
(right (string->number (last expr))))
(display (+ left right)))
(newline))
(define (display-minus expr)
(display "Result: ")
(let ((left (string->number (first expr)))
(right (string->number (last expr))))
(display (- left right)))
(newline))
(define (display-multiply expr)
(display "Result: ")
(let ((left (string->number (first expr)))
(right (string->number (last expr))))
(display (* left right)))
(newline))
(define (display-divide expr)
(display "Result: ")
(let ((left (string->number (first expr)))
(right (string->number (last expr))))
(display (/ left right)))
(newline))
(define basic-parser
(parser
(start start)
(end NEWLINE EOF)
(tokens value-tokens empty-tokens)
(error (lambda (ok? name value)
(printf "Couldn't parse: ~a\n" name)))
(grammar
(start ((expr) $1)
((expr start) $2))
(expr ((INT PLUS INT) (display-plus (list $1 $3)))
((INT MINUS INT) (display-minus (list $1 $3)))
((INT MULTIPLY INT) (display-multiply (list $1 $3)))
((INT DIVIDE INT) (display-divide (list $1 $3)))
((ANY) (displayln $1))))))
(define input1 (open-input-string "123 + 456"))
(define input2 (open-input-string "123 *456"))
(basic-parser (lambda() (basic-lexer input1)))
(basic-parser (lambda() (basic-lexer input2)))
;(define (my-repl)
; (display ">>> ")
; (let* ((input (read-line))
; (input-port (open-input-string
; (list->string
; (drop-right
; (string->list input) 1)))))
; (cond
; ((not (equal? "\r" input)
; (print (basic-parser
; (lambda () (basic-lexer input-port))))))))
; (my-repl))
(define (calc str)
(let* ([port (open-input-string str)]
[result (basic-parser (lambda() (basic-lexer port)))])
(displayln result)))
(define (repl)
(display ">>> ")
(let ((input (read-line)))
(print input)
(cond
((eof-object? input) (displayln "eof"))
((eq? input #\newline) (displayln "new line"))
(else (calc (read-line))))
(newline))
(repl))
A test from DrRacket is shown here:
Welcome to DrRacket, version 7.1 [3m].
Language: racket, with debugging; memory limit: 512 MB.
Result: 579
Result: 56088
> (repl)
>>> 1+1
"1+1"2+2
Result: 4
#<void>
>>> 3+3
"3+3"4+4
Result: 8
#<void>
And from the repl:
Welcome to Racket v7.1.
> (require "untitled7.rkt")
Result: 579
Result: 56088
> (repl)
>>> "\r"
#<void>
>>> 1+1
"1+1\r"2+2
Result: 4
#<void>
>>> 3+3
"3+3\r"4+4
Result: 8
#<void>
>>> #<eof>eof
>>> ; user break [,bt for context]
It only displays every second calculation. It appears that read-line returns a new line before waiting for user input, which I tried to check with (eof-object? input) and (eq? input #\newline) but now I get only every second result.
There are two problems:
First, you're reading a line, (let ((input (read-line))), but you're not sending that input to the calculator, you'r sending another one – (calc (read-line)).
You should pass input to calc for evaluation instead.
Second, you have a lot of #<void>s in your output.
This is because calc assumes that your parser produces a value that it can print:
(displayln result)
but the parser does not produce any value, it only prints one.
Either remove the output of result, or rewrite the parser to return the value to its caller.
Replace (calc (read-line)) with (calc input).
I wrote a procedure that gets a valid prefix list for subtraction (e.g, "(- 6 5)" for what we know as "6-5"). Here is my code:
(define parse-diff-list
(lambda (datum)
(cond
((number? datum) (const-exp datum)) ;; if datum is a number, return const-exp
((pair? datum) ;; if datum is a pair:
(let ((sym (car datum))) ;; let sym be the first of the pair
(cond
((eqv? sym '-) ;; if sym is minus:
(let ((lst1 (parse-diff-list (cdr datum)))) ;; parse second element of subtraction
(let ((lst2 (parse-diff-list (cdr lst1)))) ;; parse first element of subtraction
(cons (diff-exp (car lst1) (car lst2)) (cdr lst2))))) ;; "perform" the subtraction
((number? sym) ;; if sym is number:
(cons (const-exp sym) (cdr datum))) ;; return const-exp with the remainder of the list, yet to be processed
(else (eopl:error 'parse-diff-list "bad prefix-expression, expected - ~s" sym)))))
(eopl:error 'parse-diff-list "bad prefix-expression ~s" datum))))
(define parse-prefix
(lambda (lst)
(car (parse-diff-list lst))))
It works fine logically, but I don't understand the logic of the indentation in printing. For the input:
(parse-prefix '(- - 1 2 - 3 - 4 5))
It prints:
#(struct:diff-exp
#(struct:diff-exp #(struct:const-exp 1) #(struct:const-exp 2))
#(struct:diff-exp #(struct:const-exp 3) #(struct:diff-exp #(struct:const-exp 4) #(struct:const-exp 5)))
While I would want the following print style:
#(struct:diff-exp
#(struct:diff-exp
#(struct:const-exp 1)
#(struct:const-exp 2))
#(struct:diff-exp
#(struct:const-exp 3)
#(struct:diff-exp
#(struct:const-exp 4)
#(struct:const-exp 5)))
It's more than a petty question for me, as it does create indentations but I don't know how it does it.
Thanks a lot!
Take a look at racket/pretty the pretty printing library.
In particular note the parameter (pretty-print-columns) which
you can set like this:
`(pretty-print-columns 40)`
in order to avoid long lines.
http://docs.racket-lang.org/reference/pretty-print.html
(I am guessing you are using DrRacket based on the way the structures are printing)
I can print n-numbers as list with this code below:
(define (print-first-n stream1 n)
(cond((= n 0) '())
(else(cons(stream-car stream1) (print-first-n (stream-cdr stream1) (- n 1))))))
But I have no idea about how to add commas.
You can't print a comma in a normal list, but we can build a string with the contents of the stream, separated by commas. This will work, assuming that the string contains numbers:
(define (print-first-n stream1 n)
(cond ((= n 1)
(number->string (stream-car stream1)))
(else
(string-append
(number->string (stream-car stream1)) ", "
(print-first-n (stream-cdr stream1) (- n 1))))))
The above solution is fine for a small value of n, but terribly inefficient for large values (lots of temporary strings will be created, with O(n^2) complexity for the append operation). For a more efficient implementation, consider using SRFI-13's concatenation procedures, like this:
(require srfi/13)
(define (print-first-n stream1 n)
(let loop ((strm stream1) (n n) (acc '()))
(if (= n 1)
(string-concatenate-reverse
(cons (number->string (stream-car strm)) acc))
(loop (stream-cdr strm)
(sub1 n)
(list* ", " (number->string (stream-car strm)) acc)))))
Either way: let's say that integers is an infinite stream of integers starting at 1, this is how it would look:
(print-first-n integers 5)
=> "1, 2, 3, 4, 5"
If the stream contains some other data type, use the appropriate procedure to convert each element to a string.
If your function just prints the stream contents, and doesn't need to build a string (like Óscar's answer), here's my take on it (uses SRFI 41 streams):
(define (print-first-n stream n)
(stream-for-each (lambda (delim item)
(display delim)
(display item))
(stream-cons "" (stream-constant ", "))
(stream-take n stream)))
Example:
> (define natural (stream-cons 1 (stream-map (lambda (x) (+ x 1)) natural)))
> (print-first-n natural 10)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
To output to a string (like Óscar's answer), just wrap the whole thing in a string port:
(define (print-first-n stream n)
(call-with-output-string
(lambda (out)
(stream-for-each (lambda (delim item)
(display delim out)
(display item out))
(stream-cons "" (stream-constant ", "))
(stream-take n stream)))))
I am making a Common Lisp function to print the first N prime numbers. So far I've managed to write this code:
;globals
(setf isprime 1) ;if 1 then its a prime, 0 if not.
(setf from 1) ;start from 1
(setf count 0) ;should act as counter to check if we have already
; N primes printed
;function so far.
(defun prime-numbers (to)
(if (> count to) nil(progn
(is-prime from from)
(if (= isprime 1) (print from)(setf count (+ count 1)))
(setf isprime 1)
(setf from (+ from 1))
(prime-numbers to)))
(if (>= count to)(setf count 0) (setf from 1)))
;code to check if a number is prime
(defun is-prime(num val)
(if (< num 3) nil
(progn
(if (= (mod val (- num 1)) 0) (setf isprime 0))
(is-prime (- num 1) val))))
My problem is, it does not print N primes correctly.
If I call >(prime-numbers 10),
results are:
1
2
3
5
7
11
13
17
19
1,
i.e. it printed only 9 primes correctly.
but then if i call >(prime-numbers 2)
the results are: 1
2
3
5
7
1
what am I doing wrong here?? this is my first time to code in LISP.
UPDATE:
(defparameter from 1)
(defparameter count 0)
(defun prime-numbers (to)
(if (> count to)nil
(progn
(when (is-prime from)
(print from)
(setf count (+ count 1)))
(setf from (+ from 1))
(prime-numbers to)))
(when (>= count to)
(setf count 0)
(setf from 1)))
(defun is-prime (n)
(cond ((= 2 n) t)
((= 3 n) t)
((evenp n) nil)
(t
(loop for i from 3 to (isqrt n) by 2
never (zerop (mod n i))))))
works fine. but outputs a NIL at the end.
First, there's no need to use globals here, at all.
Use true/false return values. That would allow your is-prime function to be something like:
(defun is-prime (n)
(cond ((= 2 n) t) ;; Hard-code "2 is a prime"
((= 3 n) t) ;; Hard-code "3 is a prime"
((evenp n) nil) ;; If we're looking at an even now, it's not a prime
(t ;; If it is divisible by an odd number below its square root, it's not prime
(loop for i from 3 to (isqrt n) by 2
never (zerop (mod n i))))))
That way, the function is not relying on any external state and there's nothing that can confuse anything.
Second, the last 1 you see is (probably) the return value from the function.
To check that, try:
(progn (prime-numbers 10) nil)
Third, re-write your prime-numbers function to not use global variables.
Fourth, never create global variables with setf or setq, use either defvar or defparameter. It's also (mostly, but some disagree) good style to use *earmuffs* on your global (really, "special") variables.
To expand on Vatines answer:
A possible rewrite of the prime-numbers function, using the same algoritm but avoiding globals is
(defun prime-numbers (num &optional (from 2))
(cond ((<= num 0) nil)
((is-prime from) (cons from (prime-numbers (1- num) (1+ from))))
(t (prime-numbers num (1+ from)))))
This function also returns the primes instead of printing them.
The problem with this recursive solution is it consumes stack for each prime found/tested. Thus stack space may be exhausted for large values of num.
A non-recursive variant is
(defun prime-numbers (num &optional (start 2))
(loop for n upfrom start
when (is-prime n)
sum 1 into count
and collect n
until (>= count num)))
I'm having some trouble figuring out how to separate a string which is tab delimited into chunks of data as an example if i have a text file which I'm reading from that looks like this
a1 b1 c1 d1 e1
a2 b2 c2 d2 e2
and i read the first line of my file and get a string which of
"a1 b1 c1 d1 e2"
I want to separate this into 5 variables a,b,c,d and e, or create a list (a b c d e). Any thoughts?
Thanks.
Try concatenating parentheses onto the front and back of your input string, then using read-from-string (I assume you're using Common Lisp, since you tagged your question clisp).
(setf str "a1 b1 c1 d1 e2")
(print (read-from-string (concatenate 'string "(" str ")")))
Yet another way to go about it (a tad more robust, perhaps), You can also easily modify it so that you could `setf' a character in the string once the callback is called, but I didn't do it that way because it seemed like you don't need this sort of ability. Also, in that later case, I'd rather use a macro.
(defun mapc-words (function vector
&aux (whites '(#\Space #\Tab #\Newline #\Rubout)))
"Iterates over string `vector' and calls the `function'
with the non-white characters collected so far.
The white characters are, by default: #\Space, #\Tab
#\Newline and #\Rubout.
`mapc-words' will short-circuit when `function' returns false."
(do ((i 0 (1+ i))
(start 0)
(len 0))
((= i (1+ (length vector))))
(if (or (= i (length vector)) (find (aref vector i) whites))
(if (> len 0)
(if (not (funcall function (subseq vector start i)))
(return-from map-words)
(setf len 0 start (1+ i)))
(incf start))
(incf len))) vector)
(mapc-words
#'(lambda (word)
(not
(format t "word collected: ~s~&" word)))
"a1 b1 c1 d1 e1
a2 b2 c2 d2 e2")
;; word collected: "a1"
;; word collected: "b1"
;; word collected: "c1"
;; word collected: "d1"
;; word collected: "e1"
;; word collected: "a2"
;; word collected: "b2"
;; word collected: "c2"
;; word collected: "d2"
;; word collected: "e2"
Here's an example macro you could use, if you wanted to modify the string as you read it, but I'm not entirely happy with it, so maybe someone will come up with a better variant.
(defmacro with-words-in-string
((word start end
&aux (whites '(#\Space #\Tab #\Newline #\Rubout)))
s
&body body)
`(do ((,end 0 (1+ ,end))
(,start 0)
(,word)
(len 0))
((= ,end (1+ (length ,s))))
(if (or (= ,end (length ,s)) (find (aref ,s ,end) ',whites))
(if (> len 0)
(progn
(setf ,word (subseq ,s ,start ,end))
,#body
(setf len 0 ,start (1+ ,end)))
(incf ,start))
(incf len))))
(with-words-in-string (word start end)
"a1 b1 c1 d1 e1
a2 b2 c2 d2 e2"
(format t "word: ~s, start: ~s, end: ~s~&" word start end))
assuming that they are tabbed (not spaced) then this will create a list
(defun tokenize-tabbed-line (line)
(loop
for start = 0 then (+ space 1)
for space = (position #\Tab line :start start)
for token = (subseq line start space)
collect token until (not space)))
which results in the following:
CL-USER> (tokenize-tabbed-line "a1 b1 c1 d1 e1")
("a1" "b1" "c1" "d1" "e1")