Scheme When to use Symbols instead of strings?

I apologize in advance for my primitive English; I will try my best to avoid grammatical errors and the like.

Two weeks ago, I decided to refresh my knowledge of the Scheme (and its enlightenments) by introducing some mathematical material that I received between my hands, in particular, regular languages ​​from a course on the theory of automata and calculations in which I am registered.

So far, I have represented alphabets as lists of characters instead of

  • lists of characters because I want to have letters of variable size.
  • lists of strings because I feel uneven.

I am inexperienced and would like to know your opinion about this particular choice. Are the characters reserved for a specific task and that is why I use them incorrectly? Any comments on this are greatly appreciated as I seek guidance.

In the future, there will also be time for the realization of the set of all possible words over an alphabet that is infinite. I thought to limit the set to the maximum number of allowed words. Again, is this good practice, or should I go after the threads? I feel that threads will be a better approach, but I haven't studied them yet, so I really don't know what consequences might use them.

Anyway, any suggestions or comments are welcome. I really appreciate the time you took to read my doubts. Good weekend!

+4
source share
1 answer

The simple difference is that characters are very cheap to compare. Can eq? use eq? to compare two characters as identical. This is because during compilation, the compiler will essentially list all characters with a number, so you are simply comparing integers, a very cheap job with the machine.

This means that two characters are identical if and only if they consist of the same characters. It is impossible to distinguish two characters that have the same characters in the code, they are constants, they are the same objects, like 3 and 3 .

However, two lines can be different objects located in different places of memory, and to compare them, you need to compare each character separately to match.

Therefore, symbols should be used and are often used for this, for example, given:

 (assq 'symb '((foo 1 2 3) (bar symb) (symb "match"))) 

This returns a list (symb "match") , it's as cheap as a comparison:

 (assq 4 '((0 1 2 3) (1 symb) (4 "match"))) 

Which returns a list (4 "match") . However, when using strings in the form of keys, assq no longer enough, which uses the eq? procedure eq? for comparison, use assoc instead, which uses the equal? , which is much more expensive because it recursively compares the composition. The aforementioned implementation is actually cheap enough to be often used as a way to model associative arrays and environments in interpreters, although this, of course, is not random access.

Edit: as you requested, in threads:

The Scheme standard supports a good pair, one of them is the force function, the other is a special form called delay . Effective that

 (delay (+ 1 2 3)) 

Or any other code instead of (+ 1 2 3) returns this so-called "promise", it delays the calculation of the answer in this promise, but promises that the result will be identical when evaluating this 6 we get there. This may seem completely useless, but let's say that the result depends on some variable that can be changed:

 (define x 4) ; x is bound to 4. (let ((promise (delay (+ 2 x)))) ; evaluating the expression now would result into 6, but we delay it. (set! x 5) ; we change the value of x. (display (force promise)) ; displays 7 (set! x 6) ; change it again (force promise) ; ALSO displays 7, not 8 ) 

It becomes obvious that a promise is truly evaluated only once, and in a forced repetition it gives the same meaning as in the evaluation for the first time.

This is what is commonly used in streams or conceptual "endless lists". The cdr of the list in this case is a promise for the rest of the list, which is forcibly restored, because otherwise it will turn into an inexhaustible calculation, for example, we usually define:

 (define (stream-cdr x) (force (cdr x))) ; it forces that promise to evaluate it. ; and optionally (define stream-car car) ; same 

Since this argument cannot evaluate its argument, it must be a special form:

 (define-syntax stream-cons (syntax-rules () ((stream-cons xy) (cons x (delay y)))) 

The compiler or interpreter of your circuit now translates each event (stream-cons xy) to (cons x (delay y)) for any arbitrary x and y.

So, as a simple example, now that our estimate is delayed until we need it, we can create an endless list of zeros:

 (define zero-stream (stream-cons 0 zero-streams)) 

The list borrowed on its own, which will undoubtedly never end if we did not use a thread that is useless, but it shows this idea. We can just use stream-cdr again and again on this, without even reaching the empty list, we again get the same "endless list 0".

A more practical example is a list of all Fibonacci numbers, which is more complex:

 (define fibs (stream-cons 0 (stream-cons 1 (stream-map + fibs (stream-cdr fibs)))) 

Stream-map is an analogue of normal display, this definition is quite complicated, but I'm sure you can find it, it generates a stream. So, `(stream-map (lambda (xy) (+ 1 xy)) zeroes zeroes) will create a stream completely filled with one. The flow of fibs is recursively determined by itself. The first two elements are given, and the rest are calculated from two threads, which are simply fibs and the cdr stream of the fibs themselves.

+7
source

Source: https://habr.com/ru/post/1313973/


All Articles