I am making a compiler for a language in LISP, and the overall aim is for the compiler to produce LISP code from the original language. Trying to measure the performance of the generated code, I found that it is severely lacking in printing strings.
In the original language, characters are byte -arithmetic- values, thus strings are arrays of bytes, and the value of the byte corresponds to the character whose value is the ascii-code of the byte. A "printable" byte array must be null-terminated. Thus, to print a byte array as a character string, I have to map the original array's elements into characters before printing it. The function that handles this is as follows:
(defun writeString (X &AUX (NPOS 0) (i 0))
(declare (type (simple-VECTOR fixnum *) x))
(declare (type fixnum NPOS i))
(SETF NPOS (POSITION 0 X))
(IF (NOT NPOS)
(SETF NPOS (LENGTH X)))
(princ (MAKE-ARRAY NPOS
:INITIAL-CONTENTS (map 'vector
#'code-char
(SUBSEQ X 0 NPOS))
:ELEMENT-TYPE 'base-char)))
and it is injected into the generated code.
Running a sample code with time, I found out that the princ part results in a lot of consing during execution, which slows things down. When in the place of make-array... a static string is put, there is no slowdown and no consing, so I guess that's the part the damage is done.
While compiling, I have set flags full on speed, the byte values are declared as fixnum for now in the generated code.
Can anyone point me to a better way to print my byte array as character string while avoiding excessive consing?
I could store bytes as characters from the get-go, but that would result in the parts of the language that treat them as numbers being slower due to the need to convert.
Problems in your code
Your code:
(defun writeString (X &AUX (NPOS 0) (i 0))
(declare (type (simple-VECTOR fixnum *) x))
(declare (type fixnum NPOS i))
(SETF NPOS (POSITION 0 X))
(IF (NOT NPOS)
(SETF NPOS (LENGTH X)))
(princ (MAKE-ARRAY NPOS
:INITIAL-CONTENTS (map 'vector
#'code-char
(SUBSEQ X 0 NPOS))
:ELEMENT-TYPE 'base-char)))
There are a couple of mistakes in the code:
i is not used
the first type declaration is syntactical not valid
the declaration for NPOS is wrong. You define it as FIXNUM, but it can be NIL.
There are a bunch of programming mistakes:
there is no need to allocate any array if all you want is to output characters.
even if you want to generate an array, one can do it once
X is not a good name for a string
A simple solution:
(defun writestring (bytestring)
(loop for byte across bytestring
while (plusp byte)
do (write-char (code-char byte))))
A type declared version could be:
(defun writestring (bytestring)
(declare (vector bytestring))
(loop for byte of-type (integer 0 255) across bytestring
while (plusp byte)
do (write-char (code-char byte))))
Instead (integer 0 255) one can also use (unsigned-byte 8).
About generating vectors:
Let's also look how you try to create the array:
You create an array with make-array, using the contents from another array.
Why not tell MAP to generate the correct array?
CL-USER 46 > (map '(vector base-char) #'code-char #(102 111 111 98 97 114))
"foobar"
Now if you want to allocate arrays for some reason:
do it once
map the content into the generated array. Use map-into for that. It will stop with the shorter sequence.
Example:
CL-USER 48 > (let ((bytestring #(102 111 111 98 97 114 0 100 100 100)))
(map-into (make-array (or (position 0 bytestring)
(length bytestring))
:element-type 'base-char)
#'code-char
bytestring))
"foobar"
You could rely on write-sequence, which hopefully is optimized to write a sequence of characters or bytes. It also accepts an :end argument which is useful for delimiting the end of the written string.
I doubt you really need to use literal vectors (which are always simple-vector), but if so you maybe want to change them. You can do it at read-time:
(let ((input #.(coerce #(102 111 111 98 97 114 0 100 100 100)
'(vector (mod 256)))))
(write-sequence (map '(vector base-char)
#'code-char
input)
*standard-output*
:end (position 0 input)))
I never used something like the following, but you could also open the same file in both character and byte mode, and switch whenever necessary:
(with-open-file (out-c #P"/tmp/test"
:if-exists :supersede
:direction :output)
(with-open-file (out-8 #P"/tmp/test"
:element-type '(unsigned-byte 8)
:direction :output
:if-exists :append)
(format out-c "Hello [")
(file-position out-8 (file-position out-c))
(write-sequence #(102 111 111 98 97 114) out-8)
(file-position out-c (file-position out-8))
(format out-c "]")))
It prints "Hello [foobar]" in /tmp/test, and it seems to work with multibyte characters, but you probably need to test that more.
Related
In these day i'm working to a json parse in prolog and lisp.
yesterday with your help i finished the prolog project and now i need help again.
the funcion is always json-get but now in lisp.
this is the functin that i wrote:
(defun json-get (json_obj fields &optional n)
(let ((place (assoc fields json_obj :test 'string=)))
(if (null place)
n
(ns (second place) t)))
the behavior of the funtion should be the same of the prolog predicate.
for example if the input is:
CL-prompt> (defparameter x (json-parse "{\"nome\" : \"Arthur\",\"cognome\" : \"Dent\"}"))
X
CL-prompt> x
(json-obj ("nome" "Arthur") ("cognome" "Dent"))
the output should be:
CL-prompt> (json-get x "cognome")
"Dent"
insted, if the input is:
(json-get (json-parse
"{\"name\" : \"Zaphod\",
\"heads\" : [[\"Head1\"], [\"Head2\"]]}")
"heads" 1 0)
the output should be:
"Head2"
the function that i wrote is totally wrong?
P.S. for this project are forbidden functions like SET, SETQ, SETF e MULTIPLE-VALUE-SETQ and DO, DO*, DOTIMES, DOLIST e LOOP and DEFPARAMETER, DEFVAR e DEFCOSTANT inside a function
thanks guys
edit 1:
this is the description of this funcion,
a json-get function that accepts a JSON object
(represented in Common Lisp, as produced by the json_parse function) and a series of
"Fields", retrieve the corresponding object. A field represented by N (with N a number
greater than or equal to 0) represents an index of a JSON array.
edit 2 :
if i try to run json-get lisp answer me with:
Error: The variable PLACE is unbound.
You need to implement this recursively. You also need to distinguish JSON arrays (which are implemented as a list of elements prefixed with json-array) and JSON objects (which are implemented as an association list.
(defun json-get (json_obj fields)
(if (null fields) ; base case of recursion
json_obj
(let* ((cur-key (car fields))
(current (cond ((and (integerp cur-key)
(eq (car json_obj) 'json-array))
(nth (1+ cur-key) json_obj)) ; add 1 to skip over JSON-ARRAY
((and (stringp cur-key)
(eq (car json_obj) 'json-obj))
(second (assoc cur-key (cdr json_obj) :test #'string=))) ; Use CDR to skip over JSON-OBJ
(t (error "~S is not a JSON object or array or ~s is not appropriate key" json_obj cur-key)))))
(json-get current (cdr fields)))))
fields has to be a list of fields, so your second example would be:
(json-get (json-parse
"{\"name\" : \"Zaphod\",
\"heads\" : [[\"Head1\"], [\"Head2\"]]}")
'("heads" 1 0))
and the first example should be:
(json-get x '("cognome"))
I am trying to make my own length/2 function(which allows you to find the length of a list) in lisp and am having an issue.
If I were to program this in java I would create a global index variable
that = 0
Then in the method I would do
if(list.equal(null))
{
return index;
}
else
{
index++;
return functioname(tail of list) ;
}.
Obviously this is not actual java syntax but I am just trying to relay the logic I want to apply in lisp.
My main issue is that ifs in lisp only allow you to do
if test expression
then do something
else
do something else
while I am trying to do
if test expression
then do something
else
do 2x something
Is there a way I can accomplish this in lisp or is there a better way to go about this problem?
Do it recursively:
len(list):
if list is null
return 0
else
remove first_item from list
return 1 + len(list)
(define (length items)
(if (null? items)
0
(+ 1
(length (cdr items)))))
(length (list 1 2 3))
3
Or use set!:
(define count 0)
(define (length items)
(when (not (null? items))
(set! count (+ count 1))
(length (cdr items))))
(length (list 1 2 3 4 5))
count
5
Scheme has the special operator begin (the Common Lisp equivalent is progn) that lets you group expressions which are executed in sequence, and returns the last one.
(if (null? items)
0
(begin (set! index (+ 1 index))
(my-length (cdr items))
index))
If the if expression returns nil under the false condition, then you can also use when (as in Rahn's answer) which is a bit more compact.
That said, having a global variable, or any variable at all, whose value gets changed (which is the purpose of a variable) is not the Lisp/recursive way of doing things. (Global variables are not a good thing in any language.) Rahn's first example is the only way any experienced Lisp programmer would do it.
In my example above, the (set! index (+ 1 index)) and index)) lines, and the global variable index are completely unnecessary if you add the 1 to the (my-length (cdr items)). If the list is empty, the length is zero; otherwise, the length is 1 + the length of the tail of the list.
I want to convert the number 8.7 to binary.
I know the commands
(format nil "~b" (rationalize 8.7)) ===>
1010111/1010
or
(format nil "~b" (/ 87 10))====> 1010111/1010
We observe if we do the quotient binary 1010111/1010 we obtain 1000.1011001100110011001100110011.
Is possible to obtain in Lisp
(8.7)_2 ~ 1000.1011001100110011001100110011?
If yes, how?
"2.718..." is equal to 2 * 10^1 + 7 * 10^-1 + 1 * 10^-2 + 8 * 10^-3... This means that you can generate the string by the reverse process of concatenating string(n / base^i) where i is the index into the string and n is the value that still needs to be converted to base. It's essentially a greedy change-making algorithm.
The following roughly works, with no guarantee that it produces the exact IEEE 754 fraction. It should be as accurate as your implementation's floats
(defun fractional-binary (fractional &optional (place 1) (result "."))
(if (zerop fractional)
result
(let ((fraction (expt 2 (- place))))
(if (<= fraction fractional)
(fractional-binary (- fractional fraction)
(1+ place)
(concatenate 'string result "1"))
(fractional-binary fractional
(1+ place)
(concatenate 'string result "0"))))))
CL-USER> (fractional-binary .7)
".101100110011001100110011"
I assumed that values passed into a lisp function are assigned to a quote matching the name of the parameter. However, I was surprised that this:
(defun test (x) (print (eval 'x)))
(test 5)
doesn't work (the variable x is unbound). So if parameters aren't stored as symbols in the function, what exactly IS x in this example? Is there a way to access parameters from a symbol matching the parameter name?
More context:
What I would like to do is something like this:
defun slice (r1 c1 r2 c2 board)
(dolist (param '(r1 c1 r2 c2)) ;adjust for negative indices
(if (< (eval param) 0)
(set param (+ (length board) (eval param)))))
;Body of function
Basically, I want to iterate through the first four parameters and make an adjustment to any of their values if they are < 0. Of course, I could do a let and have an individual line for each parameter, but considering I'm doing the same thing for each of the four parameters this seemed cleaner.
However, I get the error that the variable R1 is unbound.
That's basically how lexical binding works: the variable name gets replaced within the lexical scope with a direct reference to where the variable's value is stored. Binding the variable name's symbol-value is only done for dynamic variable which you can declare with special.
One way to avoid repeating yourself would be a macro:
(defmacro with-adjusting ((&rest vars) adjust-value &body body)
`(let ,(loop for var in vars
collect `(,var (if (minusp ,var)
(+ ,var ,adjust-value)
,var)))
,#body))
(defun slice (r1 c1 r2 c2 board)
(with-adjusting (r1 c1 r2 c2) (length board)
;; function body
Is there a way to access parameters from a symbol matching the parameter name?
Not for lexical binding. Common Lisp gives no way to access a lexical variable from a similar named symbol. You would need to declare the variable special.
So if parameters aren't stored as symbols in the function, what exactly IS x in this example?
A processor register? A part of a stack frame?
With dynamic binding:
CL-USER 40 > (defun foo (a b)
(declare (special a b))
(dolist (v '(a b))
(if (zerop (symbol-value v))
(set v 10)))
(values a b))
FOO
CL-USER 41 > (foo 1 0)
1
10
As Rainer explained, you cannot access the lexical argument value by its name.
What you can do instead is use the &rest argument together with destructuring-bind if you want the variables too:
(defun slice (board &rest params)
(destructuring-bind (r1 c1 r2 c2)
(mapcar (lambda (param) ;adjust for negative indices
(if (minusp param)
(+ (length board) param)
param))
params)
... operate on r1 c1 r2 c2 ...))
I'm trying to achieve parallell MySQL access with Scheme engines (light threads, as I've come to understand it).
My problem is, I don't know how to interact with C properly, thus can't take care of the results from the MySQL C API. This could also be a question about integrating C and Scheme.
My choice of Ypsilon is because it's fast (faster than Spark at least) and R6RS. Choosing another Scheme implementation is only relevant if it's R6RS and has speed comparable to Ypsilon.
This file bind the C API:
http://code.google.com/p/ypsilon/source/browse/trunk/sitelib/ypsilon/mysql.scm?spec=svn444&r=444
Top make it compile, I had to change all int8_t to int, unsigned-int to int, etc. There's a lot of C types I miss in my Ypsilon library, that is (it's the latest complete distribution, still).
My book about Scheme, The Scheme Programming Language by Dybvig (4:th edition) sais nothing about C integration.
The function mysql_fetch_row returns a MYSQL_ROW struct. This page present a way to handle this:
(google for) fixedpoint mysql-client-in-ypsilon.html
I cannot compile this. Have to check why.
This is used:
make-bytevector-mapping = Provides transparent access to an arbitrary memory block
In its FFI, Ypsilon has a function called define-c-struct-type:
http://www.littlewing.co.jp/ypsilon/doc-draft/libref.ypsilon.c-types.html#define-c-struct-methods
I haven't tried that yet. I'll be back with more information. If anyone has succeeded with something like this, please let me know.
Regards
Olle
My code so far:
(import (core)
(ypsilon ffi)
(ypsilon c-types)
(ypsilon mysql))
(define NULL 0)
(define host "localhost")
(define user "root")
(define passwd "bla")
(define database "bla")
(define mysql)
(define query "select bla from bla")
(begin
(set! mysql (mysql_init #f))
(mysql_real_connect mysql host user passwd database 0 #f 0)
(display (mysql_stat mysql))
(newline)
(display (string-append "mysql_query:\t" (number->string (mysql_query mysql query))))
(newline)
(let ([result (mysql_store_result mysql)])
(let rec ([n (mysql_num_fields result)] [row (mysql_fetch_row result)] [lengths (mysql_fetch_lengths result)])
(display (string-append "num_fields:\t" (number->string n)))
(newline)
(display (string-append "lengths:\t" (number->string lengths)))
(newline)
(display (string-append "row:\t\t" (number->string row)))
(newline)
(if (= row (- 0 1))
""
(let ([row (make-bytevector-mapping row (bytevector-c-int-ref (make-bytevector-mapping lengths sizeof:int)) (* 10 sizeof:int))])
(display (utf8->string row))
(newline)
(newline)
(newline)
(rec n (- lengths 1) (mysql_fetch_row result))
)))
(newline)))
This will output:
Uptime: 1203 Threads: 1 Questions: 204 Slow queries: 0 Opens: 569 Flush tables: 1 Open tables: 64 Queries per second avg: 0.169
mysql_query: 0
num_fields: 1
lengths: 8525640
row: 8541880
error in bytevector-c-int-ref: expected 2, but 1 argument given
irritants:
(#<bytevector-mapping 0x821748 4>)
backtrace:
0 (bytevector-c-int-ref (make-bytevector-mapping lengths sizeof:int))
..."/home/olle/scheme/div.scm" line 54
1 (rec n (- lengths 1) (mysql_fetch_row result))
..."/home/olle/scheme/div.scm" line 63
2 (let rec ((n (mysql_num_fields result)) (row (mysql_fetch_row result)) (lengths ...) ...) ...)
..."/home/olle/scheme/div.scm" line 37
I looked at the documentation for the for bytevector-c-int-ref the first thing that jumped out to me was that it requires two arguments (like the error says) the second being a non-negative byte offset. Does it run with that change?