Needleman–Wunsch アルゴリズム

Needleman–Wunsch を Scheme で実装してみた。
# アルゴリズム
https://bi.biopapyrus.jp/seq/alignment/needleman%E2%80%93wunsch.html
# プログラム例
アルゴリズムは先のページを参照してください。そのままコードにしただけです。
```
(define nil '())

(define f (make-hash-table 'equal?))
(define p (make-hash-table 'equal?))

(define score-match 2)
(define score-mismatch -1)
(define score-gap -2)
(define d (* -1 score-gap))

(define (score w1 w2)
  (cond ((or (null? w1) (null? w2)) score-gap)
        ((eq? w1 w2) score-match)
        (else score-mismatch)))

(define (s x y)
  (score (hash-table-get f (make-key x -1) nil)
         (hash-table-get f (make-key -1 y) nil)))

(define (make-key a b)
  (string-append (number->string a) "," (number->string b)))

(define (get-x key)
  (if (null? key)
      nil
      (string->number (car (string-split key ",")))))

(define (get-y key)
  (if (null? key)
      nil
      (string->number (cadr (string-split key ",")))))

(define (init word1 word2 lim-x lim-y)
  (define (iter-x x w)
    (if (> x lim-x)
        'done
        (begin
          (hash-table-put! f (make-key x 0) (* x -1 d))
          (hash-table-put! f (make-key x -1) (car w))
          (hash-table-put! p (make-key x 0) (list (list 3 (make-key (- x 1) 0))))
          (iter-x (+ x 1) (cdr w)))))
  (define (iter-y y w)
    (if (> y lim-y)
        'done
        (begin
          (hash-table-put! f (make-key 0 y) (* y -1 d))
          (hash-table-put! f (make-key -1 y) (car w))
          (hash-table-put! p (make-key 0 y) (list (list 1 (make-key 0 (- y 1)))))
          (iter-y (+ y 1) (cdr w)))))
  (iter-x 1 word1)
  (iter-y 1 word2)
  (hash-table-put! f (make-key 0 0) 0))

(define (evaluate-scores lim-x lim-y)
  (define (iter-x x y)
    (if (> x lim-x)
        'done
        (let* ((key (make-key x y))
               (up-key (make-key x (- y 1)))
               (up-left-key (make-key (- x 1) (- y 1)))
               (left-key (make-key (- x 1) y))
               (up-left (+ (hash-table-get f up-left-key) (s x y)))
               (up (+ (hash-table-get f up-key) (* -1 d)))
               (left (+ (hash-table-get f left-key) (* -1 d)))
               (p0 (sort `((,up 1 ,up-key) (,up-left 2 ,up-left-key) (,left 3 ,left-key)) > car))
               (p1 (car p0))
               (p2 (cadr p0))
               (p3 (caddr p0)))
          (hash-table-put! f key (car p1))
          (cond ((= (car p1) (car p2) (car p3))
                 (hash-table-put! p key (sort (cons (cdr p1) (hash-table-get p key nil)) < car))
                 (hash-table-put! p key (sort (cons (cdr p2) (hash-table-get p key nil)) < car))
                 (hash-table-put! p key (sort (cons (cdr p3) (hash-table-get p key nil)) < car)))
                ((= (car p1) (car p2))
                 (hash-table-put! p key (sort (cons (cdr p1) (hash-table-get p key nil)) < car))
                 (hash-table-put! p key (sort (cons (cdr p2) (hash-table-get p key nil)) < car)))
                (else
                 (hash-table-put! p key (sort (cons (cdr p1) (hash-table-get p key nil)) < car))))
          (iter-x (+ x 1) y))))
  (define (iter-y y)
    (if (> y lim-y)
        'done
        (begin
          (iter-x 1 y)
          (iter-y (+ y 1)))
        ))
  (iter-y 1))

(define (make-global-alignment key)
  (if (equal? key (make-key 0 0))
      nil
      (let* ((val (hash-table-get p key nil))
             (up (assv 1 val))
             (up-left (assv 2 val))
             (left (assv 3 val)))
        (cons key
              (list
               (if up (make-global-alignment (cadr up)) nil)
               (if up-left (make-global-alignment (cadr up-left)) nil)
               (if left (make-global-alignment (cadr left)) nil))))))

(define (make-word paths)
  (define (iter p xx yy w1 w2)
    (if (null? p)
        (begin
          (print (reverse w1))
          (print (reverse w2))
          (print ""))
        (let ((x (get-x (car p)))
              (y (get-y (car p))))
          (cond ((and (> x xx) (> y yy))
                 (iter (cdr p) x y (cons (hash-table-get f (make-key x -1)) w1) (cons (hash-table-get f (make-key -1 y)) w2)))
                ((> x xx)
                 (iter (cdr p) x yy (cons (hash-table-get f (make-key x -1)) w1) (cons "-" w2)))
                ((> y yy)
                 (iter (cdr p) xx y (cons "-" w1) (cons (hash-table-get f (make-key -1 y)) w2)))))))
  (iter paths 0 0 nil nil))

(define (print-score len1 len2)
  (print "SCORE: " (hash-table-get f (make-key len1 len2))))

(define (print-words lst paths)
  (let* ((nodes (cdr lst))
         (up (car nodes))
         (up-left (cadr nodes))
         (left (caddr nodes)))
    (if (and (null? up) (null? up-left) (null? left))
        (make-word (cons (car lst) paths))
        (begin
          (and (not (null? up)) (print-words (car nodes) (cons (car lst) paths)))
          (and (not (null? up-left)) (print-words (cadr nodes) (cons (car lst) paths)))
          (and (not (null? left)) (print-words (caddr nodes) (cons (car lst) paths)))
          'done))))

(define (needleman-wunsch word-lst1 word-lst2)
  (hash-table-clear! f)
  (hash-table-clear! p)

  (let ((len1 (length word-lst1))
        (len2 (length word-lst2)))
    (init word-lst1 word-lst2 len1 len2)
    (evaluate-scores len1 len2)
    (print-score len1 len2)
    (print-words (make-global-alignment (make-key len1 len2)) nil)))
```
# 実行結果
```
gosh> (needleman-wunsch '(A T T G C) '(A T G C))
SCORE: 6
(A T T G C)
(A - T G C)

(A T T G C)
(A T - G C)

done
```