I am new to Racket Lang and previously I wrote thousands of lines of code in C++, Java and C. I am trying to figure out how to do the following task:
Given an array (like C uint8_t array) with the following format:
First byte is used to indicate the "format", let's say this could be 0x0a, 0x0b and so on.
Remaining data may include C strings without the null terminator and integers.
Write a function that parses the array and puts the values in some variables.
Before asking here, I was reading: https://docs.racket-lang.org/guide and also https://docs.racket-lang.org/reference
My approach is as follows:
I am using a byte string because it seems it can be used to mimic the C++/C arrays.
I am using tail recursion to traverse the "array".
Questions:
1) In C, I always use the return value of a function as an error code: 0 is ok and any negative value is an error. With Racket, I am using multiple return values to indicate: a) the return code, b) the processed value(s), i.e. something like:
(values return_code out1 out2 ...)
What do you think? Do you recommend the use of exceptions for error handling?
2) What's the best approach to process arrays in Racket? I mean, the best according to the offered by Racket and to achieve a good performance.
Thanks!
Edit1:
Regarding my first question (the return codes), I am calling many functions and I would like to return an exit code that helps me to know if there was an error inside the function. This is a sample code:
#lang racket
(define (is_valid in)
(cond
[(and (>= in 10) (<= in 15)) #t]
[else #f]))
(define (copy_values in)
(define len (bytes-ref in 2))
; 3 is where the string "ABCD" begins
(define str (subbytes in 3 (+ 3 len)))
(define numbers (subbytes in (+ 3 len)))
(values str numbers))
(define (parse in)
(define type (bytes-ref in 0))
(if (is_valid type)
(let ()
(define-values (str numbers) (copy_values in))
(values #t str numbers))
(values #f 0 0)))
; format: type strlen
; len |-- str --| | -- numbers -- |
;
(define input1 (bytes 10 10 4 65 66 67 68 110 120 130 140 150))
(define input2 (bytes 1 10 4 65 66 67 68 110 120 130 140 150))
(parse input1)
(parse input2)
This is the output in DrRacket:
Welcome to DrRacket, version 6.7 [3m].
Language: racket, with debugging; memory limit: 128 MB.
#t
#"ABCD"
#"nx\202\214\226"
#f
0
0
Look how I use the (values ...) stuff, does that make sense?
To return a value to the operating system, use (exit [v]).. Other values should probably go to standard out and/or standard error. This means writing them before the program exits.
In terms of assigning the values of an array to variables, there are many ways to do it depending on how lexical scope comes into play within the function and module and across modules. Also reference semantics versus value semantics are considerations.
Finally, there is no compelling reason to choose recursion over an explicit loop when dealing with a byte-string. bytes-length is a low level primitive implemented in C and will return the length needed for a loop without testing for an empty byte-string.
I have c-string style array if_name defined:
(define-cstruct _ifreq ([ifr_name (_array _byte IFNAMSIZE)]
;; ommited ...
))
I can access individual elements by (array->ref) and through recursion create list from it. Then use (list->bytes) to get lisp data structure. I am curious if there is a simpler way without need for list creation.
Racket comes with mutable byte strings for just such an occasion!
(require ffi/unsafe)
(define (byte-array->bytes array)
(let* ([len (array-length array)]
[byte* (make-bytes len)])
(for ([i (in-range len)])
(bytes-set! byte* i (array-ref array i)))
byte*))
Expressions like this will cause an error
(= nil 3)
Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
=(nil .......
Then is there an easy way(for example, another function called my-eq) to make this expression returns nil(means False) like this:
(my-eq nil 3)
=> nil
That's eq or equal.
(eq 3 nil)
=> nil
(eq OBJ1 OBJ2)
Return t if the two args are the same Lisp object.
(equal O1 O2)
Return t if two Lisp objects have similar structure and contents.
They must have the same data type.
Conses are compared by comparing the cars and the cdrs.
Vectors and strings are compared element by element.
Numbers are compared by value, but integers cannot equal floats.
(Use `=' if you want integers and floats to be able to be equal.)
Symbols must match exactly.
A list in lisp is a series of cons cells, but in Tcl, a list is a string with whitespace separating the elements. For translating code from lisp to tcl, one might simply take lisp lists and translate them to Tcl lists. However, this runs into trouble with side effecting cons cells not coming across to the Tcl code. For example, consider this code in lisp:
(setq a (list 1 2 3 4))
(let ((b a)
(a (cddr a)))
(declare (special a b))
(setf (cadr b) ‘b)
(setf (cadr a) ‘d)
(print a))
(print a)
;; Results in:
(3 d)
(1 b 3 d)
Is there a Tcl package that would provide a better emulation of lisp lists in Tcl? Does such package offer easy conversion to regular Tcl lists?
What might the above code look like in Tcl using such package?
Lisp cons cells can't be directly modeled as Tcl values because of fundamentally different semantic models. Lisp uses a model whereby values are directly updatable; the value is the memory cell. Tcl uses a different model with values which are conceptually immutable and where there is no difference in principle between any “1 2 3 4” that happens to be lying around from any other; mutable entities in Tcl are variables with names (the name strings themselves are immutable, of course…) This immutability makes sense at the level of simple values, but it extends also to Tcl's lists and dictionaries; mutation operations all either return a new value or update a variable. (The implementation is more efficient than this, using a copy-on-write strategy to preserve the semantic model of immutability while being able to implement things with mutation of the value itself when that is actually known to be semantically equivalent.)
Because of this, you have to construct updatable cons cells as variables. Here's how you might do it:
proc cons {a b} {
global gensym cons
set handle G[incr gensym]
set cons($handle) [list $a $b]
return $handle
}
proc car {handle} {
global cons
return [lindex $cons($handle) 0]
}
proc cdr {handle} {
global cons
return [lindex $cons($handle) 1]
}
proc setCar {handle value} {
global cons
lset cons($handle) 0 $value
}
# Convenience procedures
proc makeFromList args {
set result "!nil"
foreach value [lreverse $args] {
set result [cons $value $result]
}
return $result
}
proc getAsList {handle} {
set result {}
while {$handle ne "!nil"} {
lappend result [car $handle]
set handle [cdr $handle]
}
return $result
}
set a [makeFromList 1 2 3 4]
# Use some local context; Tcl doesn't have anything exactly like Lisp's "let"
apply {a {
set b $a
set a [cdr [cdr $a]]
setCar [cdr $b] "b"
setCar [cdr $a] "d"
puts [getAsList $a]
}} $a
puts [getAsList $a]
This produces the expected output (given that Lisp and Tcl have different ideas how a list should be formatted).
I'm writing a scheme interpreter, and in the case of an if statement such as:
(if (< 1 0) 'true)
Any interpreter I've tried just returns a new prompt. But when I coded this, I had an if for whether there was an alternative expression. What can I return in the if such that nothing gets printed?
(if (has-alternative if-expr)
(eval (alternative if-expr))
#f) ;; what do I return here?
According to the R6RS specification:
If <test> yields #f and no <alternate>
is specified, then the result of the
expression is unspecified.
So go wild, return anything you want! Although #f or '() are what I, personally, would expect.
Scheme can indeed return no values:
> (values)
In R5RS the one-armed form of if is specified to return an unspecified value.
That means it is up to you, to decide which value to return.
Quite a few Schemes have chosen to introduce a specific value called
"the unspecified value" and returns that value.
Others return "the invisible value" #<void> and the REPL is written
such that it doesn't print it.
> (void)
At first one might think, this is the same as (values),
but note the difference:
> (length (list (void)))
1
> (length (list (values)))
error> context expected 1 value, received 0 values
(Here (list ...) expected 1 value, but received nothing)
If #<void> is part of a list, it is printed:
> (list (void))
(#<void>)
A number of Schemes (PLT, Ikarus, Chicken) have a void type, which you can produce with (void).
In PLT at least, void is what you get when you do (when (< 1 0) #t).
(PLT v4 doesn't allow if without an else clause.)
When the return value is unspecified you can return what you like; the user just can't rely on that value being there, ever, or across implementations.
First, it's OK to require if to have an else clause, if it makes it easier for you. Second, Scheme supports returning multiple values from a function, so if you were to implement the return values as a list, you could have an empty list signify that no return value was given.
(if (has-alternative if-expr)
(eval (alternative if-expr)) ; make sure eval returns a list
'())
An important distinction here: I'm not returning an empty list if there was no else clause. The empty list signifies that there was no return value. If there were one return value from an expression (say it was 3) you would have (3) as the return from the eval behind the scenes. Similarly, returning multiple values from an expression would cause the eval return list to have multiple elements.
Finally, in all practicality, you could really return anything if the condition fails and there's no else, because it would be an error in a program to attempt to capture the value of a function that doesn't return anything. As such, it would be the job of the programmer, not the language, to catch this error.
Weird people would return 'nil or '|| (the empty symbol). The problem is to return a symbol that cannot be return by (eval (alternative if-expr)) to avoid confusion.
If anything can be returned by (eval (alternative if-expr)) and you still want to know whether you came in this alternative or not, you have to pack the result with more information :
(if (has-alternative if-expr)
(cons #t (eval (alternative if-expr)))
(cons #f #f))
Thus, the result is a cons cell. If its car is #t, then you evaled something. If it is #f, you didn't.
What is wrong with just:
(if (has-alternative if-expr) (eval (alternative if-expr)))
?