Question

I want to write a function that scores the alignment of two strings according to the scoring scheme specified below.

So this function will take two or more characters and return an integer.

Match Type and Points  
Match          2  
Mismatch      -1  
Space-match   -2  

So 
"h" "h"     gives you 2  
"h" "k"      gives you      -1  
"h" "_"      gives you      -2

(define (char-scorer char1 char2)  
  (cond ((char=? char1 char2) 2) 
    ((or (char=? char1 #\_ ) (char=? #\_ char2)) -2)
    (else -1)))  

(define (alignment-score s1 s2)    
  (define min-length (min (string-length s1) (string-length s2)))
  (let loop ((score 0) (index 0))
    (if (= index min-length) 
        score
        (loop (+ score (char-scorer (string-ref s1 index)
                                    (string-ref s2 index)))

          (+ index 1)))))

But when I tried using (alignment-score-not-tail "Hello" "_ _low"), which is suppose to give me -4, but i get -1

Was it helpful?

Solution

Based on your latest string-scorer, I would do the following:

(define (char-scorer char1 char2)  
  (cond 
    ((char=? char1 char2) 2) 
    ((or (char=? #\_ char1) (char=? #\_ char2)) -2)
    (else -1)))  

(define (alignment-score-not-tail string1 string2)
  (apply + (map char-scorer (string->list string1) (string->list string2))))

then

> (alignment-score-not-tail "Hello" "__low")
-4

Some details:

  1. I chose to compare chars, not one-letter strings. This comes in handy later when using string->list. Hence also the use of char=? which is more specific than equal?.
  2. I convert the strings to lists, for example (string->list "Hello") is '(#\H #\e #\l #\l #\o).
  3. If you map your char comparison to the 2 strings converted to lists, you get a list with the individual results (in your example, '(-2 -2 2 -1 -1).
  4. Now you just have to add all these numbers, that's what apply + does. Not the best solution, but works for small lists.

Alternatively, if this is supposed to be a non tail-recursive procedure, you could do the following:

(define (alignment-score-not-tail string1 string2)
  (define (helper lst1 lst2)
    (if (or (null? lst1) (null? lst2))
        0
        (+ (char-scorer (car lst1) (car lst2)) (helper (cdr lst1) (cdr lst2)))))
  (helper (string->list string1) (string->list string2)))

and transforming to a tail-recusive one could be done as follows:

(define (alignment-score-tail string1 string2)
  (define (helper lst1 lst2 result)
    (if (or (null? lst1) (null? lst2))
        result
        (helper (cdr lst1) (cdr lst2) (+ result (char-scorer (car lst1) (car lst2))))))
  (helper (string->list string1) (string->list string2) 0))

OTHER TIPS

A syntactically correct version of what you have above is:

(define (string-scorer string1 string2)
  (cond ((equal? string1 string2) 2)
        ((or (equal? string1 "_" ) (equal? "_" string2)) -2)
        ((not (equal? string1 string2)) -1)
        (else 0))) ; ????

But I think you still have some confusion here. The first issue is that you specified in the comments thread that you are actually comparing strings rather than characters, so you need to use the string "_" rather than #\_. The second issue is that there are no conditions under which the else part of your conditional is reached. Once the first condition fails, we know that the two strings are not equal, so there is really no point in specifying the third condition as ((not (equal? string1 string2)) - 1) when it could just as well be written else -1. A simpler version corresponding exactly to your spec would read:

(define (string-scorer string1 string2)  
  (cond ((equal? string1 string2) 2) 
        ((or (equal? string1 "_" ) (equal? "_" string2)) -2)
        (else -1)))

You can type in (list (string-scorer "h" "h") (string-scorer "h" "k") (string-scorer "h" "_") (string-scorer "_" "h")) to check it. But if there is some condition under which you want your function to return 0, then you need to rethink your spec.

Edit: Here are some added functions that use the first one to score two multi-character strings, including when they are of different length (I assumed that the "extra" characters in the longer of the two strings would be counted as -1, since they are not aligned with any characters in the shorter string):

(define (string-head string1)
  (cond ((> (string-length string1) 0) (substring string1 0 1))
        (else "")))
(define (string-tail string1)
  (substring string1 1 (string-length string1)))
(define (string-scorer-2 string1 string2)
  (cond ((= (string-length string1) 0) (* -1 (string-length string2)))
        ((= (string-length string2) 0) (* -1 (string-length string1)))
        (else (+ (string-scorer (string-head string1)
                                (string-head string2))
                 (string-scorer-2 (string-tail string1)
                                (string-tail string2))))))

But you may want to just use some variant of uselpa's function, which is a lot more concise. :-)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top